Skip to content

Commit 404682b

Browse files
kallewoofajtowns
andcommitted
add signet basic support (signet.cpp)
Co-authored-by: Anthony Towns <[email protected]>
1 parent a2147d7 commit 404682b

File tree

4 files changed

+200
-0
lines changed

4 files changed

+200
-0
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ BITCOIN_CORE_H = \
201201
script/signingprovider.h \
202202
script/standard.h \
203203
shutdown.h \
204+
signet.h \
204205
streams.h \
205206
support/allocators/secure.h \
206207
support/allocators/zeroafterfree.h \
@@ -321,6 +322,7 @@ libbitcoin_server_a_SOURCES = \
321322
rpc/server.cpp \
322323
script/sigcache.cpp \
323324
shutdown.cpp \
325+
signet.cpp \
324326
timedata.cpp \
325327
torcontrol.cpp \
326328
txdb.cpp \

src/consensus/params.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ struct Params {
8080
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
8181
uint256 nMinimumChainWork;
8282
uint256 defaultAssumeValid;
83+
84+
/**
85+
* If true, witness commitments contain a payload equal to a Bitcoin Script solution
86+
* to the signet challenge. See BIP325.
87+
*/
88+
bool signet_blocks{false};
89+
std::vector<uint8_t> signet_challenge;
8390
};
8491
} // namespace Consensus
8592

src/signet.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright (c) 2019-2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <signet.h>
6+
7+
#include <array>
8+
#include <cstdint>
9+
#include <vector>
10+
11+
#include <consensus/merkle.h>
12+
#include <consensus/params.h>
13+
#include <consensus/validation.h>
14+
#include <core_io.h>
15+
#include <hash.h>
16+
#include <primitives/block.h>
17+
#include <primitives/transaction.h>
18+
#include <span.h>
19+
#include <script/interpreter.h>
20+
#include <script/standard.h>
21+
#include <streams.h>
22+
#include <util/strencodings.h>
23+
#include <util/system.h>
24+
#include <uint256.h>
25+
26+
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
27+
28+
static constexpr unsigned int BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
29+
30+
static bool FetchAndClearCommitmentSection(const Span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result)
31+
{
32+
CScript replacement;
33+
bool found_header = false;
34+
result.clear();
35+
36+
opcodetype opcode;
37+
CScript::const_iterator pc = witness_commitment.begin();
38+
std::vector<uint8_t> pushdata;
39+
while (witness_commitment.GetOp(pc, opcode, pushdata)) {
40+
if (pushdata.size() > 0) {
41+
if (!found_header && pushdata.size() > (size_t) header.size() && Span<const uint8_t>(pushdata.data(), header.size()) == header) {
42+
// pushdata only counts if it has the header _and_ some data
43+
result.insert(result.end(), pushdata.begin() + header.size(), pushdata.end());
44+
pushdata.erase(pushdata.begin() + header.size(), pushdata.end());
45+
found_header = true;
46+
}
47+
replacement << pushdata;
48+
} else {
49+
replacement << opcode;
50+
}
51+
}
52+
53+
if (found_header) witness_commitment = replacement;
54+
return found_header;
55+
}
56+
57+
static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CBlock& block)
58+
{
59+
std::vector<uint256> leaves;
60+
leaves.resize(block.vtx.size());
61+
leaves[0] = cb.GetHash();
62+
for (size_t s = 1; s < block.vtx.size(); ++s) {
63+
leaves[s] = block.vtx[s]->GetHash();
64+
}
65+
return ComputeMerkleRoot(std::move(leaves));
66+
}
67+
68+
SignetTxs SignetTxs::Create(const CBlock& block, const CScript& challenge)
69+
{
70+
CMutableTransaction tx_to_spend;
71+
tx_to_spend.nVersion = 0;
72+
tx_to_spend.nLockTime = 0;
73+
tx_to_spend.vin.emplace_back(COutPoint(), CScript(OP_0), 0);
74+
tx_to_spend.vout.emplace_back(0, challenge);
75+
76+
CMutableTransaction tx_spending;
77+
tx_spending.nVersion = 0;
78+
tx_spending.nLockTime = 0;
79+
tx_spending.vin.emplace_back(COutPoint(), CScript(), 0);
80+
tx_spending.vout.emplace_back(0, CScript(OP_RETURN));
81+
82+
// can't fill any other fields before extracting signet
83+
// responses from block coinbase tx
84+
85+
// find and delete signet signature
86+
if (block.vtx.empty()) return invalid(); // no coinbase tx in block; invalid
87+
CMutableTransaction modified_cb(*block.vtx.at(0));
88+
89+
const int cidx = GetWitnessCommitmentIndex(block);
90+
if (cidx == NO_WITNESS_COMMITMENT) {
91+
return invalid(); // require a witness commitment
92+
}
93+
94+
CScript& witness_commitment = modified_cb.vout.at(cidx).scriptPubKey;
95+
96+
std::vector<uint8_t> signet_solution;
97+
if (!FetchAndClearCommitmentSection(SIGNET_HEADER, witness_commitment, signet_solution)) {
98+
// no signet solution -- allow this to support OP_TRUE as trivial block challenge
99+
} else {
100+
try {
101+
VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0);
102+
v >> tx_spending.vin[0].scriptSig;
103+
v >> tx_spending.vin[0].scriptWitness.stack;
104+
if (!v.empty()) return invalid(); // extraneous data encountered
105+
} catch (const std::exception&) {
106+
return invalid(); // parsing error
107+
}
108+
}
109+
uint256 signet_merkle = ComputeModifiedMerkleRoot(modified_cb, block);
110+
111+
std::vector<uint8_t> block_data;
112+
CVectorWriter writer(SER_NETWORK, INIT_PROTO_VERSION, block_data, 0);
113+
writer << block.nVersion;
114+
writer << block.hashPrevBlock;
115+
writer << signet_merkle;
116+
writer << block.nTime;
117+
tx_to_spend.vin[0].scriptSig << block_data;
118+
tx_spending.vin[0].prevout = COutPoint(tx_to_spend.GetHash(), 0);
119+
120+
return {tx_to_spend, tx_spending};
121+
}
122+
123+
// Signet block solution checker
124+
bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams)
125+
{
126+
if (block.GetHash() == consensusParams.hashGenesisBlock) {
127+
// genesis block solution is always valid
128+
return true;
129+
}
130+
131+
const CScript challenge(consensusParams.signet_challenge.begin(), consensusParams.signet_challenge.end());
132+
const SignetTxs signet_txs(block, challenge);
133+
134+
if (!signet_txs.m_valid) {
135+
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution parse failure)\n");
136+
return false;
137+
}
138+
139+
const CScript& scriptSig = signet_txs.m_to_sign.vin[0].scriptSig;
140+
const CScriptWitness& witness = signet_txs.m_to_sign.vin[0].scriptWitness;
141+
142+
TransactionSignatureChecker sigcheck(&signet_txs.m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs.m_to_spend.vout[0].nValue);
143+
144+
if (!VerifyScript(scriptSig, signet_txs.m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
145+
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n");
146+
return false;
147+
}
148+
return true;
149+
}

src/signet.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) 2019-2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_SIGNET_H
6+
#define BITCOIN_SIGNET_H
7+
8+
#include <consensus/params.h>
9+
#include <primitives/block.h>
10+
#include <primitives/transaction.h>
11+
12+
/**
13+
* Extract signature and check whether a block has a valid solution
14+
*/
15+
bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams);
16+
17+
/**
18+
* Generate the signet tx corresponding to the given block
19+
*
20+
* The signet tx commits to everything in the block except:
21+
* 1. It hashes a modified merkle root with the signet signature removed.
22+
* 2. It skips the nonce.
23+
*/
24+
class SignetTxs {
25+
private:
26+
struct invalid {};
27+
SignetTxs(invalid i) : m_to_spend(), m_to_sign(), m_valid(false) { }
28+
29+
template<class T1, class T2>
30+
SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign}, m_valid(true) { }
31+
32+
static SignetTxs Create(const CBlock& block, const CScript& challenge);
33+
34+
public:
35+
SignetTxs(const CBlock& block, const CScript& challenge) : SignetTxs(Create(block, challenge)) { }
36+
37+
const CTransaction m_to_spend;
38+
const CTransaction m_to_sign;
39+
const bool m_valid;
40+
};
41+
42+
#endif // BITCOIN_SIGNET_H

0 commit comments

Comments
 (0)