-
Notifications
You must be signed in to change notification settings - Fork 36.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add signet basic support (signet.cpp)
Co-authored-by: Anthony Towns <aj@erisian.com.au>
- Loading branch information
Showing
4 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Copyright (c) 2019-2020 The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <signet.h> | ||
|
||
#include <array> | ||
#include <cstdint> | ||
#include <vector> | ||
|
||
#include <consensus/merkle.h> | ||
#include <consensus/params.h> | ||
#include <consensus/validation.h> | ||
#include <core_io.h> | ||
#include <hash.h> | ||
#include <primitives/block.h> | ||
#include <primitives/transaction.h> | ||
#include <span.h> | ||
#include <script/interpreter.h> | ||
#include <script/standard.h> | ||
#include <streams.h> | ||
#include <util/strencodings.h> | ||
#include <util/system.h> | ||
#include <uint256.h> | ||
|
||
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2}; | ||
|
||
static constexpr unsigned int BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY; | ||
|
||
static bool FetchAndClearCommitmentSection(const Span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result) | ||
{ | ||
CScript replacement; | ||
bool found_header = false; | ||
result.clear(); | ||
|
||
opcodetype opcode; | ||
CScript::const_iterator pc = witness_commitment.begin(); | ||
std::vector<uint8_t> pushdata; | ||
while (witness_commitment.GetOp(pc, opcode, pushdata)) { | ||
if (pushdata.size() > 0) { | ||
if (!found_header && pushdata.size() > (size_t) header.size() && Span<const uint8_t>(pushdata.data(), header.size()) == header) { | ||
// pushdata only counts if it has the header _and_ some data | ||
result.insert(result.end(), pushdata.begin() + header.size(), pushdata.end()); | ||
pushdata.erase(pushdata.begin() + header.size(), pushdata.end()); | ||
found_header = true; | ||
} | ||
replacement << pushdata; | ||
} else { | ||
replacement << opcode; | ||
} | ||
} | ||
|
||
if (found_header) witness_commitment = replacement; | ||
return found_header; | ||
} | ||
|
||
static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CBlock& block) | ||
{ | ||
std::vector<uint256> leaves; | ||
leaves.resize(block.vtx.size()); | ||
leaves[0] = cb.GetHash(); | ||
for (size_t s = 1; s < block.vtx.size(); ++s) { | ||
leaves[s] = block.vtx[s]->GetHash(); | ||
} | ||
return ComputeMerkleRoot(std::move(leaves)); | ||
} | ||
|
||
SignetTxs SignetTxs::Create(const CBlock& block, const CScript& challenge) | ||
{ | ||
CMutableTransaction tx_to_spend; | ||
tx_to_spend.nVersion = 0; | ||
tx_to_spend.nLockTime = 0; | ||
tx_to_spend.vin.emplace_back(COutPoint(), CScript(OP_0), 0); | ||
tx_to_spend.vout.emplace_back(0, challenge); | ||
|
||
CMutableTransaction tx_spending; | ||
tx_spending.nVersion = 0; | ||
tx_spending.nLockTime = 0; | ||
tx_spending.vin.emplace_back(COutPoint(), CScript(), 0); | ||
tx_spending.vout.emplace_back(0, CScript(OP_RETURN)); | ||
|
||
// can't fill any other fields before extracting signet | ||
// responses from block coinbase tx | ||
|
||
// find and delete signet signature | ||
if (block.vtx.empty()) return invalid(); // no coinbase tx in block; invalid | ||
CMutableTransaction modified_cb(*block.vtx.at(0)); | ||
|
||
const int cidx = GetWitnessCommitmentIndex(block); | ||
if (cidx == NO_WITNESS_COMMITMENT) { | ||
return invalid(); // require a witness commitment | ||
} | ||
|
||
CScript& witness_commitment = modified_cb.vout.at(cidx).scriptPubKey; | ||
|
||
std::vector<uint8_t> signet_solution; | ||
if (!FetchAndClearCommitmentSection(SIGNET_HEADER, witness_commitment, signet_solution)) { | ||
// no signet solution -- allow this to support OP_TRUE as trivial block challenge | ||
} else { | ||
try { | ||
VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0); | ||
v >> tx_spending.vin[0].scriptSig; | ||
v >> tx_spending.vin[0].scriptWitness.stack; | ||
if (!v.empty()) return invalid(); // extraneous data encountered | ||
} catch (const std::exception&) { | ||
return invalid(); // parsing error | ||
} | ||
} | ||
uint256 signet_merkle = ComputeModifiedMerkleRoot(modified_cb, block); | ||
|
||
std::vector<uint8_t> block_data; | ||
CVectorWriter writer(SER_NETWORK, INIT_PROTO_VERSION, block_data, 0); | ||
writer << block.nVersion; | ||
writer << block.hashPrevBlock; | ||
writer << signet_merkle; | ||
writer << block.nTime; | ||
tx_to_spend.vin[0].scriptSig << block_data; | ||
tx_spending.vin[0].prevout = COutPoint(tx_to_spend.GetHash(), 0); | ||
|
||
return {tx_to_spend, tx_spending}; | ||
} | ||
|
||
// Signet block solution checker | ||
bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams) | ||
{ | ||
if (block.GetHash() == consensusParams.hashGenesisBlock) { | ||
// genesis block solution is always valid | ||
return true; | ||
} | ||
|
||
const CScript challenge(consensusParams.signet_challenge.begin(), consensusParams.signet_challenge.end()); | ||
const SignetTxs signet_txs(block, challenge); | ||
|
||
if (!signet_txs.m_valid) { | ||
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution parse failure)\n"); | ||
return false; | ||
} | ||
|
||
const CScript& scriptSig = signet_txs.m_to_sign.vin[0].scriptSig; | ||
const CScriptWitness& witness = signet_txs.m_to_sign.vin[0].scriptWitness; | ||
|
||
TransactionSignatureChecker sigcheck(&signet_txs.m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs.m_to_spend.vout[0].nValue); | ||
|
||
if (!VerifyScript(scriptSig, signet_txs.m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) { | ||
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n"); | ||
return false; | ||
} | ||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright (c) 2019-2020 The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#ifndef BITCOIN_SIGNET_H | ||
#define BITCOIN_SIGNET_H | ||
|
||
#include <consensus/params.h> | ||
#include <primitives/block.h> | ||
#include <primitives/transaction.h> | ||
|
||
/** | ||
* Extract signature and check whether a block has a valid solution | ||
*/ | ||
bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams); | ||
|
||
/** | ||
* Generate the signet tx corresponding to the given block | ||
* | ||
* The signet tx commits to everything in the block except: | ||
* 1. It hashes a modified merkle root with the signet signature removed. | ||
* 2. It skips the nonce. | ||
*/ | ||
class SignetTxs { | ||
private: | ||
struct invalid {}; | ||
SignetTxs(invalid i) : m_to_spend(), m_to_sign(), m_valid(false) { } | ||
|
||
template<class T1, class T2> | ||
SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign}, m_valid(true) { } | ||
|
||
static SignetTxs Create(const CBlock& block, const CScript& challenge); | ||
|
||
public: | ||
SignetTxs(const CBlock& block, const CScript& challenge) : SignetTxs(Create(block, challenge)) { } | ||
|
||
const CTransaction m_to_spend; | ||
const CTransaction m_to_sign; | ||
const bool m_valid; | ||
}; | ||
|
||
#endif // BITCOIN_SIGNET_H |