def test_apply_collation_wrong_root(): """Test apply_collation with wrong roots in header test verify_execution_results """ shard_id = 1 t = chain(shard_id) # test 1 - arrange state = t.chain.shards[shard_id].state txqueue = TransactionQueue() tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) txqueue.add_transaction(tx1) # post_state_root collation = t.generate_collation(shard_id=1, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) period_start_prevblock = t.chain.get_block( collation.header.period_start_prevhash) # Set wrong root collation.header.post_state_root = trie.BLANK_ROOT with pytest.raises(ValueError): collator.apply_collation(state, collation, period_start_prevblock) # test 2 - arrange state = t.chain.shards[shard_id].state txqueue = TransactionQueue() tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) txqueue.add_transaction(tx1) # receipts_root collation = t.generate_collation(shard_id=1, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) period_start_prevblock = t.chain.get_block( collation.header.period_start_prevhash) # Set wrong root collation.header.receipts_root = trie.BLANK_ROOT with pytest.raises(ValueError): collator.apply_collation(state, collation, period_start_prevblock) # test 3 - arrange state = t.chain.shards[shard_id].state txqueue = TransactionQueue() tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) txqueue.add_transaction(tx1) # receipts_root collation = t.generate_collation(shard_id=1, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) period_start_prevblock = t.chain.get_block( collation.header.period_start_prevhash) # Set wrong root collation.header.tx_list_root = trie.BLANK_ROOT with pytest.raises(ValueError): collator.apply_collation(state, collation, period_start_prevblock)
def test_apply_collation(): """Apply collation to ShardChain """ shard_id = 1 t = chain(shard_id) txqueue = TransactionQueue() tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) tx2 = t.generate_shard_tx(shard_id, tester.k3, tester.a5, int(0.03 * utils.denoms.ether)) txqueue.add_transaction(tx1) txqueue.add_transaction(tx2) state = t.chain.shards[shard_id].state prev_state_root = state.trie.root_hash collation = t.generate_collation(shard_id=1, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) period_start_prevblock = t.chain.get_block( collation.header.period_start_prevhash) collator.apply_collation(state, collation, period_start_prevblock) assert state.trie.root_hash != prev_state_root assert collation.header.post_state_root == state.trie.root_hash assert collation.header.post_state_root == t.chain.shards[ shard_id].state.trie.root_hash
def test_create_collation_with_txs(): """Test create_collation with transactions """ shard_id = 1 t = chain(shard_id) parent_collation_hash = t.chain.shards[shard_id].head_hash expected_period_number = t.chain.get_expected_period_number() txqueue = TransactionQueue() tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) tx2 = t.generate_shard_tx(shard_id, tester.k3, tester.a5, int(0.03 * utils.denoms.ether)) txqueue.add_transaction(tx1) txqueue.add_transaction(tx2) collation = collator.create_collation(t.chain, shard_id, parent_collation_hash, expected_period_number, coinbase=tester.a0, key=tester.k0, txqueue=txqueue) assert collation.transaction_count == 2
def test_create_collation_empty_txqueue(): """Test create_collation without transactions """ shard_id = 1 t = chain(shard_id) parent_collation_hash = t.chain.shards[shard_id].head_hash expected_period_number = t.chain.get_expected_period_number() txqueue = TransactionQueue() collation = collator.create_collation(t.chain, shard_id, parent_collation_hash, expected_period_number, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) assert collation.transaction_count == 0 assert collation.header.coinbase == tester.a1 # sign error with pytest.raises(TypeError): collation = collator.create_collation(t.chain, shard_id, parent_collation_hash, expected_period_number, coinbase=tester.a1, key=123, txqueue=txqueue)
def mine_on_chain(chain, parent=None, transactions=[], coinbase=None, timestamp=None): """Mine the next block on a chain. The newly mined block will be considered to be the head of the chain, regardless of its total difficulty. :param parent: the parent of the block to mine, or `None` to use the current chain head :param transactions: a list of transactions to include in the new block :param coinbase: optional coinbase to replace ``chain.coinbase`` """ txqueue = TransactionQueue() for t in transactions: txqueue.add_transaction(t) parent_timestamp = parent.timestamp if parent else chain.state.timestamp hc, _ = meta.make_head_candidate(chain, txqueue, parent, timestamp or parent_timestamp + 1, coinbase or '\x00' * 20) assert hc.difficulty == 1 m = ethpow.Miner(hc) rounds = 100 nonce = 0 while True: b = m.mine(rounds=rounds, start_nonce=nonce) if b: break nonce += rounds assert chain.add_block(b) return b
def __init__(self, app): super(ChainServiceMock, self).__init__(app) self.on_new_head_cbs = [] self.transaction_queue = TransactionQueue() self.is_syncing = False self.mined_block = None self.block_mined_event = Event() self.head_candidate = Block(BlockHeader(difficulty=DIFFICULTY), db=DB())
def test_verify_collation_header(): shard_id = 1 t = chain(shard_id) parent_collation_hash = t.chain.shards[shard_id].head_hash expected_period_number = t.chain.get_expected_period_number() txqueue = TransactionQueue() tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) tx2 = t.generate_shard_tx(shard_id, tester.k3, tester.a5, int(0.03 * utils.denoms.ether)) txqueue.add_transaction(tx1) txqueue.add_transaction(tx2) collation = collator.create_collation(t.chain, shard_id, parent_collation_hash, expected_period_number, coinbase=tester.a0, key=tester.k0, txqueue=txqueue) # Verify collation header assert collator.verify_collation_header(t.chain, collation.header) # Bad collation header 1 collation = collator.create_collation(t.chain, shard_id, parent_collation_hash, expected_period_number, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) collation.header.shard_id = -1 with pytest.raises(ValueError): collator.verify_collation_header(t.chain, collation.header) # Bad collation header 2 - call_msg_add_header error collation = collator.create_collation(t.chain, shard_id, parent_collation_hash, expected_period_number, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) collation.header.sig = utils.sha3('hello') with pytest.raises(ValueError): collator.verify_collation_header(t.chain, collation.header)
def test_transaction(): """Test create and apply collation with transactions """ shard_id = 1 t = chain(shard_id) log.info('head state: {}'.format( encode_hex(t.chain.shards[shard_id].state.trie.root_hash))) tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) tx2 = t.generate_shard_tx(shard_id, tester.k3, tester.a5, int(0.03 * utils.denoms.ether)) # Prepare txqueue txqueue = TransactionQueue() txqueue.add_transaction(tx1) txqueue.add_transaction(tx2) collation = t.generate_collation(shard_id=1, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) log.debug('collation: {}, transaction_count:{}'.format( collation.to_dict(), collation.transaction_count)) period_start_prevblock = t.chain.get_block( collation.header.period_start_prevhash) log.debug('period_start_prevblock: {}'.format( encode_hex(period_start_prevblock.header.hash))) t.chain.shards[shard_id].add_collation(collation, period_start_prevblock, t.chain.handle_ignored_collation) state = t.chain.shards[shard_id].mk_poststate_of_collation_hash( collation.header.hash) # Check to addesss received value assert state.get_balance(tester.a4) == 1000030000000000000000 # Check incentives assert state.get_balance(tester.a1) == 1000002000000000000000 # mk_poststate_of_collation_hash error with pytest.raises(Exception): state = t.chain.shards[shard_id].mk_poststate_of_collation_hash( b'1234')
def test_validate_transaction_tree(): """Test validate_transaction_tree(collation) """ t = chain(shard_id) tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) tx2 = t.generate_shard_tx(shard_id, tester.k3, tester.a5, int(0.03 * utils.denoms.ether)) txqueue = TransactionQueue() txqueue.add_transaction(tx1) txqueue.add_transaction(tx2) collation = t.generate_collation(shard_id=1, coinbase=tester.a1, key=tester.k1, txqueue=txqueue) assert state_transition.validate_transaction_tree(collation) collation.header.tx_list_root = trie.BLANK_ROOT with pytest.raises(ValueError): state_transition.validate_transaction_tree(collation)
def __init__(self, genesis, key, network, env, time_offset=5): # Create a chain object self.chain = Chain(genesis, env=env) # Create a transaction queue self.txqueue = TransactionQueue() # Use the validator's time as the chain's time self.chain.time = lambda: self.get_timestamp() # My private key self.key = key # My address self.address = privtoaddr(key) # My randao self.randao = RandaoManager(sha3(self.key)) # Pointer to the test p2p network self.network = network # Record of objects already received and processed self.received_objects = {} # The minimum eligible timestamp given a particular number of skips self.next_skip_count = 0 self.next_skip_timestamp = 0 # Is this validator active? self.active = False # Code that verifies signatures from this validator self.validation_code = generate_validation_code(privtoaddr(key)) # Validation code hash self.vchash = sha3(self.validation_code) # Parents that this validator has already built a block on self.used_parents = {} # This validator's clock offset (for testing purposes) self.time_offset = random.randrange(time_offset) - (time_offset // 2) # Determine the epoch length self.epoch_length = self.call_casper('getEpochLength') # My minimum gas price self.mingasprice = 20 * 10**9 # Give this validator a unique ID self.id = len(ids) ids.append(self.id) self.update_activity_status() self.cached_head = self.chain.head_hash
def test_add_transactions(): """Test add_transactions(state, collation, txqueue, min_gasprice=0) """ t = chain(shard_id) tx1 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(0.03 * utils.denoms.ether)) tx2 = t.generate_shard_tx(shard_id, tester.k3, tester.a5, int(0.03 * utils.denoms.ether)) txqueue = TransactionQueue() txqueue.add_transaction(tx1) txqueue.add_transaction(tx2) coinbase = tester.a1 state = t.chain.shards[shard_id].state.ephemeral_clone() collation = state_transition.mk_collation_from_prevstate( t.chain.shards[shard_id], state, coinbase) state_transition.add_transactions(state, collation, txqueue, shard_id, mainchain_state=t.head_state) assert collation.transaction_count == 2 assert state.get_balance(tester.a4) == 1000 * utils.denoms.ether + int( 0.03 * utils.denoms.ether) # InsufficientBalance -> don't include this transaction tx3 = t.generate_shard_tx(shard_id, tester.k2, tester.a4, int(100000000000 * utils.denoms.ether)) txqueue.add_transaction(tx3) state_transition.add_transactions(state, collation, txqueue, shard_id, mainchain_state=t.head_state) assert collation.transaction_count == 2
def __init__(self, key, genesis, network, valcode_addr=None, mining=False): self.key = key self.coinbase = utils.privtoaddr(self.key) self.chain = chain.Chain(genesis=genesis, reset_genesis=True, coinbase=self.coinbase, new_head_cb=self._on_new_head) self.mining = mining self.nonce = self.chain.state.get_nonce(self.coinbase) self.valcode_tx = None self.deposit_tx = None self.valcode_addr = valcode_addr self.prepares = dict() self.prev_prepare_epoch = 0 self.prev_commit_epoch = 0 self.epoch_length = self.chain.env.config['EPOCH_LENGTH'] # When the transaction_queue is modified, we must set # self._head_candidate_needs_updating to True in order to force the # head candidate to be updated. self.transaction_queue = TransactionQueue() self._head_candidate_needs_updating = True # Add validator to the network self.network = network self.network.join(self)
def make_block(chain, key, randao, vchash, skips): h, _ = make_head_candidate(chain, TransactionQueue(), timestamp=get_timestamp(chain, skips)) return sign_block(h, key, randao.get_parent(call_casper(chain.state, 'getRandao', [vchash])), vchash, skips)
def __init__(self, app): self.config = app.config sce = self.config['eth'] if int(sce['pruning']) >= 0: self.db = RefcountDB(app.services.db) if "I am not pruning" in self.db.db: raise RuntimeError( "The database in '{}' was initialized as non-pruning. " "Can not enable pruning now.".format( self.config['data_dir'])) self.db.ttl = int(sce['pruning']) self.db.db.put("I am pruning", "1") else: self.db = app.services.db if "I am pruning" in self.db: raise RuntimeError( "The database in '{}' was initialized as pruning. " "Can not disable pruning now".format( self.config['data_dir'])) self.db.put("I am not pruning", "1") if 'network_id' in self.db: db_network_id = self.db.get(b'network_id') if db_network_id != to_string(sce['network_id']): raise RuntimeError( "The database in '{}' was initialized with network id {} and can not be used " "when connecting to network id {}. Please choose a different data directory." .format(self.config['data_dir'], db_network_id, sce['network_id'])) else: self.db.put(b'network_id', to_string(sce['network_id'])) self.db.commit() assert self.db is not None super(ChainService, self).__init__(app) log.info('initializing chain') coinbase = app.services.accounts.coinbase env = Env(self.db, sce['block']) genesis_data = sce.get('genesis_data', {}) if not genesis_data: genesis_data = mk_genesis_data(env) self.chain = Chain(env=env, genesis=genesis_data, coinbase=coinbase, new_head_cb=self._on_new_head) header = self.chain.state.prev_headers[0] log.info('chain at', number=header.number) if 'genesis_hash' in sce: assert sce['genesis_hash'] == self.chain.genesis.hex_hash, \ "Genesis hash mismatch.\n Expected: %s\n Got: %s" % ( sce['genesis_hash'], self.chain.genesis.hex_hash) self.dao_challenges = dict() self.synchronizer = Synchronizer(self, force_sync=None) self.block_queue = Queue(maxsize=self.block_queue_size) # When the transaction_queue is modified, we must set # self._head_candidate_needs_updating to True in order to force the # head candidate to be updated. self.transaction_queue = TransactionQueue() self._head_candidate_needs_updating = True # Initialize a new head candidate. _ = self.head_candidate self.min_gasprice = 20 * 10**9 # TODO: better be an option to validator service? self.add_blocks_lock = False self.add_transaction_lock = gevent.lock.Semaphore() self.broadcast_filter = DuplicatesFilter() self.on_new_head_cbs = [] self.newblock_processing_times = deque(maxlen=1000) gevent.spawn_later(self.process_time_queue_period, self.process_time_queue)