def add_output_to_coinstake(self, block, value, peer=1): coinstake = block.vtx[1] if not hasattr(self, 'DUMMY_KEY'): self.init_dummy_key() coinstake.vout.append( CTxOut(value * COIN, CScript([self.DUMMY_KEY.get_pubkey(), OP_CHECKSIG]))) coinstake.vout[1].nValue -= value * COIN # re-sign coinstake prevout = COutPoint() prevout.deserialize_uniqueness(BytesIO(block.prevoutStake)) coinstake.vin[0] = CTxIn(prevout) stake_tx_signed_raw_hex = self.nodes[peer].signrawtransaction( bytes_to_hex_str(coinstake.serialize()))['hex'] block.vtx[1] = CTransaction() block.vtx[1].from_hex(stake_tx_signed_raw_hex) # re-sign block block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.re_sign_block()
def fake_stake(self, staking_utxo_list, nHeight=-1, fDoubleSpend=False): """ General method to create, send and test the spam blocks :param staking_utxo_list: (string list) utxos to use for staking nHeight: (int, optional) height of the staked block. Used only for fork chain. In main chain it's current height + 1 fDoubleSpend: (bool) if true, stake input is double spent in block.vtx :return: """ def get_prev_modifier(prevBlockHash): prevBlock = self.nodes[1].getblock(prevBlockHash) if prevBlock['height'] > 250: return prevBlock['stakeModifier'] return "0" # Get block number, block time and prevBlock hash and modifier currHeight = self.nodes[1].getblockcount() isMainChain = (nHeight == -1) chainName = "main" if isMainChain else "forked" nTime = self.mocktime if isMainChain: nHeight = currHeight + 1 prevBlockHash = self.nodes[1].getblockhash(nHeight - 1) prevModifier = get_prev_modifier(prevBlockHash) nTime += (nHeight - currHeight) * 60 # New block hash, coinstake input and list of txes bHash = None stakedUtxo = None # For each test, send three blocks. # On main chain they are all the same height. # On fork chain, send three blocks where both the second and third block sent, # are built on top of the first one. for i in range(3): fMustBeAccepted = (not isMainChain and i != 1) block_txes = [] # update block number and prevBlock hash on second block sent on forked chain if not isMainChain and i == 1: nHeight += 1 nTime += 60 prevBlockHash = bHash prevModifier = get_prev_modifier(prevBlockHash) stakeInputs = self.get_prevouts(1, staking_utxo_list, False, nHeight - 1) # Update stake inputs for second block sent on forked chain (must stake the same input) if not isMainChain and i == 1: stakeInputs = self.get_prevouts(1, [stakedUtxo], False, nHeight-1) # Make spam txes sending the inputs to DUMMY_KEY in order to test double spends if fDoubleSpend: spending_prevouts = self.get_prevouts(1, staking_utxo_list) block_txes = self.make_txes(1, spending_prevouts, self.DUMMY_KEY.get_pubkey()) # Stake the spam block block = self.stake_block(1, 7, nHeight, prevBlockHash, prevModifier, "0", stakeInputs, nTime, "", block_txes, fDoubleSpend) # Log stake input prevout = COutPoint() prevout.deserialize_uniqueness(BytesIO(block.prevoutStake)) self.log.info("Staked input: [%s...-%s]" % ('{:x}'.format(prevout.hash)[:12], prevout.n)) # Try submitblock and check result self.log.info("Trying to send block [%s...] with height=%d" % (block.hash[:16], nHeight)) var = self.nodes[1].submitblock(bytes_to_hex_str(block.serialize())) sleep(1) if (not fMustBeAccepted and var not in [None, "rejected"]): raise AssertionError("Error, block submitted (%s) in %s chain" % (var, chainName)) elif (fMustBeAccepted and var != "inconclusive"): raise AssertionError("Error, block not submitted (%s) in %s chain" % (var, chainName)) self.log.info("Done. Updating context...") # Sync and check block hash bHash = block.hash self.checkBlockHash(bHash, fMustBeAccepted) # Update curr block data stakedUtxo = [x for x in staking_utxo_list if COutPoint( int(x['txid'], 16), x['vout']).serialize_uniqueness() == block.prevoutStake][0] # Remove the used coinstake input (except before second block on fork chain) if isMainChain or i != 0: staking_utxo_list.remove(stakedUtxo) self.log.info("All blocks sent")