Skip to content

Commit

Permalink
Move DGP related checks from Checkblock to ConnectBlock
Browse files Browse the repository at this point in the history
  • Loading branch information
Neil committed Aug 10, 2017
1 parent 1e32da1 commit c9188d9
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 5 deletions.
155 changes: 155 additions & 0 deletions qa/rpc-tests/qtum-dgp-block-size-sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#!/usr/bin/env python3

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.script import *
from test_framework.mininode import *
from test_framework.qtum import *
from test_framework.address import *
from test_framework.blocktools import *
import io

class DGPState:
def __init__(self, node, contract_address):
self.node = node
self.contract_address = contract_address
self.abiAddAddressProposal = "bf5f1e83" #addAddressProposal(address,uint256)
self.abiSetInitialAdmin = "6fb81cbb" # setInitialAdmin()

def send_set_initial_admin(self, sender):
self.node.sendtoaddress(sender, 1)
self.node.sendtocontract(self.contract_address, self.abiSetInitialAdmin, 0, 1000000, 0.00000001, sender)

def send_add_address_proposal(self, proposal_address, type1, sender):
self.node.sendtoaddress(sender, 1)
self.node.sendtocontract(self.contract_address, self.abiAddAddressProposal + proposal_address.zfill(64) + hex(type1)[2:].zfill(64), 0, 1000000, 0.00000001, sender)


"""
Note, these tests do not test the functionality of the DGP template contract itself, for tests for the DGP template, see qtum-dgp.py
"""
class QtumDGPBlockSizeSyncTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 8

def setup_network(self):
# We only start 2 nodes, the last nodes are saved to check that it correctly syncs later on
self.nodes = start_nodes(2, self.options.tmpdir)
self.node = self.nodes[0]
self.is_network_split = False

def create_block_of_approx_max_size(self, size_in_bytes):
tip = self.node.getblock(self.node.getbestblockhash())
block = create_block(int(self.node.getbestblockhash(), 16), create_coinbase(self.node.getblockcount()+1), tip['time'])
block.hashUTXORoot = int(tip['hashUTXORoot'], 16)
block.hashStateRoot = int(tip['hashStateRoot'], 16)

unspents = self.node.listunspent()
while len(block.serialize()) < size_in_bytes:
unspent = unspents.pop(0)
tx = CTransaction()
tx.vin = [CTxIn(COutPoint(int(unspent['txid'], 16), unspent['vout']), nSequence=0)]
for i in range(50):
tx.vout.append(CTxOut(int(unspent['amount']*COIN/100 - 11000), scriptPubKey=CScript([OP_TRUE]*10000)))
tx_hex = self.node.signrawtransaction(bytes_to_hex_str(tx.serialize()))['hex']
f = io.BytesIO(hex_str_to_bytes(tx_hex))
block.vtx.append(CTransaction())
block.vtx[-1].deserialize(f)

while len(block.serialize()) > size_in_bytes:
block.vtx[-1].vout.pop(-1)
if not block.vtx[-1].vout:
block.vtx.pop(-1)
tx_hex = self.node.signrawtransaction(bytes_to_hex_str(block.vtx[-1].serialize()))['hex']
f = io.BytesIO(hex_str_to_bytes(tx_hex))
block.vtx[-1] = CTransaction()
block.vtx[-1].deserialize(f)

block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
block.solve()
print("block size", len(block.serialize()))
return block

def create_proposal_contract(self, block_size=2000000):
"""
pragma solidity ^0.4.11;
contract blockSize {
uint32[1] _blockSize=[
8000000 //block size in bytes
];
function getBlockSize() constant returns(uint32[1] _size){
return _blockSize;
}
}
"""
# The contracts below only differ in the _blockSize variable
if block_size == 32000000:
contract_data = self.node.createcontract("60606040526020604051908101604052806301e8480063ffffffff16815250600090600161002e92919061003f565b50341561003a57600080fd5b610115565b8260016007016008900481019282156100d15791602002820160005b8382111561009f57835183826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261005b565b80156100cf5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261009f565b505b5090506100de91906100e2565b5090565b61011291905b8082111561010e57600081816101000a81549063ffffffff0219169055506001016100e8565b5090565b90565b610162806101246000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806392ac3c621461003e575b600080fd5b341561004957600080fd5b610051610090565b6040518082600160200280838360005b8381101561007d5780820151818401525b602081019050610061565b5050505090500191505060405180910390f35b610098610108565b60006001806020026040519081016040528092919082600180156100fd576020028201916000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116100c05790505b505050505090505b90565b6020604051908101604052806001905b600063ffffffff1681526020019060019003908161011857905050905600a165627a7a72305820322c4456cb00ecc4c7f2878fe22cc7ff6addbf199842e68a4b23e98d51446b080029", 10000000, 0.00000001)
elif block_size == 8000000:
contract_data = self.node.createcontract("6060604052602060405190810160405280627a120062ffffff16815250600090600161002c92919061003d565b50341561003857600080fd5b610112565b8260016007016008900481019282156100ce5791602002820160005b8382111561009c57835183826101000a81548163ffffffff021916908362ffffff1602179055509260200192600401602081600301049283019260010302610059565b80156100cc5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261009c565b505b5090506100db91906100df565b5090565b61010f91905b8082111561010b57600081816101000a81549063ffffffff0219169055506001016100e5565b5090565b90565b610162806101216000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806392ac3c621461003e575b600080fd5b341561004957600080fd5b610051610090565b6040518082600160200280838360005b8381101561007d5780820151818401525b602081019050610061565b5050505090500191505060405180910390f35b610098610108565b60006001806020026040519081016040528092919082600180156100fd576020028201916000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116100c05790505b505050505090505b90565b6020604051908101604052806001905b600063ffffffff1681526020019060019003908161011857905050905600a165627a7a723058209bab110523b5fdedfb12512d3aedc1ba1add53dff85edb77aeec48ebdc01c35c0029", 10000000, 0.00000001)
elif block_size == 4000000:
contract_data = self.node.createcontract("6060604052602060405190810160405280623d090062ffffff16815250600090600161002c92919061003d565b50341561003857600080fd5b610112565b8260016007016008900481019282156100ce5791602002820160005b8382111561009c57835183826101000a81548163ffffffff021916908362ffffff1602179055509260200192600401602081600301049283019260010302610059565b80156100cc5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261009c565b505b5090506100db91906100df565b5090565b61010f91905b8082111561010b57600081816101000a81549063ffffffff0219169055506001016100e5565b5090565b90565b610162806101216000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806392ac3c621461003e575b600080fd5b341561004957600080fd5b610051610090565b6040518082600160200280838360005b8381101561007d5780820151818401525b602081019050610061565b5050505090500191505060405180910390f35b610098610108565b60006001806020026040519081016040528092919082600180156100fd576020028201916000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116100c05790505b505050505090505b90565b6020604051908101604052806001905b600063ffffffff1681526020019060019003908161011857905050905600a165627a7a72305820c5f02b85c3d9d7b93140775449355f53a7cb98dcafc56f07cdb09e9f2dc240550029", 10000000, 0.00000001)
elif block_size == 2000000:
contract_data = self.node.createcontract("6060604052602060405190810160405280621e848062ffffff16815250600090600161002c92919061003d565b50341561003857600080fd5b610112565b8260016007016008900481019282156100ce5791602002820160005b8382111561009c57835183826101000a81548163ffffffff021916908362ffffff1602179055509260200192600401602081600301049283019260010302610059565b80156100cc5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261009c565b505b5090506100db91906100df565b5090565b61010f91905b8082111561010b57600081816101000a81549063ffffffff0219169055506001016100e5565b5090565b90565b610162806101216000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806392ac3c621461003e575b600080fd5b341561004957600080fd5b610051610090565b6040518082600160200280838360005b8381101561007d5780820151818401525b602081019050610061565b5050505090500191505060405180910390f35b610098610108565b60006001806020026040519081016040528092919082600180156100fd576020028201916000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116100c05790505b505050505090505b90565b6020604051908101604052806001905b600063ffffffff1681526020019060019003908161011857905050905600a165627a7a723058201f747ceade404003185ab16248ecd30e8c1a63a811e55d7961ce3a47ddd01b160029", 10000000, 0.00000001)
elif block_size == 1000000:
contract_data = self.node.createcontract("6060604052602060405190810160405280620f424062ffffff16815250600090600161002c92919061003d565b50341561003857600080fd5b610112565b8260016007016008900481019282156100ce5791602002820160005b8382111561009c57835183826101000a81548163ffffffff021916908362ffffff1602179055509260200192600401602081600301049283019260010302610059565b80156100cc5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261009c565b505b5090506100db91906100df565b5090565b61010f91905b8082111561010b57600081816101000a81549063ffffffff0219169055506001016100e5565b5090565b90565b610162806101216000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806392ac3c621461003e575b600080fd5b341561004957600080fd5b610051610090565b6040518082600160200280838360005b8381101561007d5780820151818401525b602081019050610061565b5050505090500191505060405180910390f35b610098610108565b60006001806020026040519081016040528092919082600180156100fd576020028201916000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116100c05790505b505050505090505b90565b6020604051908101604052806001905b600063ffffffff1681526020019060019003908161011857905050905600a165627a7a7230582034c00d84f338629f594676d9bc32d5b9d7b92f3b438e9cc82a3efd92805f14730029", 10000000, 0.00000001)

self.proposal_address = contract_data['address']

def assert_block_accepted(self, block, with_witness=True):
current_block_count = self.node.getblockcount()
assert_equal(self.node.submitblock(bytes_to_hex_str(block.serialize(with_witness))), None)
assert_equal(self.node.getblockcount(), current_block_count+1)
self.sync_all()
assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())

def assert_block_limits(self, max_accepted_block_size, possible_block_sizes):
accepted_block_sizes = possible_block_sizes[0:possible_block_sizes.index(max_accepted_block_size)+1]

for block_size in accepted_block_sizes:
block = self.create_block_of_approx_max_size(block_size)
self.assert_block_accepted(block)

self.sync_all()

# Make sure that both nodes now have the same tip
assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())

def run_test(self):
# Generate some blocks to make sure we have enough spendable outputs
self.node.generate(1000 + COINBASE_MATURITY)
self.BLOCK_SIZE_DGP = DGPState(self.node, "0000000000000000000000000000000000000081")
self.is_network_split = False
connect_nodes_bi(self.nodes, 0, 1)

# Start off by setting ourself as admin
admin_address = self.node.getnewaddress()

# Set ourself up as admin
self.BLOCK_SIZE_DGP.send_set_initial_admin(admin_address)
self.node.generate(1)

possible_block_sizes = [4000000, 2000000, 1000000, 32000000, 8000000]
ascending_block_sizes = sorted(possible_block_sizes)

for max_block_size in possible_block_sizes:
self.create_proposal_contract(max_block_size)
self.BLOCK_SIZE_DGP.send_add_address_proposal(self.proposal_address, 2, admin_address)
self.node.generate(2) # We need to generate 2 blocks now for it to activate
self.assert_block_limits(max_block_size, ascending_block_sizes)

# Bring the last nodes online and make sure that they sync with node 0 and 1 (A and B)
for i in range(2, 8):
self.nodes.append(start_node(i, self.options.tmpdir))
connect_nodes_bi(self.nodes, 0, i)
connect_nodes_bi(self.nodes, 1, i)
self.sync_all()

if __name__ == '__main__':
QtumDGPBlockSizeSyncTest().main()
19 changes: 14 additions & 5 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
uint64_t countCumulativeGasUsed = 0;
/////////////////////////////////////////////////

// Move this check from CheckBlock to ConnectBlock as it depends on DGP values
if (block.vtx.empty() || block.vtx.size() > dgpMaxBlockSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > dgpMaxBlockSize) // qtum
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");

// Move this check from ContextualCheckBlock to ConnectBlock as it depends on DGP values
if (GetBlockWeight(block) > dgpMaxBlockWeight) {
return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
}

// Check it again in case a previous version let a bad block in
if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck))
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
Expand Down Expand Up @@ -3810,8 +3819,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// checks that use witness data may be performed here.

// Size limits
if (block.vtx.empty() || block.vtx.size() > dgpMaxBlockSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > dgpMaxBlockSize) // qtum
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
// if (block.vtx.empty() || block.vtx.size() > dgpMaxBlockSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > dgpMaxBlockSize) // qtum
// return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");

// First transaction must be coinbase in case of PoW block, the rest must not be
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
Expand Down Expand Up @@ -4063,9 +4072,9 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// large by filling up the coinbase witness, which doesn't change
// the block hash, so we couldn't mark the block as permanently
// failed).
if (GetBlockWeight(block) > dgpMaxBlockWeight) {
return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
}
// if (GetBlockWeight(block) > dgpMaxBlockWeight) {
// return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
// }

return true;
}
Expand Down

0 comments on commit c9188d9

Please sign in to comment.