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 <pubkey.h>
#include <dynafed.h>
// ELEMENTS
@ -33,73 +34,9 @@ public:
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
{
if (this->reject != other.reject) {
return false;
} else if (this->m_offline_keys.size() != other.m_offline_keys.size()) {
if (this->m_offline_keys.size() != other.m_offline_keys.size()) {
return false;
} else {
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;
}
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()
|| 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);
}
paklist = CPAKList(offline_keys, online_keys, is_reject);
paklist = CPAKList(offline_keys, online_keys);
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);
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);
online_keys.push_back(std::vector<unsigned char>(pubkey, pubkey+outputlen));
}
is_reject = reject;
}
// 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;
}
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 <secp256k1/include/secp256k1_whitelist.h>
#include <boost/optional.hpp>
#include <chain.h>
class CPAKList
{
private:
std::vector<secp256k1_pubkey> m_offline_keys;
std::vector<secp256k1_pubkey> m_online_keys;
bool reject;
std::vector<CScript> GenerateCoinbasePAKCommitments() const;
std::vector<CScript> GenerateCoinbasePAKReject() const;
public:
CPAKList()
{
reject = true;
}
CPAKList() {}
/**
* 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.
*/
CPAKList(std::vector<secp256k1_pubkey> offline_keys, std::vector<secp256k1_pubkey> online_keys, bool reject) :
m_offline_keys(offline_keys), m_online_keys(online_keys), reject(reject) {
CPAKList(std::vector<secp256k1_pubkey> offline_keys, std::vector<secp256k1_pubkey> online_keys) :
m_offline_keys(offline_keys), m_online_keys(online_keys) {
assert(m_offline_keys.size() == m_online_keys.size());
assert(m_offline_keys.size() <= SECP256K1_WHITELIST_MAX_N_KEYS);
}
@ -41,11 +35,7 @@ public:
}
bool IsReject() const
{
return reject;
}
bool IsEmpty() const
{
return !reject && this->size() == 0;
return size()==0;
}
std::vector<secp256k1_pubkey> OnlineKeys() const
{
@ -60,24 +50,21 @@ public:
return m_offline_keys.size();
}
static CScript Magic();
/** 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 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;
static bool FromBytes(CPAKList &paklist, const std::vector<std::vector<unsigned char> >& offline_keys, const std::vector<std::vector<unsigned char> >& online_keys);
void ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys) const;
};
/**
** 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>
**/
bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash);
// ELEMENTS:
extern boost::optional<CPAKList> g_paklist_config;
extern CPAKList g_paklist_blockchain;
///////////
CPAKList GetActivePAKList(const CBlockIndex* pblockindex, const Consensus::Params& params);
bool IsPAKValidOutput(const CTxOut& txout, const CPAKList& paklist);
bool IsPAKValidTx(const CTransaction& tx, const CPAKList& paklist);
#endif // BITCOIN_PRIMITIVES_PAK_H

View file

@ -667,8 +667,7 @@ UniValue FormatPAKList(CPAKList &paklist) {
UniValue paklist_value(UniValue::VOBJ);
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);
paklist.ToBytes(offline_keys, online_keys);
UniValue retOnline(UniValue::VARR);
UniValue retOffline(UniValue::VARR);
@ -679,7 +678,7 @@ UniValue FormatPAKList(CPAKList &paklist) {
}
paklist_value.pushKV("online", retOnline);
paklist_value.pushKV("offline", retOffline);
paklist_value.pushKV("reject", is_reject);
paklist_value.pushKV("reject", retOffline.empty());
return paklist_value;
}
@ -688,12 +687,11 @@ UniValue getpakinfo(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
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{
"{\n"
"\"config_paklist\" (array) The PAK list loaded from beta.conf at startup\n"
"\"block_paklist\" (array) The PAK list loaded from latest block commitment\n"
"\"block_paklist\" (array) The PAK list loaded from latest epoch\n"
"}\n"
},
RPCExamples{""},
@ -701,13 +699,9 @@ UniValue getpakinfo(const JSONRPCRequest& request)
LOCK(cs_main);
UniValue paklist_value(UniValue::VOBJ);
if (g_paklist_config) {
paklist_value = FormatPAKList(*g_paklist_config);
}
UniValue ret(UniValue::VOBJ);
ret.pushKV("config_paklist", paklist_value);
ret.pushKV("block_paklist", FormatPAKList(g_paklist_blockchain));
CPAKList paklist = GetActivePAKList(chainActive.Tip(), Params().GetConsensus());
ret.pushKV("block_paklist", FormatPAKList(paklist));
return ret;
}

View file

@ -2628,17 +2628,6 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
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;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.;
@ -3448,72 +3437,6 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
}
// 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.

View file

@ -33,7 +33,6 @@
#include <atomic>
#include <boost/optional.hpp> // GetPAKKeysFromCommitment
#include <primitives/pak.h> // CPAKList
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);
// 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 */
class CVerifyDB {