Skip to content

Commit a306c0c

Browse files
committed
Update PAK internals, helper functions
1 parent 60d4093 commit a306c0c

File tree

5 files changed

+76
-187
lines changed

5 files changed

+76
-187
lines changed

src/primitives/pak.cpp

Lines changed: 57 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <primitives/pak.h>
66
#include <pubkey.h>
7+
#include <dynafed.h>
78

89
// ELEMENTS
910

@@ -33,73 +34,9 @@ class CSecp256k1Init {
3334
static CSecp256k1Init instance_of_csecp256k1;
3435
}
3536

36-
CScript CPAKList::Magic()
37-
{
38-
CScript scriptPubKey;
39-
scriptPubKey.resize(6);
40-
scriptPubKey[0] = OP_RETURN;
41-
scriptPubKey[1] = 0x04;
42-
scriptPubKey[2] = 0xab;
43-
scriptPubKey[3] = 0x22;
44-
scriptPubKey[4] = 0xaa;
45-
scriptPubKey[5] = 0xee;
46-
return scriptPubKey;
47-
}
48-
49-
std::vector<CScript> CPAKList::GenerateCoinbasePAKCommitments() const
50-
{
51-
std::vector<CScript> commitments;
52-
CScript scriptPubKey = CPAKList::Magic();
53-
54-
for (unsigned int i = 0; i < m_offline_keys.size(); i++) {
55-
CScript scriptCommitment(scriptPubKey);
56-
unsigned char pubkey[33];
57-
size_t outputlen = 33;
58-
secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_offline_keys[i], SECP256K1_EC_COMPRESSED);
59-
assert(outputlen == 33);
60-
scriptCommitment << std::vector<unsigned char>(pubkey, pubkey+outputlen);
61-
secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_online_keys[i], SECP256K1_EC_COMPRESSED);
62-
assert(outputlen == 33);
63-
scriptCommitment << std::vector<unsigned char>(pubkey, pubkey+outputlen);
64-
commitments.push_back(scriptCommitment);
65-
}
66-
67-
return commitments;
68-
}
69-
70-
std::vector<CScript> CPAKList::GenerateCoinbasePAKReject() const
71-
{
72-
CScript scriptPubKey = CPAKList::Magic();
73-
74-
std::vector<unsigned char> reject;
75-
reject.push_back('R');
76-
reject.push_back('E');
77-
reject.push_back('J');
78-
reject.push_back('E');
79-
reject.push_back('C');
80-
reject.push_back('T');
81-
82-
scriptPubKey << reject;
83-
84-
std::vector<CScript> commitment;
85-
commitment.push_back(scriptPubKey);
86-
return commitment;
87-
}
88-
89-
void CPAKList::CreateCommitments(std::vector<CScript> &commitments) const
90-
{
91-
if(reject) {
92-
commitments = GenerateCoinbasePAKReject();
93-
} else {
94-
commitments = GenerateCoinbasePAKCommitments();
95-
}
96-
}
97-
9837
bool CPAKList::operator==(const CPAKList &other) const
9938
{
100-
if (this->reject != other.reject) {
101-
return false;
102-
} else if (this->m_offline_keys.size() != other.m_offline_keys.size()) {
39+
if (this->m_offline_keys.size() != other.m_offline_keys.size()) {
10340
return false;
10441
} else {
10542
for (unsigned int i = 0; i < this->m_offline_keys.size(); i++) {
@@ -112,7 +49,7 @@ bool CPAKList::operator==(const CPAKList &other) const
11249
return true;
11350
}
11451

115-
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)
52+
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)
11653
{
11754
if(offline_keys_bytes.size() != online_keys_bytes.size()
11855
|| offline_keys_bytes.size() > SECP256K1_WHITELIST_MAX_N_KEYS) {
@@ -134,11 +71,11 @@ bool CPAKList::FromBytes(CPAKList &paklist, std::vector<std::vector<unsigned cha
13471
online_keys.push_back(pubkey2);
13572
}
13673

137-
paklist = CPAKList(offline_keys, online_keys, is_reject);
74+
paklist = CPAKList(offline_keys, online_keys);
13875
return true;
13976
}
14077

141-
void CPAKList::ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys, bool &is_reject) const
78+
void CPAKList::ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys) const
14279
{
14380
offline_keys.resize(0);
14481
online_keys.resize(0);
@@ -151,7 +88,6 @@ void CPAKList::ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, s
15188
secp256k1_ec_pubkey_serialize(secp256k1_ctx_pak, pubkey, &outputlen, &m_online_keys[i], SECP256K1_EC_COMPRESSED);
15289
online_keys.push_back(std::vector<unsigned char>(pubkey, pubkey+outputlen));
15390
}
154-
is_reject = reject;
15591
}
15692

15793
// Proof follows the OP_RETURN <genesis_block_hash> <destination_scriptpubkey>
@@ -243,3 +179,55 @@ bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash)
243179

244180
return true;
245181
}
182+
183+
CPAKList CreatePAKListFromExtensionSpace(const std::vector<std::vector<unsigned char>>& extension_space)
184+
{
185+
std::vector<std::vector<unsigned char>> offline_keys;
186+
std::vector<std::vector<unsigned char>> online_keys;
187+
for (const auto& entry : extension_space) {
188+
// As soon as we find something that is possibly not 2 serialized pubkeys
189+
// we stop looking. CPAKList::FromBytes does pubkey validation itself.
190+
if (entry.size() != 66) {
191+
break;
192+
}
193+
offline_keys.emplace_back(entry.begin(), entry.begin()+33);
194+
online_keys.emplace_back(entry.begin()+33, entry.end());
195+
// Allow additional data, just ignore
196+
if (offline_keys.size() == SECP256K1_WHITELIST_MAX_N_KEYS) {
197+
break;
198+
}
199+
}
200+
CPAKList paklist;
201+
if (!CPAKList::FromBytes(paklist, offline_keys, online_keys)) {
202+
return CPAKList();
203+
}
204+
return paklist;
205+
}
206+
207+
CPAKList GetActivePAKList(const CBlockIndex* pblockindex, const Consensus::Params& params)
208+
{
209+
assert(pblockindex);
210+
211+
return CreatePAKListFromExtensionSpace(ComputeNextBlockFullCurrentParameters(pblockindex, params).m_extension_space);
212+
}
213+
214+
bool IsPAKValidOutput(const CTxOut& txout, const CPAKList& paklist)
215+
{
216+
const CChainParams& params = Params();
217+
if (txout.scriptPubKey.IsPegoutScript(params.ParentGenesisBlockHash()) &&
218+
txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == params.GetConsensus().pegged_asset &&
219+
(!ScriptHasValidPAKProof(txout.scriptPubKey, params.ParentGenesisBlockHash(), paklist))) {
220+
return false;
221+
}
222+
return true;
223+
}
224+
225+
bool IsPAKValidTx(const CTransaction& tx, const CPAKList& paklist)
226+
{
227+
for (const auto& txout : tx.vout) {
228+
if (!IsPAKValidOutput(txout, paklist)) {
229+
return false;
230+
}
231+
}
232+
return true;
233+
}

src/primitives/pak.h

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,22 @@
88
#include <script/script.h>
99
#include <secp256k1/include/secp256k1_whitelist.h>
1010
#include <boost/optional.hpp>
11+
#include <chain.h>
1112

1213
class CPAKList
1314
{
1415
private:
1516
std::vector<secp256k1_pubkey> m_offline_keys;
1617
std::vector<secp256k1_pubkey> m_online_keys;
17-
bool reject;
18-
19-
std::vector<CScript> GenerateCoinbasePAKCommitments() const;
20-
std::vector<CScript> GenerateCoinbasePAKReject() const;
2118

2219
public:
23-
CPAKList()
24-
{
25-
reject = true;
26-
}
20+
CPAKList() {}
2721
/**
2822
* Creates a new CPAKList. Requires that the number of offline keys is the same as the number of online keys
2923
* and that this number is not larger than SECP256K1_WHITELIST_MAX_N_KEYS.
3024
*/
31-
CPAKList(std::vector<secp256k1_pubkey> offline_keys, std::vector<secp256k1_pubkey> online_keys, bool reject) :
32-
m_offline_keys(offline_keys), m_online_keys(online_keys), reject(reject) {
25+
CPAKList(std::vector<secp256k1_pubkey> offline_keys, std::vector<secp256k1_pubkey> online_keys) :
26+
m_offline_keys(offline_keys), m_online_keys(online_keys) {
3327
assert(m_offline_keys.size() == m_online_keys.size());
3428
assert(m_offline_keys.size() <= SECP256K1_WHITELIST_MAX_N_KEYS);
3529
}
@@ -41,11 +35,7 @@ class CPAKList
4135
}
4236
bool IsReject() const
4337
{
44-
return reject;
45-
}
46-
bool IsEmpty() const
47-
{
48-
return !reject && this->size() == 0;
38+
return size()==0;
4939
}
5040
std::vector<secp256k1_pubkey> OnlineKeys() const
5141
{
@@ -60,24 +50,21 @@ class CPAKList
6050
return m_offline_keys.size();
6151
}
6252

63-
static CScript Magic();
64-
/** Produce a list of scripts to add to the coinbase to signal changes in PAK list or rejection of any pak proofs to nodes */
65-
void CreateCommitments(std::vector<CScript> &commitments) const;
66-
67-
static bool FromBytes(CPAKList &paklist, std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys, bool is_reject);
68-
void ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys, bool &is_reject) const;
53+
static bool FromBytes(CPAKList &paklist, const std::vector<std::vector<unsigned char> >& offline_keys, const std::vector<std::vector<unsigned char> >& online_keys);
54+
void ToBytes(std::vector<std::vector<unsigned char> >& offline_keys, std::vector<std::vector<unsigned char> >& online_keys) const;
6955
};
7056

7157
/**
7258
** Returns true if the script includes valid pegout proof
73-
** given the PAK list loaded. Two pushes after regular pegout script:
59+
** given the PAK list. Two pushes after regular pegout script:
7460
** <full_pubkey> <proof>
7561
**/
7662
bool ScriptHasValidPAKProof(const CScript& script, const uint256& genesis_hash);
7763

78-
// ELEMENTS:
79-
extern boost::optional<CPAKList> g_paklist_config;
80-
extern CPAKList g_paklist_blockchain;
81-
///////////
64+
CPAKList GetActivePAKList(const CBlockIndex* pblockindex, const Consensus::Params& params);
65+
66+
bool IsPAKValidOutput(const CTxOut& txout, const CPAKList& paklist);
67+
68+
bool IsPAKValidTx(const CTransaction& tx, const CPAKList& paklist);
8269

8370
#endif // BITCOIN_PRIMITIVES_PAK_H

src/rpc/misc.cpp

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -663,8 +663,7 @@ UniValue FormatPAKList(CPAKList &paklist) {
663663
UniValue paklist_value(UniValue::VOBJ);
664664
std::vector<std::vector<unsigned char> > offline_keys;
665665
std::vector<std::vector<unsigned char> > online_keys;
666-
bool is_reject;
667-
paklist.ToBytes(offline_keys, online_keys, is_reject);
666+
paklist.ToBytes(offline_keys, online_keys);
668667

669668
UniValue retOnline(UniValue::VARR);
670669
UniValue retOffline(UniValue::VARR);
@@ -675,7 +674,7 @@ UniValue FormatPAKList(CPAKList &paklist) {
675674
}
676675
paklist_value.pushKV("online", retOnline);
677676
paklist_value.pushKV("offline", retOffline);
678-
paklist_value.pushKV("reject", is_reject);
677+
paklist_value.pushKV("reject", retOffline.empty());
679678
return paklist_value;
680679
}
681680

@@ -684,26 +683,21 @@ UniValue getpakinfo(const JSONRPCRequest& request)
684683
if (request.fHelp || request.params.size() != 0)
685684
throw std::runtime_error(
686685
RPCHelpMan{"getpakinfo",
687-
"\nReturns relevant pegout authorization key (PAK) information about this node, both from command line arguments and blockchain data.\n",
686+
"\nReturns relevant pegout authorization key (PAK) information about this node, both from blockchain data.\n",
688687
{},
689688
RPCResult{
690689
"{\n"
691-
"\"config_paklist\" (array) The PAK list loaded from beta.conf at startup\n"
692-
"\"block_paklist\" (array) The PAK list loaded from latest block commitment\n"
690+
"\"block_paklist\" (array) The PAK list loaded from latest epoch\n"
693691
"}\n"
694692
},
695693
RPCExamples{""},
696694
}.ToString());
697695

698696
LOCK(cs_main);
699697

700-
UniValue paklist_value(UniValue::VOBJ);
701-
if (g_paklist_config) {
702-
paklist_value = FormatPAKList(*g_paklist_config);
703-
}
704698
UniValue ret(UniValue::VOBJ);
705-
ret.pushKV("config_paklist", paklist_value);
706-
ret.pushKV("block_paklist", FormatPAKList(g_paklist_blockchain));
699+
CPAKList paklist = GetActivePAKList(chainActive.Tip(), Params().GetConsensus());
700+
ret.pushKV("block_paklist", FormatPAKList(paklist));
707701

708702
return ret;
709703
}

src/validation.cpp

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2622,17 +2622,6 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
26222622
if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
26232623
return false;
26242624

2625-
// Get PAK commitment from coinbase, if it exists
2626-
boost::optional<CPAKList> paklist = GetPAKKeysFromCommitment(*blockConnecting.vtx[0]);
2627-
if (paklist) {
2628-
std::vector<std::vector<unsigned char> > offline_keys;
2629-
std::vector<std::vector<unsigned char> > online_keys;
2630-
bool is_reject;
2631-
paklist->ToBytes(offline_keys, online_keys, is_reject);
2632-
pblocktree->WritePAKList(offline_keys, online_keys, is_reject);
2633-
g_paklist_blockchain = *paklist;
2634-
}
2635-
26362625
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
26372626
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
26382627
// Remove conflicting transactions from the mempool.;
@@ -3442,72 +3431,6 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
34423431
}
34433432

34443433
// ELEMENTS
3445-
boost::optional<CPAKList> GetPAKKeysFromCommitment(const CTransaction& coinbase)
3446-
{
3447-
std::vector<std::vector<unsigned char> > offline_keys;
3448-
std::vector<std::vector<unsigned char> > online_keys;
3449-
bool is_reject = false;
3450-
for (unsigned int i = 0; i < coinbase.vout.size(); i++) {
3451-
const CScript& scriptPubKey = coinbase.vout[i].scriptPubKey;
3452-
3453-
// OP + push + 4 bytes + push + 33 bytes + push + 33 bytes
3454-
// or
3455-
// OP + push + 4 bytes + push + 6 bytes (REJECT)
3456-
3457-
CScript::const_iterator pc = scriptPubKey.begin();
3458-
std::vector<unsigned char> data;
3459-
opcodetype opcode;
3460-
3461-
if (!scriptPubKey.GetOp(pc, opcode, data) || opcode != OP_RETURN){
3462-
continue;
3463-
}
3464-
3465-
if (!scriptPubKey.GetOp(pc, opcode, data) || data.size() != 4 ||
3466-
data[0] != 0xab || data[1] != 0x22 || data[2] != 0xaa || data[3] != 0xee) {
3467-
continue;
3468-
}
3469-
3470-
if (!scriptPubKey.GetOp(pc, opcode, data)){
3471-
continue;
3472-
}
3473-
3474-
// Check for pak list reject signal
3475-
// Returns an empty list regardless of other commitments
3476-
if (data.size() == 6 && data[0] == 'R' && data[1] == 'E' && data[2] == 'J' && data[3] == 'E' && data[4] == 'C' && data[5] == 'T') {
3477-
is_reject = true;
3478-
continue;
3479-
}
3480-
3481-
// Check for offline key
3482-
if (data.size() != 33) {
3483-
continue;
3484-
}
3485-
3486-
// Check for online key
3487-
std::vector<unsigned char> data_online;
3488-
if (!scriptPubKey.GetOp(pc, opcode, data_online) || data_online.size() != 33) {
3489-
continue;
3490-
}
3491-
3492-
offline_keys.push_back(data);
3493-
online_keys.push_back(data_online);
3494-
}
3495-
if (is_reject) {
3496-
offline_keys.clear();
3497-
online_keys.clear();
3498-
}
3499-
if (!is_reject && offline_keys.size() == 0) {
3500-
return boost::none;
3501-
}
3502-
CPAKList paklist;
3503-
if (!CPAKList::FromBytes(paklist, offline_keys, online_keys, is_reject)) {
3504-
return boost::none;
3505-
} else {
3506-
return paklist;
3507-
}
3508-
}
3509-
3510-
35113434

35123435

35133436
/** Context-dependent validity checks.

0 commit comments

Comments
 (0)