Update PAK internals, helper functions

This commit is contained in:
Gregory Sanders 2019-05-31 12:39:55 -04:00
parent 16b87ba951
commit 804cd9f75f
5 changed files with 76 additions and 187 deletions

View file

@ -4,6 +4,7 @@
#include <primitives/pak.h> #include <primitives/pak.h>
#include <pubkey.h> #include <pubkey.h>
#include <dynafed.h>
// ELEMENTS // ELEMENTS
@ -33,73 +34,9 @@ public:
static CSecp256k1Init instance_of_csecp256k1; static CSecp256k1Init instance_of_csecp256k1;
} }
CScript CPAKList::Magic()
{
CScript scriptPubKey;
scriptPubKey.resize(6);
scriptPubKey[0] = OP_RETURN;
scriptPubKey[1] = 0x04;
scriptPubKey[2] = 0xab;
scriptPubKey[3] = 0x22;
scriptPubKey[4] = 0xaa;
scriptPubKey[5] = 0xee;
return scriptPubKey;
}
std::vector<CScript> CPAKList::GenerateCoinbasePAKCommitments() const
{
std::vector<CScript> commitments;
CScript scriptPubKey = CPAKList::Magic();
for (unsigned int i = 0; i < m_offline_keys.size(); i++) {
CScript scriptCommitment(scriptPubKey);
unsigned char pubkey[33];
size_t outputlen = 33;
secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_offline_keys[i], SECP256K1_EC_COMPRESSED);
assert(outputlen == 33);
scriptCommitment << std::vector<unsigned char>(pubkey, pubkey+outputlen);
secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_online_keys[i], SECP256K1_EC_COMPRESSED);
assert(outputlen == 33);
scriptCommitment << std::vector<unsigned char>(pubkey, pubkey+outputlen);
commitments.push_back(scriptCommitment);
}
return commitments;
}
std::vector<CScript> CPAKList::GenerateCoinbasePAKReject() const
{
CScript scriptPubKey = CPAKList::Magic();
std::vector<unsigned char> reject;
reject.push_back('R');
reject.push_back('E');
reject.push_back('J');
reject.push_back('E');
reject.push_back('C');
reject.push_back('T');
scriptPubKey << reject;
std::vector<CScript> commitment;
commitment.push_back(scriptPubKey);
return commitment;
}
void CPAKList::CreateCommitments(std::vector<CScript> &commitments) const
{
if(reject) {
commitments = GenerateCoinbasePAKReject();
} else {
commitments = GenerateCoinbasePAKCommitments();
}
}
bool CPAKList::operator==(const CPAKList &other) const bool CPAKList::operator==(const CPAKList &other) const
{ {
if (this->reject != other.reject) { if (this->m_offline_keys.size() != other.m_offline_keys.size()) {
return false;
} else if (this->m_offline_keys.size() != other.m_offline_keys.size()) {
return false; return false;
} else { } else {
for (unsigned int i = 0; i < this->m_offline_keys.size(); i++) { for (unsigned int i = 0; i < this->m_offline_keys.size(); i++) {
@ -112,7 +49,7 @@ bool CPAKList::operator==(const CPAKList &other) const
return true; return true;
} }
bool CPAKList::FromBytes(CPAKList &paklist, std::vector<std::vector<unsigned char> >& offline_keys_bytes, std::vector<std::vector<unsigned char> >& online_keys_bytes, bool is_reject) bool CPAKList::FromBytes(CPAKList &paklist, const std::vector<std::vector<unsigned char> >& offline_keys_bytes, const std::vector<std::vector<unsigned char> >& online_keys_bytes)
{ {
if(offline_keys_bytes.size() != online_keys_bytes.size() if(offline_keys_bytes.size() != online_keys_bytes.size()
|| offline_keys_bytes.size() > SECP256K1_WHITELIST_MAX_N_KEYS) { || offline_keys_bytes.size() > SECP256K1_WHITELIST_MAX_N_KEYS) {
@ -134,11 +71,11 @@ bool CPAKList::FromBytes(CPAKList &paklist, std::vector<std::vector<unsigned cha
online_keys.push_back(pubkey2); online_keys.push_back(pubkey2);
} }
paklist = CPAKList(offline_keys, online_keys, is_reject); paklist = CPAKList(offline_keys, online_keys);
return true; return true;
} }
void CPAKList::ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys, bool &is_reject) const void CPAKList::ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys) const
{ {
offline_keys.resize(0); offline_keys.resize(0);
online_keys.resize(0); online_keys.resize(0);
@ -151,7 +88,6 @@ void CPAKList::ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, s
secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, 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<unsigned char>(pubkey, pubkey+outputlen)); online_keys.push_back(std::vector<unsigned char>(pubkey, pubkey+outputlen));
} }
is_reject = reject;
} }
// Proof follows the OP_RETURN <genesis_block_hash> <destination_scriptpubkey> // Proof follows the OP_RETURN <genesis_block_hash> <destination_scriptpubkey>
@ -243,3 +179,55 @@ bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash)
return true; return true;
} }
CPAKList CreatePAKListFromExtensionSpace(const std::vector<std::vector<unsigned char>>& extension_space)
{
std::vector<std::vector<unsigned char>> offline_keys;
std::vector<std::vector<unsigned char>> online_keys;
for (const auto& entry : extension_space) {
// As soon as we find something that is possibly not 2 serialized pubkeys
// we stop looking. CPAKList::FromBytes does pubkey validation itself.
if (entry.size() != 66) {
break;
}
offline_keys.emplace_back(entry.begin(), entry.begin()+33);
online_keys.emplace_back(entry.begin()+33, entry.end());
// Allow additional data, just ignore
if (offline_keys.size() == SECP256K1_WHITELIST_MAX_N_KEYS) {
break;
}
}
CPAKList paklist;
if (!CPAKList::FromBytes(paklist, offline_keys, online_keys)) {
return CPAKList();
}
return paklist;
}
CPAKList GetActivePAKList(const CBlockIndex* pblockindex, const Consensus::Params& params)
{
assert(pblockindex);
return CreatePAKListFromExtensionSpace(ComputeNextBlockFullCurrentParameters(pblockindex, params).m_extension_space);
}
bool IsPAKValidOutput(const CTxOut& txout, const CPAKList& paklist)
{
const CChainParams& params = Params();
if (txout.scriptPubKey.IsPegoutScript(params.ParentGenesisBlockHash()) &&
txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == params.GetConsensus().pegged_asset &&
(!ScriptHasValidPAKProof(txout.scriptPubKey, params.ParentGenesisBlockHash(), paklist))) {
return false;
}
return true;
}
bool IsPAKValidTx(const CTransaction& tx, const CPAKList& paklist)
{
for (const auto& txout : tx.vout) {
if (!IsPAKValidOutput(txout, paklist)) {
return false;
}
}
return true;
}

View file

@ -8,28 +8,22 @@
#include <script/script.h> #include <script/script.h>
#include <secp256k1/include/secp256k1_whitelist.h> #include <secp256k1/include/secp256k1_whitelist.h>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <chain.h>
class CPAKList class CPAKList
{ {
private: private:
std::vector<secp256k1_pubkey> m_offline_keys; std::vector<secp256k1_pubkey> m_offline_keys;
std::vector<secp256k1_pubkey> m_online_keys; std::vector<secp256k1_pubkey> m_online_keys;
bool reject;
std::vector<CScript> GenerateCoinbasePAKCommitments() const;
std::vector<CScript> GenerateCoinbasePAKReject() const;
public: public:
CPAKList() CPAKList() {}
{
reject = true;
}
/** /**
* Creates a new CPAKList. Requires that the number of offline keys is the same as the number of online keys * Creates a new CPAKList. Requires that the number of offline keys is the same as the number of online keys
* and that this number is not larger than SECP256K1_WHITELIST_MAX_N_KEYS. * and that this number is not larger than SECP256K1_WHITELIST_MAX_N_KEYS.
*/ */
CPAKList(std::vector<secp256k1_pubkey> offline_keys, std::vector<secp256k1_pubkey> online_keys, bool reject) : CPAKList(std::vector<secp256k1_pubkey> offline_keys, std::vector<secp256k1_pubkey> online_keys) :
m_offline_keys(offline_keys), m_online_keys(online_keys), reject(reject) { m_offline_keys(offline_keys), m_online_keys(online_keys) {
assert(m_offline_keys.size() == m_online_keys.size()); assert(m_offline_keys.size() == m_online_keys.size());
assert(m_offline_keys.size() <= SECP256K1_WHITELIST_MAX_N_KEYS); assert(m_offline_keys.size() <= SECP256K1_WHITELIST_MAX_N_KEYS);
} }
@ -41,11 +35,7 @@ public:
} }
bool IsReject() const bool IsReject() const
{ {
return reject; return size()==0;
}
bool IsEmpty() const
{
return !reject && this->size() == 0;
} }
std::vector<secp256k1_pubkey> OnlineKeys() const std::vector<secp256k1_pubkey> OnlineKeys() const
{ {
@ -60,24 +50,21 @@ public:
return m_offline_keys.size(); return m_offline_keys.size();
} }
static CScript Magic(); static bool FromBytes(CPAKList &paklist, const std::vector<std::vector<unsigned char> >& offline_keys, const std::vector<std::vector<unsigned char> >& online_keys);
/** Produce a list of scripts to add to the coinbase to signal changes in PAK list or rejection of any pak proofs to nodes */ void ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys) const;
void CreateCommitments(std::vector<CScript> &commitments) const;
static bool FromBytes(CPAKList &paklist, std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys, bool is_reject);
void ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys, bool &is_reject) const;
}; };
/** /**
** Returns true if the script includes valid pegout proof ** Returns true if the script includes valid pegout proof
** given the PAK list loaded. Two pushes after regular pegout script: ** given the PAK list. Two pushes after regular pegout script:
** <full_pubkey> <proof> ** <full_pubkey> <proof>
**/ **/
bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash); bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash);
// ELEMENTS: CPAKList GetActivePAKList(const CBlockIndex* pblockindex, const Consensus::Params& params);
extern boost::optional<CPAKList> g_paklist_config;
extern CPAKList g_paklist_blockchain; bool IsPAKValidOutput(const CTxOut& txout, const CPAKList& paklist);
///////////
bool IsPAKValidTx(const CTransaction& tx, const CPAKList& paklist);
#endif // BITCOIN_PRIMITIVES_PAK_H #endif // BITCOIN_PRIMITIVES_PAK_H

View file

@ -667,8 +667,7 @@ UniValue FormatPAKList(CPAKList &paklist) {
UniValue paklist_value(UniValue::VOBJ); UniValue paklist_value(UniValue::VOBJ);
std::vector<std::vector<unsigned char> > offline_keys; std::vector<std::vector<unsigned char> > offline_keys;
std::vector<std::vector<unsigned char> > online_keys; std::vector<std::vector<unsigned char> > online_keys;
bool is_reject; paklist.ToBytes(offline_keys, online_keys);
paklist.ToBytes(offline_keys, online_keys, is_reject);
UniValue retOnline(UniValue::VARR); UniValue retOnline(UniValue::VARR);
UniValue retOffline(UniValue::VARR); UniValue retOffline(UniValue::VARR);
@ -679,7 +678,7 @@ UniValue FormatPAKList(CPAKList &paklist) {
} }
paklist_value.pushKV("online", retOnline); paklist_value.pushKV("online", retOnline);
paklist_value.pushKV("offline", retOffline); paklist_value.pushKV("offline", retOffline);
paklist_value.pushKV("reject", is_reject); paklist_value.pushKV("reject", retOffline.empty());
return paklist_value; return paklist_value;
} }
@ -688,12 +687,11 @@ UniValue getpakinfo(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0) if (request.fHelp || request.params.size() != 0)
throw std::runtime_error( throw std::runtime_error(
RPCHelpMan{"getpakinfo", RPCHelpMan{"getpakinfo",
"\nReturns relevant pegout authorization key (PAK) information about this node, both from command line arguments and blockchain data.\n", "\nReturns relevant pegout authorization key (PAK) information about this node, both from blockchain data.\n",
{}, {},
RPCResult{ RPCResult{
"{\n" "{\n"
"\"config_paklist\" (array) The PAK list loaded from beta.conf at startup\n" "\"block_paklist\" (array) The PAK list loaded from latest epoch\n"
"\"block_paklist\" (array) The PAK list loaded from latest block commitment\n"
"}\n" "}\n"
}, },
RPCExamples{""}, RPCExamples{""},
@ -701,13 +699,9 @@ UniValue getpakinfo(const JSONRPCRequest& request)
LOCK(cs_main); LOCK(cs_main);
UniValue paklist_value(UniValue::VOBJ);
if (g_paklist_config) {
paklist_value = FormatPAKList(*g_paklist_config);
}
UniValue ret(UniValue::VOBJ); UniValue ret(UniValue::VOBJ);
ret.pushKV("config_paklist", paklist_value); CPAKList paklist = GetActivePAKList(chainActive.Tip(), Params().GetConsensus());
ret.pushKV("block_paklist", FormatPAKList(g_paklist_blockchain)); ret.pushKV("block_paklist", FormatPAKList(paklist));
return ret; return ret;
} }

View file

@ -2628,17 +2628,6 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED)) if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
return false; return false;
// Get PAK commitment from coinbase, if it exists
boost::optional<CPAKList> paklist = GetPAKKeysFromCommitment(*blockConnecting.vtx[0]);
if (paklist) {
std::vector<std::vector<unsigned char> > offline_keys;
std::vector<std::vector<unsigned char> > online_keys;
bool is_reject;
paklist->ToBytes(offline_keys, online_keys, is_reject);
pblocktree->WritePAKList(offline_keys, online_keys, is_reject);
g_paklist_blockchain = *paklist;
}
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal); LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.; // Remove conflicting transactions from the mempool.;
@ -3448,72 +3437,6 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
} }
// ELEMENTS // ELEMENTS
boost::optional<CPAKList> GetPAKKeysFromCommitment(const CTransaction& coinbase)
{
std::vector<std::vector<unsigned char> > offline_keys;
std::vector<std::vector<unsigned char> > online_keys;
bool is_reject = false;
for (unsigned int i = 0; i < coinbase.vout.size(); i++) {
const CScript& scriptPubKey = coinbase.vout[i].scriptPubKey;
// OP + push + 4 bytes + push + 33 bytes + push + 33 bytes
// or
// OP + push + 4 bytes + push + 6 bytes (REJECT)
CScript::const_iterator pc = scriptPubKey.begin();
std::vector<unsigned char> data;
opcodetype opcode;
if (!scriptPubKey.GetOp(pc, opcode, data) || opcode != OP_RETURN){
continue;
}
if (!scriptPubKey.GetOp(pc, opcode, data) || data.size() != 4 ||
data[0] != 0xab || data[1] != 0x22 || data[2] != 0xaa || data[3] != 0xee) {
continue;
}
if (!scriptPubKey.GetOp(pc, opcode, data)){
continue;
}
// Check for pak list reject signal
// Returns an empty list regardless of other commitments
if (data.size() == 6 && data[0] == 'R' && data[1] == 'E' && data[2] == 'J' && data[3] == 'E' && data[4] == 'C' && data[5] == 'T') {
is_reject = true;
continue;
}
// Check for offline key
if (data.size() != 33) {
continue;
}
// Check for online key
std::vector<unsigned char> data_online;
if (!scriptPubKey.GetOp(pc, opcode, data_online) || data_online.size() != 33) {
continue;
}
offline_keys.push_back(data);
online_keys.push_back(data_online);
}
if (is_reject) {
offline_keys.clear();
online_keys.clear();
}
if (!is_reject && offline_keys.size() == 0) {
return boost::none;
}
CPAKList paklist;
if (!CPAKList::FromBytes(paklist, offline_keys, online_keys, is_reject)) {
return boost::none;
} else {
return paklist;
}
}
/** Context-dependent validity checks. /** Context-dependent validity checks.

View file

@ -33,7 +33,6 @@
#include <atomic> #include <atomic>
#include <boost/optional.hpp> // GetPAKKeysFromCommitment
#include <primitives/pak.h> // CPAKList #include <primitives/pak.h> // CPAKList
class CBlockIndex; class CBlockIndex;
@ -423,8 +422,6 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr
std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
// ELEMENTS // ELEMENTS
/** Extract pak commitment from coinbase, if it exists. List must be ordered, but not necessarily consecutive in output index */
boost::optional<CPAKList> GetPAKKeysFromCommitment(const CTransaction& coinbase);
/** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */ /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
class CVerifyDB { class CVerifyDB {