forked from Bitcoin-ABC/bitcoin-abc
-
Notifications
You must be signed in to change notification settings - Fork 28
/
dsproof_validate.cpp
189 lines (164 loc) · 6.54 KB
/
dsproof_validate.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright (C) 2019-2020 Tom Zander <tomz@freedommail.ch>
// Copyright (C) 2020 Calin Culianu <calin.culianu@gmail.com>
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <coins.h>
#include <dsproof/dsproof.h>
#include <logging.h>
#include <script/interpreter.h>
#include <script/script.h>
#include <script/standard.h>
#include <txmempool.h>
#include <validation.h> // for pcoinsTip
#include <stdexcept>
#include <vector>
namespace {
class DSPSignatureChecker : public BaseSignatureChecker {
public:
DSPSignatureChecker(const DoubleSpendProof *proof, const DoubleSpendProof::Spender &spender, Amount amount)
: m_proof(proof),
m_spender(spender),
m_amount(amount)
{
}
bool CheckSig(const std::vector<uint8_t> &vchSigIn, const std::vector<uint8_t> &vchPubKey, const CScript &scriptCode, uint32_t /*flags*/) const override {
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
return false;
std::vector<uint8_t> vchSig(vchSigIn);
if (vchSig.empty())
return false;
vchSig.pop_back(); // drop the hashtype byte tacked on to the end of the signature
CHashWriter ss(SER_GETHASH, 0);
ss << m_spender.txVersion << m_spender.hashPrevOutputs << m_spender.hashSequence;
ss << m_proof->outPoint();
ss << static_cast<const CScriptBase &>(scriptCode);
ss << m_amount << m_spender.outSequence << m_spender.hashOutputs;
ss << m_spender.lockTime << (int32_t) m_spender.pushData.front().back();
const uint256 sighash = ss.GetHash();
if (vchSig.size() == 64)
return pubkey.VerifySchnorr(sighash, vchSig);
return pubkey.VerifyECDSA(sighash, vchSig);
}
bool CheckLockTime(const CScriptNum&) const override {
return true;
}
bool CheckSequence(const CScriptNum&) const override {
return true;
}
const DoubleSpendProof *m_proof;
const DoubleSpendProof::Spender &m_spender;
const Amount m_amount;
};
} // namespace
auto DoubleSpendProof::validate(const CTxMemPool &mempool, CTransactionRef spendingTx) const -> Validity
{
AssertLockHeld(cs_main);
AssertLockHeld(mempool.cs);
try {
// This ensures not empty and that all pushData vectors have exactly 1 item, among other things.
checkSanityOrThrow();
} catch (const std::runtime_error &e) {
LogPrint(BCLog::DSPROOF, "DoubleSpendProof::%s: %s\n", __func__, e.what());
return Invalid;
}
// Check if ordering is proper
int32_t diff = m_spender1.hashOutputs.Compare(m_spender2.hashOutputs);
if (diff == 0)
diff = m_spender1.hashPrevOutputs.Compare(m_spender2.hashPrevOutputs);
if (diff > 0)
return Invalid; // non-canonical order
// Get the previous output we are spending.
Coin coin;
{
const CCoinsViewMemPool view(pcoinsTip.get(), mempool); // this checks both mempool coins and confirmed coins
if (!view.GetCoin(outPoint(), coin)) {
/* if the output we spend is missing then either the tx just got mined
* or, more likely, our mempool just doesn't have it.
*/
return MissingUTXO;
}
}
const Amount &amount = coin.GetTxOut().nValue;
const CScript &prevOutScript = coin.GetTxOut().scriptPubKey;
/*
* Find the matching transaction spending this. Possibly identical to one
* of the sides of this DSP.
* We need this because we want the public key that it contains.
*/
if (!spendingTx) {
auto it = mempool.mapNextTx.find(m_outPoint);
if (it == mempool.mapNextTx.end())
return MissingTransaction;
spendingTx = mempool.get(it->second->GetId());
}
assert(bool(spendingTx));
/*
* TomZ: At this point (2019-07) we only support P2PKH payments.
*
* Since we have an actually spending tx, we could trivially support various other
* types of scripts because all we need to do is replace the signature from our 'tx'
* with the one that comes from the DSP.
*/
const txnouttype scriptType = TX_PUBKEYHASH; // FUTURE: look at prevTx to find out script-type
std::vector<uint8_t> pubkey;
for (const auto &vin : spendingTx->vin) {
if (vin.prevout == m_outPoint) {
// Found the input script we need!
const CScript &inScript = vin.scriptSig;
auto scriptIter = inScript.begin();
opcodetype type;
inScript.GetOp(scriptIter, type); // P2PKH: first signature
inScript.GetOp(scriptIter, type, pubkey); // then pubkey
break;
}
}
if (pubkey.empty())
return Invalid;
CScript inScript;
if (scriptType == TX_PUBKEYHASH) {
inScript << m_spender1.pushData.front();
inScript << pubkey;
}
DSPSignatureChecker checker1(this, m_spender1, amount);
ScriptError error;
ScriptExecutionMetrics metrics; // dummy
if (!VerifyScript(inScript, prevOutScript, 0 /*flags*/, checker1, metrics, &error)) {
LogPrint(BCLog::DSPROOF, "DoubleSpendProof failed validating first tx due to %s\n", ScriptErrorString(error));
return Invalid;
}
inScript.clear();
if (scriptType == TX_PUBKEYHASH) {
inScript << m_spender2.pushData.front();
inScript << pubkey;
}
DSPSignatureChecker checker2(this, m_spender2, amount);
if (!VerifyScript(inScript, prevOutScript, 0 /*flags*/, checker2, metrics, &error)) {
LogPrint(BCLog::DSPROOF, "DoubleSpendProof failed validating second tx due to %s\n", ScriptErrorString(error));
return Invalid;
}
return Valid;
}
/* static */ bool DoubleSpendProof::checkIsProofPossibleForAllInputsOfTx(const CTxMemPool &mempool, const CTransaction &tx)
{
AssertLockHeld(cs_main);
AssertLockHeld(mempool.cs);
if (tx.vin.empty() || tx.IsCoinBase()) {
return false;
}
const CCoinsViewMemPool view(pcoinsTip.get(), mempool); // this checks both mempool coins and confirmed coins
// Check all inputs
for (const auto & txin : tx.vin) {
Coin coin;
if (!view.GetCoin(txin.prevout, coin)) {
// if the Coin this tx spends is missing then either this tx just got mined or our mempool + blockchain
// view just doesn't have the coin.
return false;
}
if (!coin.GetTxOut().scriptPubKey.IsPayToPubKeyHash()) {
// For now, dsproof only supports P2PKH
return false;
}
}
return true;
}