Signing a transaction
script/sign.cpp#SignTransaction()
will sign a transaction one input at a time, by looping through the vin
s of the CMutableTransaction
it has been passed.
The critical section of the SignTransaction()
loop is shown below:
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
CTxIn& txin = mtx.vin[i];
auto coin = coins.find(txin.prevout);
if (coin == coins.end() || coin->second.IsSpent()) {
input_errors[i] = "Input not found or already spent";
continue;
}
const CScript& prevPubKey = coin->second.out.scriptPubKey;
const CAmount& amount = coin->second.out.nValue;
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
The Pubkey and amount for each coin are retrieved, along with signature data for the coin. DataFromTransaction()
returns all the information needed to produce a signature for that coin as a SignatureData
struct:
// This struct contains information from a transaction input and also contains signatures for that input.
// The information contained here can be used to create a signature and is also filled by ProduceSignature
// in order to construct final scriptSigs and scriptWitnesses.
struct SignatureData {
bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete
bool witness = false; ///< Stores whether the input this SigData corresponds to is a witness input
CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format
CScript redeem_script; ///< The redeemScript (if any) for the input
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
uint256 missing_witness_script; ///< SHA256 of the missing witnessScript (if any)
SignatureData() {}
explicit SignatureData(const CScript& script) : scriptSig(script) {}
void MergeSignatureData(SignatureData sigdata);
};
With the signing SigningProvider
, scriptPubKey
and sigdata
we are able to call script/sign.cpp#ProduceSignature()
for signing on each individual input. Inputs by default will signed with a sighash of SIGHASH_ALL
, but this can be re-configured as appropriate.
Producing a signature
Taking a look inside ProduceSignature()
we can see how this works.
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
{
if (sigdata.complete) return true;
std::vector<valtype> result;
TxoutType whichType;
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
// ...
}
The function performs some initialisations before calling script/sign.cpp#SignStep()
for the first time, with the SigVersion
SIGVERSION::BASE
. SignStep()
in turn calls Solver()
, which is a function designed to detect the script type encoding of the scriptPubKey
, and then return the detected type along with the parsed scriptPubKey
s/hashes.
If it is successful, SignStep
continues by switching over the script type and, depending on the script type, calling the required signing operation and pushing the required elements onto the sigdata
variable.
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
std::vector<valtype>& ret, TxoutType& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{
// ...
whichTypeRet = Solver(scriptPubKey, vSolutions);
switch (whichTypeRet) {
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT:
// ...
case TxoutType::PUBKEY:
// ...
case TxoutType::PUBKEYHASH:
// ...
case TxoutType::SCRIPTHASH:
// ...
case TxoutType::MULTISIG:
// ...
case TxoutType::WITNESS_V0_KEYHASH:
// ...
case TxoutType::WITNESS_V0_SCRIPTHASH:
// ...
}
// ...
}
Once SignStep()
returns to ProduceSignature()
, a second switch takes place. If we are trying to produce a signature for P2SH, P2WPKH or P2WSH then the first pass from SignStep()
will have been enough to detect the TxOutType
and assemble the (redeem/witness) scripts, but not yet generate the entire signature in required format. In order to get this signature, SignStep()
is called again, this time with the assembled redeem/witness script and the appropriate TxOutType
.
This recursion makes sense if you consider that, in order to sign for these script-encumbered inputs, we don’t want to sign for the scriptPubKey that we are starting with but for the {redeem|witness} script instead. |
We can see this switch in ProduceSignature()
:
if (solved && whichType == TxoutType::SCRIPTHASH)
{
// Solver returns the subscript that needs to be evaluated;
// the final scriptSig is the signatures from that
// and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end());
sigdata.redeem_script = subscript;
solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TxoutType::SCRIPTHASH;
P2SH = true;
}
if (solved && whichType == TxoutType::WITNESS_V0_KEYHASH)
{
CScript witnessscript;
// This puts the parsed pubkeys from the first pass into the witness script
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
TxoutType subType;
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
}
else if (solved && whichType == TxoutType::WITNESS_V0_SCRIPTHASH)
{
CScript witnessscript(result[0].begin(), result[0].end());
sigdata.witness_script = witnessscript;
TxoutType subType;
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TxoutType::SCRIPTHASH && subType != TxoutType::WITNESS_V0_SCRIPTHASH && subType != TxoutType::WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
} else if (solved && whichType == TxoutType::WITNESS_UNKNOWN) {
sigdata.witness = true;
}
Finally, if all went well the signature is checked with VerifyScript()
.