Skip to content

Commit 0422beb

Browse files
committed
Make SignatureData able to store signatures and scripts
In addition to having the scriptSig and scriptWitness, have SignatureData also be able to store just the signatures (pubkeys mapped to sigs) and scripts (script ids mapped to scripts). Also have DataFromTransaction be able to extract signatures and scripts from the scriptSig and scriptWitness of an input to put them in SignatureData. Adds a new SignatureChecker which takes a SignatureData and puts pubkeys and signatures into it when it successfully verifies a signature. Adds a new field in SignatureData which stores whether the SignatureData was complete. This allows us to also update the scriptSig and scriptWitness to the final one when updating a SignatureData with another one.
1 parent b6edb4f commit 0422beb

File tree

6 files changed

+129
-37
lines changed

6 files changed

+129
-37
lines changed

src/bench/verify_script.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#endif
1010
#include <script/script.h>
1111
#include <script/sign.h>
12+
#include <script/standard.h>
1213
#include <streams.h>
1314

1415
#include <array>

src/bitcoin-tx.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,13 +645,13 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
645645
const CScript& prevPubKey = coin.out.scriptPubKey;
646646
const CAmount& amount = coin.out.nValue;
647647

648-
SignatureData sigdata;
648+
SignatureData sigdata = DataFromTransaction(mergedTx, i, coin.out);
649649
// Only sign SIGHASH_SINGLE if there's a corresponding output:
650650
if (!fHashSingle || (i < mergedTx.vout.size()))
651651
ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata);
652652

653653
// ... and merge in other signatures:
654-
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
654+
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i, coin.out));
655655
UpdateInput(txin, sigdata);
656656
}
657657

src/rpc/rawtransaction.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
744744
// ... and merge in other signatures:
745745
for (const CMutableTransaction& txv : txVariants) {
746746
if (txv.vin.size() > i) {
747-
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
747+
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i, coin.out));
748748
}
749749
}
750750

@@ -875,12 +875,12 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
875875
const CScript& prevPubKey = coin.out.scriptPubKey;
876876
const CAmount& amount = coin.out.nValue;
877877

878-
SignatureData sigdata;
878+
SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
879879
// Only sign SIGHASH_SINGLE if there's a corresponding output:
880880
if (!fHashSingle || (i < mtx.vout.size())) {
881881
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
882882
}
883-
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i));
883+
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i, coin.out));
884884

885885
UpdateInput(txin, sigdata);
886886

src/script/sign.cpp

Lines changed: 106 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ static CScript PushAll(const std::vector<valtype>& values)
126126

127127
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
128128
{
129+
if (sigdata.complete) return true;
130+
129131
std::vector<valtype> result;
130132
txnouttype whichType;
131133
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE);
@@ -168,15 +170,117 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
168170
sigdata.scriptSig = PushAll(result);
169171

170172
// Test solution
171-
return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
173+
sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
174+
return sigdata.complete;
175+
}
176+
177+
class SignatureExtractorChecker final : public BaseSignatureChecker
178+
{
179+
private:
180+
SignatureData& sigdata;
181+
BaseSignatureChecker& checker;
182+
183+
public:
184+
SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
185+
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
186+
};
187+
188+
bool SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
189+
{
190+
if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
191+
CPubKey pubkey(vchPubKey);
192+
sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
193+
return true;
194+
}
195+
return false;
196+
}
197+
198+
namespace
199+
{
200+
struct Stacks
201+
{
202+
std::vector<valtype> script;
203+
std::vector<valtype> witness;
204+
205+
Stacks() {}
206+
explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {}
207+
explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
208+
EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
209+
}
210+
211+
SignatureData Output() const {
212+
SignatureData result;
213+
result.scriptSig = PushAll(script);
214+
result.scriptWitness.stack = witness;
215+
return result;
216+
}
217+
};
172218
}
173219

174-
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
220+
// Extracts signatures and scripts from incomplete scriptSigs. Please do not extend this, use PSBT instead
221+
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout)
175222
{
176223
SignatureData data;
177224
assert(tx.vin.size() > nIn);
178225
data.scriptSig = tx.vin[nIn].scriptSig;
179226
data.scriptWitness = tx.vin[nIn].scriptWitness;
227+
Stacks stack(data);
228+
229+
// Get signatures
230+
MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue);
231+
SignatureExtractorChecker extractor_checker(data, tx_checker);
232+
if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) {
233+
data.complete = true;
234+
return data;
235+
}
236+
237+
// Get scripts
238+
txnouttype script_type;
239+
std::vector<std::vector<unsigned char>> solutions;
240+
Solver(txout.scriptPubKey, script_type, solutions);
241+
SigVersion sigversion = SigVersion::BASE;
242+
CScript next_script = txout.scriptPubKey;
243+
244+
if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
245+
// Get the redeemScript
246+
CScript redeem_script(stack.script.back().begin(), stack.script.back().end());
247+
data.redeem_script = redeem_script;
248+
next_script = std::move(redeem_script);
249+
250+
// Get redeemScript type
251+
Solver(next_script, script_type, solutions);
252+
stack.script.pop_back();
253+
}
254+
if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
255+
// Get the witnessScript
256+
CScript witness_script(stack.witness.back().begin(), stack.witness.back().end());
257+
data.witness_script = witness_script;
258+
next_script = std::move(witness_script);
259+
260+
// Get witnessScript type
261+
Solver(next_script, script_type, solutions);
262+
stack.witness.pop_back();
263+
stack.script = std::move(stack.witness);
264+
stack.witness.clear();
265+
sigversion = SigVersion::WITNESS_V0;
266+
}
267+
if (script_type == TX_MULTISIG && !stack.script.empty()) {
268+
// Build a map of pubkey -> signature by matching sigs to pubkeys:
269+
assert(solutions.size() > 1);
270+
unsigned int num_pubkeys = solutions.size()-2;
271+
unsigned int last_success_key = 0;
272+
for (const valtype& sig : stack.script) {
273+
for (unsigned int i = last_success_key; i < num_pubkeys; ++i) {
274+
const valtype& pubkey = solutions[i+1];
275+
// We either have a signature for this pubkey, or we have found a signature and it is valid
276+
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
277+
last_success_key = i + 1;
278+
break;
279+
}
280+
}
281+
}
282+
}
283+
180284
return data;
181285
}
182286

@@ -263,28 +367,6 @@ static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const B
263367
return result;
264368
}
265369

266-
namespace
267-
{
268-
struct Stacks
269-
{
270-
std::vector<valtype> script;
271-
std::vector<valtype> witness;
272-
273-
Stacks() {}
274-
explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {}
275-
explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
276-
EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
277-
}
278-
279-
SignatureData Output() const {
280-
SignatureData result;
281-
result.scriptSig = PushAll(script);
282-
result.scriptWitness.stack = witness;
283-
return result;
284-
}
285-
};
286-
}
287-
288370
static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
289371
const txnouttype txType, const std::vector<valtype>& vSolutions,
290372
Stacks sigs1, Stacks sigs2, SigVersion sigversion)

src/script/sign.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,18 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
5353
/** A signature creator that just produces 72-byte empty signatures. */
5454
extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR;
5555

56+
typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair;
57+
58+
// This struct contains information from a transaction input and also contains signatures for that input.
59+
// The information contained here can be used to create a signature and is also filled by ProduceSignature
60+
// in order to construct final scriptSigs and scriptWitnesses.
5661
struct SignatureData {
57-
CScript scriptSig;
58-
CScriptWitness scriptWitness;
62+
bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete
63+
CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format
64+
CScript redeem_script; ///< The redeemScript (if any) for the input
65+
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
66+
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.
67+
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.
5968

6069
SignatureData() {}
6170
explicit SignatureData(const CScript& script) : scriptSig(script) {}
@@ -71,8 +80,8 @@ bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom,
7180
/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */
7281
SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2);
7382

74-
/** Extract signature data from a transaction, and insert it. */
75-
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
83+
/** Extract signature data from a transaction input, and insert it. */
84+
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
7685
void UpdateInput(CTxIn& input, const SignatureData& data);
7786

7887
/* Check whether we know how to sign for an output like this, assuming we

src/test/transaction_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
629629
CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false);
630630
CheckWithFlag(output2, input2, 0, false);
631631
BOOST_CHECK(*output1 == *output2);
632-
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
632+
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0, output1->vout[0]), DataFromTransaction(input2, 0, output1->vout[0])));
633633
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
634634

635635
// P2SH 2-of-2 multisig
@@ -640,7 +640,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
640640
CheckWithFlag(output2, input2, 0, true);
641641
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
642642
BOOST_CHECK(*output1 == *output2);
643-
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
643+
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0, output1->vout[0]), DataFromTransaction(input2, 0, output1->vout[0])));
644644
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
645645
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
646646

@@ -652,7 +652,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
652652
CheckWithFlag(output2, input2, 0, true);
653653
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
654654
BOOST_CHECK(*output1 == *output2);
655-
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
655+
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0, output1->vout[0]), DataFromTransaction(input2, 0, output1->vout[0])));
656656
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
657657
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
658658

@@ -664,7 +664,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
664664
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
665665
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
666666
BOOST_CHECK(*output1 == *output2);
667-
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
667+
UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0, output1->vout[0]), DataFromTransaction(input2, 0, output1->vout[0])));
668668
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
669669
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
670670
}

0 commit comments

Comments
 (0)