Skip to content

Commit a86d067

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 fdcbcb3 commit a86d067

File tree

6 files changed

+146
-37
lines changed

6 files changed

+146
-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: 111 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ static CScript PushAll(const std::vector<valtype>& values)
127127

128128
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
129129
{
130+
if (sigdata.complete) return true;
131+
130132
std::vector<valtype> result;
131133
txnouttype whichType;
132134
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE);
@@ -169,15 +171,106 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
169171
sigdata.scriptSig = PushAll(result);
170172

171173
// Test solution
172-
return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
174+
sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
175+
return sigdata.complete;
173176
}
174177

175-
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
178+
bool SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
179+
{
180+
if (checker->CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
181+
CPubKey pubkey(vchPubKey);
182+
sigdata->signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
183+
return true;
184+
}
185+
return false;
186+
}
187+
188+
namespace
189+
{
190+
struct Stacks
191+
{
192+
std::vector<valtype> script;
193+
std::vector<valtype> witness;
194+
195+
Stacks() {}
196+
explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {}
197+
explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
198+
EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
199+
}
200+
201+
SignatureData Output() const {
202+
SignatureData result;
203+
result.scriptSig = PushAll(script);
204+
result.scriptWitness.stack = witness;
205+
return result;
206+
}
207+
};
208+
}
209+
210+
// Extracts signatures and scripts from incomplete scriptSigs. Please do not extend this, use PSBT instead
211+
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout)
176212
{
177213
SignatureData data;
178214
assert(tx.vin.size() > nIn);
179215
data.scriptSig = tx.vin[nIn].scriptSig;
180216
data.scriptWitness = tx.vin[nIn].scriptWitness;
217+
Stacks stack(data);
218+
219+
// Get signatures
220+
MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue);
221+
SignatureExtractorChecker extractor_checker(&data, &tx_checker);
222+
if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) {
223+
data.complete = true;
224+
return data;
225+
}
226+
227+
// Get scripts
228+
txnouttype script_type;
229+
std::vector<std::vector<unsigned char>> solutions;
230+
Solver(txout.scriptPubKey, script_type, solutions);
231+
SigVersion sigversion = SigVersion::BASE;
232+
CScript next_script = txout.scriptPubKey;
233+
234+
if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
235+
// Get the redeemScript
236+
CScript redeem_script(stack.script.back().begin(), stack.script.back().end());
237+
data.redeem_script = redeem_script;
238+
next_script = std::move(redeem_script);
239+
240+
// Get redeemScript type
241+
Solver(next_script, script_type, solutions);
242+
stack.script.pop_back();
243+
}
244+
if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
245+
// Get the witnessScript
246+
CScript witness_script(stack.witness.back().begin(), stack.witness.back().end());
247+
data.witness_script = witness_script;
248+
next_script = std::move(witness_script);
249+
250+
// Get witnessScript type
251+
Solver(next_script, script_type, solutions);
252+
stack.witness.pop_back();
253+
stack.script = std::move(stack.witness);
254+
stack.witness.clear();
255+
sigversion = SigVersion::WITNESS_V0;
256+
}
257+
if (script_type == TX_MULTISIG && !stack.script.empty()) {
258+
// Build a map of pubkey -> signature by matching sigs to pubkeys:
259+
assert(solutions.size() > 1);
260+
unsigned int num_pubkeys = solutions.size()-2;
261+
unsigned int last_success_key = 0;
262+
for (const valtype& sig : stack.script) {
263+
for (unsigned int i = last_success_key; i < num_pubkeys; i++) {
264+
const valtype& pubkey = solutions[i+1];
265+
// We either have a signature for this pubkey, or we have found a signature and it is valid
266+
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
267+
last_success_key = i + 1;
268+
break;
269+
}
270+
}
271+
}
272+
}
273+
181274
return data;
182275
}
183276

@@ -187,6 +280,22 @@ void UpdateInput(CTxIn& input, const SignatureData& data)
187280
input.scriptWitness = data.scriptWitness;
188281
}
189282

283+
void SignatureData::MergeSignatureData(SignatureData sigdata)
284+
{
285+
if (complete) return;
286+
if (sigdata.complete) {
287+
*this = std::move(sigdata);
288+
return;
289+
}
290+
if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
291+
redeem_script = sigdata.redeem_script;
292+
}
293+
if (witness_script.empty() && !sigdata.witness_script.empty()) {
294+
witness_script = sigdata.witness_script;
295+
}
296+
signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end()));
297+
}
298+
190299
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
191300
{
192301
assert(nIn < txTo.vin.size());
@@ -264,28 +373,6 @@ static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const B
264373
return result;
265374
}
266375

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

src/script/sign.h

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,22 @@ 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+
void MergeSignatureData(SignatureData sigdata);
6272
};
6373

6474
/** Produce a script signature using a generic signature creator. */
@@ -71,8 +81,8 @@ bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom,
7181
/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */
7282
SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2);
7383

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

7888
/* Check whether we know how to sign for an output like this, assuming we
@@ -81,4 +91,15 @@ void UpdateInput(CTxIn& input, const SignatureData& data);
8191
* Solvability is unrelated to whether we consider this output to be ours. */
8292
bool IsSolvable(const SigningProvider& provider, const CScript& script);
8393

94+
class SignatureExtractorChecker : public BaseSignatureChecker
95+
{
96+
private:
97+
SignatureData* sigdata;
98+
BaseSignatureChecker* checker;
99+
100+
public:
101+
SignatureExtractorChecker(SignatureData* sigdata, BaseSignatureChecker* checker) : sigdata(sigdata), checker(checker) {}
102+
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
103+
};
104+
84105
#endif // BITCOIN_SCRIPT_SIGN_H

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)