/* * This file is part of the Flowee project * Copyright (C) 2019-2024 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FLOWEE_TRANSACTIONBUILDER_H #define FLOWEE_TRANSACTIONBUILDER_H #include "primitives/Tx.h" #include "primitives/PrivateKey.h" #include "primitives/script.h" #include "uint256.h" class PublicKey; class CScriptID; class CTransaction; class TransactionBuilderPrivate; /** * This class allows anyone to create or extend Bitcoin (BCH) transactions. * * A transaction can be started from nothing, or a partially constructed transaction * can be imported and build further. * * Bitcoin transactions are only final once all inputs are signed, which may take * multiple people signing parts of that transaction. This builder allows building * new but also importing and extending existing transactions. * * TODO when an action destroys an existing signature I should have a way to communicate * this to the user of this class. */ class TransactionBuilder { public: enum SignatureType { ECDSA, Schnorr }; TransactionBuilder(); TransactionBuilder(const Tx &existingTx); TransactionBuilder(const CTransaction &existingTx); ~TransactionBuilder(); void setFeeTarget(int satsPerKByte); /** * Add a new input and select it. * This uses the default SignAllOutputs SignAllInputs sighash. */ int appendInput(const uint256 &txid, int outputIndex); /** * Select an input based on index. * Please note that the first input is numbered as zero (0) * @returns the input number we selected. */ int selectInput(int index); int outputCount() const; int inputCount() const; /// SigHash type, the inputs part. enum SignInputs { /** * This option signs all inputs, making it impossible to combine with other inputs after signing. * * This is not directly a signhash type as this is the default behaviour. When in doubt, use this. */ SignAllInputs = 0, /** * This option allows for full combining of this input with any other inputs, even after signing. * * This sighash flag, also called SIGHASH_ANYONECANPAY, is useful to combine different inputs * from different parties and combine that into a valid transaction. * * Be careful about picking an good SignOutputs enum as picking NoOutput will essentially give your * money to anyone that manages to mine the input first. */ SignOnlyThisInput = 0x80, }; /// SighHash type, the outputs part. enum SignOutputs { /** * An input signed with this will disallow any change in all outputs. * This sighash type, also called SIGHASH_ALL, signs all outputs, preventing any modification. */ SignAllOuputs = 1, /** * Put no requirement at all on the outputs to stay the same after signing. * * This sighash type, also called SIGHASH_NONE, signs only this input. Best used in * combination with SignAllInputs because otherwise this input can be combined in another * transaction and losses can occur. */ SignNoOutputs = 2, /** * Requires the input to be combined with one specific output. * This sighash type, also called SIGHASH_SINGLE, signs the output from this transaction * that has the same index as this input. * * Please be aware that if there is no output of the same number that this silently turns * into a SignNoOutputs. * * It allows modifications of other outputs and the sequence number of other inputs. */ SignSingleOutput = 3, }; /** * Pushes the data needed for the current input to receive its signatures. * * Inputs use a signature to prove you own the money that this transaction spends. To make the * signing secure it doesn't just take the private key, it also takes the prevOutScript and the value * properties, which your wallet should supply you with. * * The SignInputs / SignOutputs options determine how flexible the signature is with regards to a * changing transaction after final signing time. * * For instance a fundraiser may want to combine inputs from a lot of people into one transaction. * Those inputs can then be signed individually using SignOnlyThisInput and later combined without * breaking the signature. * * A common rule is that outputs or inputs not included in the transaction may be changed after signing * and before the transaction is mined. * * In most cases you should be very careful to pick at least one output you care about that you * will sign because that guarentees your money can only be spent with those outputs getting paid. * * The default is to sign all inputs and all outputs, which implies that the entire transaction * is fully constructed before signatures are collected. * * Notice that actual signing only happens when calling createTransaction() */ void pushInputSignature(const PrivateKey &privKey, const CScript &prevOutScript, int64_t value, SignatureType type, SignInputs inputs = SignAllInputs, SignOutputs outputs = SignAllOuputs); /// locking options. enum LockingOptions { /** * No locking applied, transaction can be mined immediately and spent immediately after. */ NoLocking, /** * A transaction can be banned from mining till a certain block height. * * The value passed is the last block height the transaction is not allowed * to be mined in. * * Please be aware that this allows the transaction to be double spend quite easy. */ LockMiningOnBlock, /** * A transaction can be banned from mining in a block until a certain time. * The time is set in seconds since unix epoch, the time should only be in the future. * * Please be aware that this allows the transaction to be double spend quite easy. */ LockMiningOnTime, /** * A transaction *input* can be locked from being mined till a certain block height. * * This is only really useful in case the output you are spending used OP_CHECKSEQUENCEVERIFY */ RelativeSpendingLockOnBlocks, /** * A transaction *input* can be locked from being mined untill a certain time. * * This is only really useful in case the output you are spending used OP_CHECKSEQUENCEVERIFY */ RelativeSpendingLockOnTime }; #if 0 /** * Set the locking option on the current input. * * Please be aware that usage of the LockFromMiningBlock or LockFromMiningTime options * are transaction-global options and will effect all outputs in one go. */ void setLocking(LockingOptions option, uint32_t value); #endif /// delete an input based on index. Updates current input index. void deleteInput(int index); /// Appends and selects an output. /// returns current output index. int appendOutput(int64_t value); /// selects an output /// returns current output index. int selectOutput(int index); /// update the output value on the selected output void setOutputValue(int64_t value); /** * Use argument, or current output to take fee from. * The transaction-fee is the difference between incoming in inputs and outgoing in outputs. * In order to have the expected amount of fees paid, we should adjust an output to end up * with the right amount of satoshi's fees paid per kbyte of transaction-size. * * This passes in the output to use, or with the default argument it uses the current selected output. * Setting the index to a not existing output index will disable the fee adjustment feature. */ void setOutputFeeSource(int outputIndex = -1); /** * For the selected output a standard output script will be generated * that sends the funds to the public-key-hash (aka bitcoin-address) passed. */ void pushOutputPay2Address(const KeyId &address); /** * Set the current output to use the new script. * See pushOutputPay2Address() and pushOutputPay2Script() for convenience * versions of this method. */ void pushOutputScript(const CScript &script); /** * For the selected output a standard output script will be generated * that sends the funds to the pay to script-hash address passed. * * @code CashAddress::Content c = CashAddress::decodeCashAddrContent(address, "bitcoincash"); if (c.type == CashAddress::SCRIPT_TYPE) builder.pushOutputPay2Script(c.hash); * @endcode */ void pushOutputPay2Script(const std::vector &p2SHash); /** * For the selected output a standard output script will be generated * that becomes a prunable (provable unspendable) output based on the OP_RETURN opcode. */ void pushOutputNullData(const std::vector &data); /// delete an output based on index. Updates current output index. void deleteOutput(int index); /** * For the selected output, start a cashtoken with given category and bitfield. * Note that if you state a commitment or amount is to be given, you should * call the pushNftCommitment() or pushOutputFtValue() respectively. */ void startOutputToken(const uint256 &category, uint8_t bitfield); /** * For the selected output, add commitment data. * This method throws if you have not started a token, or the bitfield did * not include a commitment. * We also throw if the size is larger than 40 bytes. */ void pushNftCommitment(const std::vector &data); /** * For the selected output, add the number of fungible tokens this * output includes. * This method throws if you have not started a token, or the bitfield did * not include an amount. */ void pushOutputFtAmount(uint64_t amount); /** * Remove the token data from the currently selected output. */ void clearOutputToken(); /** * Render the state of the transaction, signing any inputs that we have signing data for. * * Inputs that did not explicitly got an pushInputSignature() called will not be signed and * instead left unchanged. * * @see pushInputSignature() * @see setAnonimize() * * @param optional pool to use for memory allocation. */ Tx createTransaction(const std::shared_ptr &pool = nullptr) const; /// return if the transaction created by createTransaction() will be anonimized bool anonimize() const; /** * By turning on the anonimize feature transactions created by createTransaction() will have their * inputs and outputs sorted according to BIP69, which as the intention that wallet apps and concepts * like change-address are at least obfuscated in their meaning. This is meant to highten the * anonimity of the transaction. * * See https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki */ void setAnonimize(bool on); /** * Find equivalent inputs with signatures and copy those signatures to the current transaction */ // mergeTransaction(const Tx &tx); /** * Change the transaction version to \a txVersion. * The transaction version passed into the builder is used otherwise, or the default (2) if the empty constructor is used. */ void setTransactionVersion(int txVersion); /** * Returns the transaction version. */ int transactionVersion() const; private: TransactionBuilder(const TransactionBuilder&) = delete; TransactionBuilder &operator=(const TransactionBuilder&) = delete; TransactionBuilderPrivate *d; }; #endif