From 43424cbf713afc834dcbdb302ec09cac011d62b0 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Thu, 17 Jan 2019 14:48:35 -0500 Subject: [PATCH] Add PAK proof validation function --- src/primitives/pak.cpp | 99 ++++++++++++++++++++++++++++++++++++++---- src/primitives/pak.h | 8 ++++ 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/primitives/pak.cpp b/src/primitives/pak.cpp index 665bcc1478..716810b31d 100644 --- a/src/primitives/pak.cpp +++ b/src/primitives/pak.cpp @@ -3,18 +3,19 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include namespace { -static secp256k1_context *secp256k1_ctx; +static secp256k1_context *secp256k1_ctx_pak; class CSecp256k1Init { public: CSecp256k1Init() { - secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_ctx_pak = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); } ~CSecp256k1Init() { - secp256k1_context_destroy(secp256k1_ctx); + secp256k1_context_destroy(secp256k1_ctx_pak); } }; static CSecp256k1Init instance_of_csecp256k1; @@ -42,10 +43,10 @@ std::vector CPAKList::GenerateCoinbasePAKCommitments() const CScript scriptCommitment(scriptPubKey); unsigned char pubkey[33]; size_t outputlen = 33; - secp256k1_ec_pubkey_serialize(secp256k1_ctx, pubkey, &outputlen, &m_offline_keys[i], SECP256K1_EC_COMPRESSED); + secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_offline_keys[i], SECP256K1_EC_COMPRESSED); assert(outputlen == 33); scriptCommitment << std::vector(pubkey, pubkey+outputlen); - secp256k1_ec_pubkey_serialize(secp256k1_ctx, pubkey, &outputlen, &m_online_keys[i], SECP256K1_EC_COMPRESSED); + secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_online_keys[i], SECP256K1_EC_COMPRESSED); assert(outputlen == 33); scriptCommitment << std::vector(pubkey, pubkey+outputlen); commitments.push_back(scriptCommitment); @@ -111,8 +112,8 @@ bool CPAKList::FromBytes(CPAKList &paklist, std::vector >& offline_keys, s for (unsigned int i = 0; i < m_offline_keys.size(); i++) { unsigned char pubkey[33]; size_t outputlen = 33; - secp256k1_ec_pubkey_serialize(secp256k1_ctx, pubkey, &outputlen, &m_offline_keys[i], SECP256K1_EC_COMPRESSED); + secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_offline_keys[i], SECP256K1_EC_COMPRESSED); offline_keys.push_back(std::vector(pubkey, pubkey+outputlen)); - secp256k1_ec_pubkey_serialize(secp256k1_ctx, pubkey, &outputlen, &m_online_keys[i], SECP256K1_EC_COMPRESSED); + secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_online_keys[i], SECP256K1_EC_COMPRESSED); online_keys.push_back(std::vector(pubkey, pubkey+outputlen)); } is_reject = reject; } +// Proof follows the OP_RETURN +// in multiple pushes: +bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash) +{ + assert(script.IsPegoutScript(genesis_hash)); + CPAKList paklist; + if (g_paklist_config) { + paklist = *g_paklist_config; + } else { + paklist = g_paklist_blockchain; + } + + if (paklist.IsReject() || paklist.IsEmpty()) { + return false; + } + + CScript::const_iterator pc = script.begin(); + std::vector data; + opcodetype opcode; + + script.GetOp(pc, opcode, data); + script.GetOp(pc, opcode, data); + script.GetOp(pc, opcode, data); + + CScript destination(data.begin(), data.end()); + + // Only accept p2pkh + if (!destination.IsPayToPubkeyHash()) { + return false; + } + + // Grab pubkey hash within the extracted sub-script + CScript::const_iterator pc2 = destination.begin(); + std::vector data2; + opcodetype opcode2; + if (!destination.GetOp(pc2, opcode2, data2) || !destination.GetOp(pc2, opcode2, data2) ||!destination.GetOp(pc2, opcode2, data2)) { + return false; + } + + // Follow-up with full pubkey + if (!script.GetOp(pc, opcode, data) || opcode != 33 || data.size() != 33) { + return false; + } + + CPubKey cpubkey(data.begin(), data.end()); + //Ensure the chaindest p2pkh matches the included pubkey + if (cpubkey.GetID() != uint160(data2)) { + return false; + } + + // Parse pubkey + secp256k1_pubkey pubkey; + if (secp256k1_ec_pubkey_parse(secp256k1_ctx_pak, &pubkey, &data[0], data.size()) != 1) { + return false; + } + + if (!script.GetOp(pc, opcode, data) || opcode > OP_PUSHDATA4 || data.size() == 0) { + return false; + } + + // Parse whitelist proof + secp256k1_whitelist_signature sig; + if (secp256k1_whitelist_signature_parse(secp256k1_ctx_pak, &sig, &data[0], data.size()) != 1) + return false; + + if (secp256k1_whitelist_signature_n_keys(&sig) != paklist.size()) { + return false; + } + + if (secp256k1_whitelist_verify(secp256k1_ctx_pak, &sig, &paklist.OnlineKeys()[0], &paklist.OfflineKeys()[0], paklist.size(), &pubkey) != 1) { + return false; + } + + //No more pushes allowed + if (script.GetOp(pc, opcode, data)) { + return false; + } + + return true; +} diff --git a/src/primitives/pak.h b/src/primitives/pak.h index 11cf1a8757..3eb5506d47 100644 --- a/src/primitives/pak.h +++ b/src/primitives/pak.h @@ -67,4 +67,12 @@ public: void ToBytes(std::vector >& offline_keys, std::vector >& online_keys, bool &is_reject) const; }; +/** + ** Returns true if the script includes valid pegout proof + ** given the PAK list loaded. Two pushes after regular pegout script: + ** + **/ +bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash); + + #endif // BITCOIN_PRIMITIVES_PAK_H