mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
add pegin witness validation and extraction helper functions
This commit is contained in:
parent
16973cd1d2
commit
34d7cd072f
4 changed files with 182 additions and 0 deletions
|
|
@ -239,6 +239,8 @@ libbitcoin_wallet_a_SOURCES = \
|
||||||
wallet/wallet.cpp \
|
wallet/wallet.cpp \
|
||||||
wallet/walletdb.cpp \
|
wallet/walletdb.cpp \
|
||||||
policy/rbf.cpp \
|
policy/rbf.cpp \
|
||||||
|
primitives/bitcoin/merkleblock.cpp \
|
||||||
|
primitives/bitcoin/block.cpp \
|
||||||
$(BITCOIN_CORE_H)
|
$(BITCOIN_CORE_H)
|
||||||
|
|
||||||
# crypto primitives library
|
# crypto primitives library
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,12 @@ public:
|
||||||
return m_value;
|
return m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only used for peg-in witness values
|
||||||
|
int64_t getint64() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<unsigned char> getvch() const
|
std::vector<unsigned char> getvch() const
|
||||||
{
|
{
|
||||||
return serialize(m_value);
|
return serialize(m_value);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@
|
||||||
#include "pow.h"
|
#include "pow.h"
|
||||||
#include "primitives/block.h"
|
#include "primitives/block.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
#include "primitives/bitcoin/block.h"
|
||||||
|
#include "primitives/bitcoin/transaction.h"
|
||||||
|
#include "primitives/bitcoin/merkleblock.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "script/sigcache.h"
|
#include "script/sigcache.h"
|
||||||
|
|
@ -2204,6 +2207,173 @@ bool BitcoindRPCCheck(const bool init)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsValidPeginWitness(const CScriptWitness& pegin_witness) {
|
||||||
|
|
||||||
|
// Format on stack is as follows:
|
||||||
|
// 1) txid - txid of parent chain transaction - TODO-PEGIN unneeded?
|
||||||
|
// 2) nout - output number that is a peg-in output - unneeded?
|
||||||
|
// 3) value - the value of the pegin output
|
||||||
|
// 4) asset type - the asset type being pegged in
|
||||||
|
// 5) genesis blockhash - genesis block of the parent chain
|
||||||
|
// 6) witness program - program that script evaluation will be evaluated against
|
||||||
|
// 7) serialized transaction - serialized bitcoin transaction
|
||||||
|
// 8) txout proof - merkle proof connecting transaction to header
|
||||||
|
//
|
||||||
|
// First 6 values are enough to validate a peg-in without any internal knowledge
|
||||||
|
// of Bitcoin serialization. This is useful for further abstraction by outsourcing
|
||||||
|
// the other validity checks to RPC calls. The last two stack items
|
||||||
|
|
||||||
|
const std::vector<std::vector<unsigned char> >& stack = pegin_witness.stack;
|
||||||
|
// Must include all elements
|
||||||
|
if (stack.size() != 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Txid must be included
|
||||||
|
if (stack[0].size() != 32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint256 txid(stack[0]);
|
||||||
|
|
||||||
|
// Get output number
|
||||||
|
int nOut = CScriptNum(stack[1], true).getint();
|
||||||
|
|
||||||
|
// Get output value. Special 8 byte length to capture possible bitcoin values
|
||||||
|
CAmount value = CScriptNum(stack[2], true, 8).getint64();
|
||||||
|
|
||||||
|
// Get asset type
|
||||||
|
if (stack[3].size() != 32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CAsset asset(stack[3]);
|
||||||
|
|
||||||
|
// Get genesis blockhash
|
||||||
|
if (stack[4].size() != 32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint256 gen_hash(stack[4]);
|
||||||
|
|
||||||
|
// Get witness program
|
||||||
|
CScript witness_program(stack[5].begin(), stack[5].end());
|
||||||
|
int version = -1;
|
||||||
|
std::vector<unsigned char> witnessProgram;
|
||||||
|
if (!witness_program.IsWitnessProgram(version, witnessProgram)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get serialized transaction
|
||||||
|
Sidechain::Bitcoin::CTransactionRef pegtx;
|
||||||
|
try {
|
||||||
|
CDataStream pegtx_stream(stack[6], SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
pegtx_stream >> pegtx;
|
||||||
|
if (!pegtx_stream.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
// Invalid encoding of transaction
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get txout proof
|
||||||
|
Sidechain::Bitcoin::CMerkleBlock merkle_block;
|
||||||
|
std::vector<uint256> txHashes;
|
||||||
|
std::vector<unsigned int> txIndices;
|
||||||
|
|
||||||
|
try {
|
||||||
|
CDataStream merkleBlockStream(stack[7], SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
merkleBlockStream >> merkle_block;
|
||||||
|
if (!merkleBlockStream.empty() || !CheckBitcoinProof(merkle_block.header.GetHash(), merkle_block.header.nBits)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (merkle_block.txn.ExtractMatches(txHashes, txIndices) != merkle_block.header.hashMerkleRoot || txHashes.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
// Invalid encoding of merkle block
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that transaction matches txid
|
||||||
|
if (pegtx->GetHash() != txid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the merkle proof corresponds to the txid
|
||||||
|
if (txid != txHashes[0]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the genesis block corresponds to a valid peg (only one for now)
|
||||||
|
if (gen_hash != Params().ParentGenesisBlockHash()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the asset type corresponds to a valid pegged asset (only one for now)
|
||||||
|
if (asset != Params().GetConsensus().pegged_asset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the transaction nout/value matches
|
||||||
|
if (nOut < 0 || (unsigned int)nOut >= pegtx->vout.size() || value != pegtx->vout[nOut].nValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the witness program matches the p2ch on the transaction output
|
||||||
|
opcodetype opcodeTmp;
|
||||||
|
unsigned char tweak[32];
|
||||||
|
CSHA256().Write(witness_program.data(), witness_program.size()).Finalize(tweak);
|
||||||
|
CScript fedpegscript = Params().GetConsensus().fedpegScript;
|
||||||
|
// fedpegscript should be multisig or OP_TRUE
|
||||||
|
txnouttype type;
|
||||||
|
std::vector<std::vector<unsigned char> > solutions;
|
||||||
|
if (!Solver(fedpegscript, type, solutions) || (type != TX_MULTISIG && type != TX_TRUE)) {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CScript::iterator sdpc = fedpegscript.begin();
|
||||||
|
std::vector<unsigned char> vch;
|
||||||
|
while (fedpegscript.GetOp(sdpc, opcodeTmp, vch))
|
||||||
|
{
|
||||||
|
if (vch.size() == 33)
|
||||||
|
{
|
||||||
|
size_t pub_len = 33;
|
||||||
|
int ret;
|
||||||
|
unsigned char *pub_start = &(*(sdpc - pub_len));
|
||||||
|
secp256k1_pubkey pubkey;
|
||||||
|
ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_verify_amounts, &pubkey, pub_start, pub_len);
|
||||||
|
assert(ret == 1);
|
||||||
|
// If someone creates a tweak that makes this fail, they broke SHA256
|
||||||
|
ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_verify_amounts, &pubkey, tweak);
|
||||||
|
assert(ret == 1);
|
||||||
|
ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_verify_amounts, pub_start, &pub_len, &pubkey, SECP256K1_EC_COMPRESSED);
|
||||||
|
assert(ret == 1);
|
||||||
|
assert(pub_len == 33);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check against expected p2sh output
|
||||||
|
CScriptID expectedP2SH(fedpegscript);
|
||||||
|
CScript expected_script(CScript() << OP_HASH160 << ToByteVector(expectedP2SH) << OP_EQUAL);
|
||||||
|
if (pegtx->vout[nOut].scriptPubKey != expected_script) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, validate peg-in via rpc call
|
||||||
|
if (GetBoolArg("-validatepegin", DEFAULT_VALIDATE_PEGIN)) {
|
||||||
|
// TODO-PEGIN return useful error message
|
||||||
|
return IsConfirmedBitcoinBlock(merkle_block.header.GetHash(), GetArg("-peginconfirmationdepth", DEFAULT_PEGIN_CONFIRMATION_DEPTH));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs unblinded output to be used in amount and scriptpubkey checks during pegin
|
||||||
|
CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness) {
|
||||||
|
// Must check validity first for formatting reasons
|
||||||
|
assert(IsValidPeginWitness(pegin_witness));
|
||||||
|
return CTxOut(CAsset(pegin_witness.stack[3]), CScriptNum(pegin_witness.stack[2], true, 8).getint64(), CScript(pegin_witness.stack[5].begin(), pegin_witness.stack[5].end()));
|
||||||
|
}
|
||||||
|
|
||||||
// Protected by cs_main
|
// Protected by cs_main
|
||||||
VersionBitsCache versionbitscache;
|
VersionBitsCache versionbitscache;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,10 @@ void UnloadBlockIndex();
|
||||||
void ThreadScriptCheck();
|
void ThreadScriptCheck();
|
||||||
/** Check if bitcoind connection via RPC is correctly working*/
|
/** Check if bitcoind connection via RPC is correctly working*/
|
||||||
bool BitcoindRPCCheck(bool init);
|
bool BitcoindRPCCheck(bool init);
|
||||||
|
/** Checks pegin witness for validity */
|
||||||
|
bool IsValidPeginWitness(const CScriptWitness& pegin_witness);
|
||||||
|
/** Extracts an output from pegin witness for evaluation as a normal output */
|
||||||
|
CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness);
|
||||||
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
||||||
bool IsInitialBlockDownload();
|
bool IsInitialBlockDownload();
|
||||||
/** Format a string that describes several potential problems detected by the core.
|
/** Format a string that describes several potential problems detected by the core.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue