Skip to content

Commit f60e068

Browse files
committed
Merge #773: HF: Dyanfed pruned header now has extra root
fd5ec4b HF: Simplify legacy->dynafed transition max sig size (Gregory Sanders) 7b15b48 HF: Publish full dynafed block no matter epoch age (Gregory Sanders) 1d78473 HF: Dyanfed pruned header now has extra root (Gregory Sanders) Pull request description: andytoshi's motivating text: "Motivation here is that the functionary wants to be able to compute the root of the parameters and use this to look up the params in the set of supported params (which includes a bunch of extra auxiliary data, in particular the signblock witness script). If the root is computed differently for compact-encoded params than for full-encoded ones, and indeed does not even commit to the extra data, this lookup is impossible without blockchain context, which is not obvious how to obtain for an unsigned block." Tree-SHA512: 737b9da99bc4abd28cc63c3a441098e0a6ea0cb2748082f845b24d50fe3a9cd1d1253d622cdca4df91e27c04a4912babc8a54409e7e0f2af14f65a3414a072ad
2 parents 08023b0 + fd5ec4b commit f60e068

File tree

9 files changed

+77
-38
lines changed

9 files changed

+77
-38
lines changed

src/chainparams.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,8 @@ class CLiquidV1Params : public CChainParams {
720720
// Block signing encumberance script, default of 51 aka OP_TRUE
721721
std::vector<unsigned char> sign_bytes = ParseHex("5b21026a2a106ec32c8a1e8052e5d02a7b0a150423dbd9b116fc48d46630ff6e6a05b92102791646a8b49c2740352b4495c118d876347bf47d0551c01c4332fdc2df526f1a2102888bda53a424466b0451627df22090143bbf7c060e9eacb1e38426f6b07f2ae12102aee8967150dee220f613de3b239320355a498808084a93eaf39a34dcd62024852102d46e9259d0a0bb2bcbc461a3e68f34adca27b8d08fbe985853992b4b104e27412102e9944e35e5750ab621e098145b8e6cf373c273b7c04747d1aa020be0af40ccd62102f9a9d4b10a6d6c56d8c955c547330c589bb45e774551d46d415e51cd9ad5116321033b421566c124dfde4db9defe4084b7aa4e7f36744758d92806b8f72c2e943309210353dcc6b4cf6ad28aceb7f7b2db92a4bf07ac42d357adf756f3eca790664314b621037f55980af0455e4fb55aad9b85a55068bb6dc4740ea87276dc693f4598db45fa210384001daa88dabd23db878dbb1ce5b4c2a5fa72c3113e3514bf602325d0c37b8e21039056d089f2fe72dbc0a14780b4635b0dc8a1b40b7a59106325dd1bc45cc70493210397ab8ea7b0bf85bc7fc56bb27bf85e75502e94e76a6781c409f3f2ec3d1122192103b00e3b5b77884bf3cae204c4b4eac003601da75f96982ffcb3dcb29c5ee419b92103c1f3c0874cfe34b8131af34699589aacec4093399739ae352e8a46f80a6f68375fae");
722722
consensus.signblockscript = CScript(sign_bytes.begin(), sign_bytes.end());
723-
consensus.max_block_signature_size = 12*74; // 11 signatures plus wiggle room
723+
// 11 signatures, 15 pubkeys, plus wiggle room
724+
consensus.max_block_signature_size = 12*74+16*33;
724725
g_signed_blocks = true;
725726

726727
g_con_blockheightinheader = true;

src/dynafed.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ DynaFedParamEntry ComputeNextBlockFullCurrentParameters(const CBlockIndex* pinde
7373
CScript sh_wsh_fedpeg_program = CScript() << OP_HASH160 << ToByteVector(fedpeg_p2sh) << OP_EQUAL;
7474

7575
// Put them in winning proposal
76-
winning_proposal = DynaFedParamEntry(p2wsh_signblock_script, consensus.max_block_signature_size+consensus.signblockscript.size(), sh_wsh_fedpeg_program, consensus.fedpegScript, consensus.first_extension_space);
76+
winning_proposal = DynaFedParamEntry(p2wsh_signblock_script, consensus.max_block_signature_size, sh_wsh_fedpeg_program, consensus.fedpegScript, consensus.first_extension_space);
7777
} else {
7878
winning_proposal = p_epoch_start->dynafed_params.m_current;
7979
}
@@ -91,12 +91,13 @@ DynaFedParamEntry ComputeNextBlockCurrentParameters(const CBlockIndex* pindexPre
9191
const uint32_t epoch_length = consensus.dynamic_epoch_length;
9292
uint32_t epoch_age = next_height % epoch_length;
9393

94-
// Return appropriate format based on epoch age
95-
if (epoch_age > 0) {
96-
// TODO implement "prune" function to remove fields in place and change serialize type
97-
return DynaFedParamEntry(entry.m_signblockscript, entry.m_signblock_witness_limit);
98-
} else {
94+
// Return appropriate format based on epoch age or if we *just* activated
95+
// dynafed via BIP9
96+
if (epoch_age == 0 || pindexPrev->dynafed_params.IsNull()) {
9997
return entry;
98+
} else {
99+
return DynaFedParamEntry(entry.m_signblockscript, entry.m_signblock_witness_limit, entry.CalculateExtraRoot());
100+
100101
}
101102
}
102103

src/miner.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
151151
fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());
152152

153153
if (IsDynaFedEnabled(pindexPrev, chainparams.GetConsensus())) {
154-
DynaFedParamEntry current_params = ComputeNextBlockCurrentParameters(chainActive.Tip(), chainparams.GetConsensus());
155-
DynaFedParams block_params(current_params, proposed_entry ? *proposed_entry : DynaFedParamEntry());
154+
const DynaFedParamEntry current_params = ComputeNextBlockCurrentParameters(chainActive.Tip(), chainparams.GetConsensus());
155+
const DynaFedParams block_params(current_params, proposed_entry ? *proposed_entry : DynaFedParamEntry());
156156
pblock->m_dynafed_params = block_params;
157157
nBlockWeight += ::GetSerializeSize(block_params, PROTOCOL_VERSION)*WITNESS_SCALE_FACTOR;
158158
nBlockWeight += current_params.m_signblock_witness_limit; // Note witness discount

src/primitives/block.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,39 @@ std::string CBlock::ToString() const
4343

4444
uint256 DynaFedParamEntry::CalculateRoot() const
4545
{
46-
if (IsNull()) {
46+
if (m_serialize_type == 0) {
4747
return uint256();
4848
}
4949

50+
std::vector<uint256> compact_leaves;
51+
compact_leaves.push_back(SerializeHash(m_signblockscript, SER_GETHASH, 0));
52+
compact_leaves.push_back(SerializeHash(m_signblock_witness_limit, SER_GETHASH, 0));
53+
uint256 compact_root(ComputeFastMerkleRoot(compact_leaves));
54+
55+
uint256 extra_root;
56+
if (m_serialize_type ==1 ) {
57+
// It's pruned, take the stored value
58+
extra_root = m_elided_root;
59+
} else if (m_serialize_type == 2) {
60+
// It's unpruned, compute the node value
61+
extra_root = CalculateExtraRoot();
62+
}
63+
5064
std::vector<uint256> leaves;
51-
leaves.push_back(SerializeHash(m_signblockscript, SER_GETHASH, 0));
52-
leaves.push_back(SerializeHash(m_signblock_witness_limit, SER_GETHASH, 0));
53-
leaves.push_back(SerializeHash(m_fedpeg_program, SER_GETHASH, 0));
54-
leaves.push_back(SerializeHash(m_fedpegscript, SER_GETHASH, 0));
55-
leaves.push_back(SerializeHash(m_extension_space, SER_GETHASH, 0));
65+
leaves.push_back(compact_root);
66+
leaves.push_back(extra_root);
5667
return ComputeFastMerkleRoot(leaves);
5768
}
5869

70+
uint256 DynaFedParamEntry::CalculateExtraRoot() const
71+
{
72+
std::vector<uint256> extra_leaves;
73+
extra_leaves.push_back(SerializeHash(m_fedpeg_program, SER_GETHASH, 0));
74+
extra_leaves.push_back(SerializeHash(m_fedpegscript, SER_GETHASH, 0));
75+
extra_leaves.push_back(SerializeHash(m_extension_space, SER_GETHASH, 0));
76+
return ComputeFastMerkleRoot(extra_leaves);
77+
}
78+
5979
uint256 DynaFedParams::CalculateRoot() const
6080
{
6181
if (IsNull()) {

src/primitives/block.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,25 @@ class CProof
5858
class DynaFedParamEntry
5959
{
6060
public:
61-
unsigned char m_serialize_type; // Determines how it is serialized, defaults to null
61+
// Determines how these entries are serialized and stored
62+
// 0 -> Null. Only used for proposed parameter "null votes"
63+
// 1 -> Pruned. Doesn't have non-signblockscript data. That elided data
64+
// is committed to in m_elided_root, and validated against chainstate.
65+
// 2 -> Full. Typically only consensus-legal at epoch start.
66+
unsigned char m_serialize_type;
67+
6268
CScript m_signblockscript;
6369
uint32_t m_signblock_witness_limit; // Max block signature witness serialized size
6470
CScript m_fedpeg_program; // The "scriptPubKey" of the fedpegscript
6571
CScript m_fedpegscript; // The witnessScript for witness v0 or undefined otherwise.
6672
// No consensus meaning to the particular bytes, currently we interpret as PAK keys, details in pak.h
6773
std::vector<std::vector<unsigned char>> m_extension_space;
74+
uint256 m_elided_root; // non-zero only when m_serialize_type == 1
6875

6976
// Each constructor sets its own serialization type implicitly based on which
7077
// arguments are given
7178
DynaFedParamEntry() { m_signblock_witness_limit = 0; m_serialize_type = 0; };
72-
DynaFedParamEntry(const CScript& signblockscript_in, const uint32_t sbs_wit_limit_in) : m_signblockscript(signblockscript_in), m_signblock_witness_limit(sbs_wit_limit_in) { m_serialize_type = 1; };
79+
DynaFedParamEntry(const CScript& signblockscript_in, const uint32_t sbs_wit_limit_in, const uint256 elided_root_in) : m_signblockscript(signblockscript_in), m_signblock_witness_limit(sbs_wit_limit_in), m_elided_root(elided_root_in) { m_serialize_type = 1; };
7380
DynaFedParamEntry(const CScript& signblockscript_in, const uint32_t sbs_wit_limit_in, const CScript& fedpeg_program_in, const CScript& fedpegscript_in, const std::vector<std::vector<unsigned char>> extension_space_in) : m_signblockscript(signblockscript_in), m_signblock_witness_limit(sbs_wit_limit_in), m_fedpeg_program(fedpeg_program_in), m_fedpegscript(fedpegscript_in), m_extension_space(extension_space_in) { m_serialize_type = 2; };
7481

7582
ADD_SERIALIZE_METHODS;
@@ -84,6 +91,7 @@ class DynaFedParamEntry
8491
case 1:
8592
READWRITE(m_signblockscript);
8693
READWRITE(m_signblock_witness_limit);
94+
READWRITE(m_elided_root);
8795
break;
8896
case 2:
8997
READWRITE(m_signblockscript);
@@ -98,6 +106,8 @@ class DynaFedParamEntry
98106
}
99107

100108
uint256 CalculateRoot() const;
109+
// Calculates root for the non-blocksigning merkle fields
110+
uint256 CalculateExtraRoot() const;
101111

102112
bool IsNull() const
103113
{

src/test/dynafed_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,23 @@ BOOST_AUTO_TEST_CASE(dynafed_params_root)
2121
CScript fp_script(opcodetype(4));
2222
std::vector<std::vector<unsigned char>> ext{ {5, 6}, {7} };
2323

24-
DynaFedParamEntry compact_entry = DynaFedParamEntry(signblockscript, signblock_wl);
24+
DynaFedParamEntry compact_entry = DynaFedParamEntry(signblockscript, signblock_wl, uint256());
2525
BOOST_CHECK_EQUAL(
2626
compact_entry.CalculateRoot().GetHex(),
27-
"dff5f3793abc06a6d75e80fe3cfd47406f732fa4ec9305960ae2a229222a1ad5"
27+
"f98f149fd11da6fbe26d0ee53cadd28372fa9eed2cb7080f41da7ca311531777"
2828
);
2929

3030
DynaFedParamEntry full_entry =
3131
DynaFedParamEntry(signblockscript, signblock_wl, fp_program, fp_script, ext);
3232
BOOST_CHECK_EQUAL(
3333
full_entry.CalculateRoot().GetHex(),
34-
"175be2087ba7cc0e33348bef493bd3e34f31f64bf9226e5881ab310dafa432ff"
34+
"8eb1b83cce69a3d8b0bfb7fbe77ae8f1d24b57a9cae047b8c0aba084ad878249"
3535
);
3636

3737
DynaFedParams params = DynaFedParams(compact_entry, full_entry);
3838
BOOST_CHECK_EQUAL(
3939
params.CalculateRoot().GetHex(),
40-
"e56cf79487952dfa85fe6a85829600adc19714ba6ab1157fdff02b25ae60cee2"
40+
"113160f76dc17fe367a2def79aefe06feeea9c795310c9e88aeedc23e145982e"
4141
);
4242
}
4343

test/functional/feature_blocksign.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def set_test_params(self):
8484
self.witnessScript = signblockscript # post-dynafed this becomes witnessScript
8585
self.extra_args = [[
8686
"-signblockscript={}".format(signblockscript),
87-
"-con_max_block_sig_size={}".format(self.required_signers*74),
87+
"-con_max_block_sig_size={}".format(self.required_signers*74+self.num_nodes*33),
8888
"-anyonecanspendaremine=1",
8989
"-con_dyna_deploy_start=0",
9090
]] * self.num_nodes
@@ -218,14 +218,16 @@ def run_test(self):
218218

219219
# Next let's activate dynafed
220220
blocks_til_dynafed = 431 - self.nodes[0].getblockcount()
221+
self.log.info("Activating dynafed")
221222
self.mine_blocks(blocks_til_dynafed, False)
222223
self.check_height(111+blocks_til_dynafed)
223224

224225
assert_equal(self.nodes[0].getblockchaininfo()['bip9_softforks']['dynafed']['status'], "active")
225226

226-
self.log.info("Mine some dynamic federation blocks without and with txns")
227-
self.mine_blocks(50, False)
228-
self.mine_blocks(50, True)
227+
self.log.info("Mine some dynamic federation blocks without txns")
228+
self.mine_blocks(10, False)
229+
self.log.info("Mine some dynamic federation blocks with txns")
230+
self.mine_blocks(10, True)
229231

230232
if __name__ == '__main__':
231233
BlockSignTest().main()

test/functional/feature_dynafed.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def go_to_epoch_end(node):
3434
blocks_to_mine = epoch_info["epoch_length"] - epoch_info["epoch_age"] - 1
3535
node.generatetoaddress(blocks_to_mine, node.getnewaddress())
3636

37-
def validate_no_vote_op_true(node, block):
37+
def validate_no_vote_op_true(node, block, first_dynafed_active_block):
3838

3939
block_info = node.getblock(block)
4040
dynamic_parameters = block_info["dynamic_parameters"]
@@ -44,14 +44,13 @@ def validate_no_vote_op_true(node, block):
4444
# signblockscript is now the P2WSH-ification of OP_TRUE
4545
WSH_OP_TRUE = node.decodescript("51")["segwit"]["hex"]
4646
assert_equal(dynamic_parameters["current"]["signblockscript"], WSH_OP_TRUE)
47-
if block_height % 10 == 0:
47+
if block_height % 10 == 0 or first_dynafed_active_block:
4848
assert_equal(dynamic_parameters["current"]["fedpegscript"], "51")
4949
assert_equal(dynamic_parameters["current"]["extension_space"], initial_extension)
5050
else:
5151
assert_equal(dynamic_parameters["current"]["fedpegscript"], "")
5252
assert_equal(dynamic_parameters["current"]["extension_space"], [])
53-
# TODO workshop this bump, or commit to new value in chainparams instead
54-
assert_equal(dynamic_parameters["current"]["max_block_witness"], 75)
53+
assert_equal(dynamic_parameters["current"]["max_block_witness"], 74)
5554
# nothing was proposed, null fields make impossible to be valid blockheader
5655
# due to script rules requiring bool true on stack
5756
assert_equal(dynamic_parameters["proposed"]["signblockscript"], "")
@@ -118,8 +117,9 @@ def test_dynafed_activation(self):
118117
# Next block is first dynamic federation block
119118
block = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0]
120119
self.sync_all()
120+
# We publish full block on BIP9 transition
121121
for i in range(self.num_nodes):
122-
validate_no_vote_op_true(self.nodes[i], block)
122+
validate_no_vote_op_true(self.nodes[i], block, True)
123123

124124
def test_illegal_proposals(self):
125125

@@ -148,14 +148,14 @@ def test_no_vote(self):
148148

149149
for i in range(self.num_nodes):
150150
for block in blocks:
151-
validate_no_vote_op_true(self.nodes[i], block)
151+
validate_no_vote_op_true(self.nodes[i], block, False)
152152

153153
# Now transition using vanilla getnewblockhex, nothing changed
154154
block = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0]
155155
self.sync_all()
156156

157157
for i in range(self.num_nodes):
158-
validate_no_vote_op_true(self.nodes[i], block)
158+
validate_no_vote_op_true(self.nodes[i], block, False)
159159

160160
def test_under_vote(self):
161161
self.log.info("Testing failed voting epoch...")
@@ -176,7 +176,7 @@ def test_under_vote(self):
176176
self.sync_all()
177177

178178
for i in range(self.num_nodes):
179-
validate_no_vote_op_true(self.nodes[i], block)
179+
validate_no_vote_op_true(self.nodes[i], block, False)
180180

181181
def test_four_fifth_vote(self):
182182
self.log.info("Testing just-successful transition epoch...")
@@ -192,7 +192,7 @@ def test_four_fifth_vote(self):
192192
chain_info = self.nodes[i].getblockchaininfo()
193193
fedpeg_info = self.nodes[i].getsidechaininfo()
194194
assert_equal(chain_info["current_signblock_hex"], WSH_OP_TRUE)
195-
assert_equal(chain_info["max_block_witness"], 75)
195+
assert_equal(chain_info["max_block_witness"], 74)
196196
assert_equal(chain_info["extension_space"], initial_extension)
197197
assert_equal(fedpeg_info["current_fedpegscripts"], ["51", "51"])
198198

@@ -210,7 +210,7 @@ def test_four_fifth_vote(self):
210210
chain_info = self.nodes[i].getblockchaininfo()
211211
fedpeg_info = self.nodes[i].getsidechaininfo()
212212
assert_equal(chain_info["current_signblock_hex"], WSH_OP_TRUE)
213-
assert_equal(chain_info["max_block_witness"], 75)
213+
assert_equal(chain_info["max_block_witness"], 74)
214214
assert_equal(chain_info["extension_space"], initial_extension)
215215
assert_equal(fedpeg_info["current_fedpegscripts"], ["51", "51"])
216216

test/functional/test_framework/messages.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -836,22 +836,24 @@ def __repr__(self):
836836
% (self.challenge, self.solution)
837837

838838
class DynaFedParamEntry:
839-
__slots__ = ("m_serialize_type", "m_signblockscript", "m_signblock_witness_limit", "m_fedpeg_program", "m_fedpegscript", "m_extension_space")
839+
__slots__ = ("m_serialize_type", "m_signblockscript", "m_signblock_witness_limit", "m_fedpeg_program", "m_fedpegscript", "m_extension_space", "m_elided_root")
840840

841841
# Constructor args will define serialization type:
842842
# null = 0
843843
# signblock-related fields = 1, required for m_current on non-epoch-starts
844844
# all fields = 2, required for epoch starts
845-
def __init__(self, m_signblockscript=b"", m_signblock_witness_limit=0, m_fedpeg_program=b"", m_fedpegscript=b"", m_extension_space=[]):
845+
def __init__(self, m_signblockscript=b"", m_signblock_witness_limit=0, m_fedpeg_program=b"", m_fedpegscript=b"", m_extension_space=[], m_elided_root=0):
846846
self.m_signblockscript = m_signblockscript
847847
self.m_signblock_witness_limit = m_signblock_witness_limit
848848
self.m_fedpeg_program = m_fedpeg_program
849849
self.m_fedpegscript = m_fedpegscript
850850
self.m_extension_space = m_extension_space
851851
if self.is_null():
852852
self.m_serialize_type = 0
853-
elif m_fedpegscript==b"" and m_extension_space == []:
853+
elif m_fedpegscript==b"" and m_fedpeg_program==b"" and m_extension_space == []:
854854
self.m_serialize_type = 1
855+
# We also set the "extra root" in this case
856+
self.m_elided_root = m_elided_root
855857
else:
856858
self.m_serialize_type = 2
857859

@@ -862,6 +864,7 @@ def set_null(self):
862864
self.m_fedpegscript = b""
863865
self.m_extension_space = []
864866
self.m_serialize_type = 0
867+
self.m_elided_root = 0
865868

866869
def is_null(self):
867870
return self.m_signblockscript == b"" and self.m_signblock_witness_limit == 0 and \
@@ -874,6 +877,7 @@ def serialize(self):
874877
if self.m_serialize_type == 1:
875878
r += ser_string(self.m_signblockscript)
876879
r += struct.pack("<I", self.m_signblock_witness_limit)
880+
r += ser_uint256(self.m_elided_root)
877881
elif self.m_serialize_type == 2:
878882
r += ser_string(self.m_signblockscript)
879883
r += struct.pack("<I", self.m_signblock_witness_limit)
@@ -889,6 +893,7 @@ def deserialize(self, f):
889893
if self.m_serialize_type == 1:
890894
self.m_signblockscript = deser_string(f)
891895
self.m_signblock_witness_limit = struct.unpack("<I", f.read(4))[0]
896+
self.m_elided_root = deser_uint256(f)
892897
elif self.m_serialize_type == 2:
893898
self.m_signblockscript = deser_string(f)
894899
self.m_signblock_witness_limit = struct.unpack("<I", f.read(4))[0]

0 commit comments

Comments
 (0)