mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
Update PAK internals, helper functions
This commit is contained in:
parent
16b87ba951
commit
804cd9f75f
5 changed files with 76 additions and 187 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue