from decimal import Decimal import time from .nodemessages import CBlock, CTransaction, CTxIn, COutPoint, CTxOut from ..script import OP_NOP, OP_CHECKSIG, CScript from ..constants import COIN from ..serialize import ser_string, serialize_script_num COINBASE_REWARD = Decimal("50.00000000") def create_block(hashprev, coinbase, n_time: int | None = None, txns=None) -> CBlock: """ Create a block (with regtest difficulty) """ block = CBlock() if n_time is None: block.n_time = int(time.time() + 600) else: block.n_time = n_time if isinstance(hashprev, str): hashprev = int(hashprev, 16) block.hash_prev_block = hashprev block.n_bits = 0x207FFFFF # Will break after a difficulty adjustment... if coinbase: block.vtx.append(coinbase) if txns: txns.sort(key=lambda x: x.hash) block.vtx += txns block.hash_merkle_root = block.calc_merkle_root() block.rehash() return block def create_coinbase(height, pubkey=None, script_pubkey=None): """ Create a coinbase transaction, assuming no miner fees. If pubkey is passed in, the coinbase output will be a P2PK output; otherwise an anyone-can-spend output. """ assert not ( pubkey and script_pubkey ), "cannot both have pubkey and custom scriptPubKey" coinbase = CTransaction() coinbase.vin.append( CTxIn( COutPoint(0, 0xFFFFFFFF), ser_string(serialize_script_num(height)), 0xFFFFFFFF, ) ) coinbaseoutput = CTxOut() coinbaseoutput.n_value = int(COINBASE_REWARD) * COIN halvings = int(height / 150) # regtest coinbaseoutput.n_value >>= halvings if pubkey is not None: coinbaseoutput.script_pubkey = CScript([pubkey, OP_CHECKSIG]) else: if script_pubkey is None: script_pubkey = CScript([OP_NOP]) coinbaseoutput.script_pubkey = CScript(script_pubkey) coinbase.vout = [coinbaseoutput] # Make sure the coinbase is at least 100 bytes coinbase_size = len(coinbase.serialize()) if coinbase_size < 100: coinbase.vin[0].script_sig += b"x" * (100 - coinbase_size) coinbase.calc_sha256() return coinbase PADDED_ANY_SPEND = ( b"\x61" * 50 ) # add a bunch of OP_NOPs to make sure this tx is long enough def create_transaction( prevtx: CTransaction, n, sig, value, out=PADDED_ANY_SPEND ) -> CTransaction: """ Create a transaction with an anyone-can-spend output, that spends the nth output of prevtx. pass a single integer value to make one output, or a list to create multiple outputs """ prevtx.calc_sha256() if not isinstance(value, list): value = [value] tx = CTransaction() assert n < len(prevtx.vout) tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), sig, 0xFFFFFFFF)) for v in value: tx.vout.append(CTxOut(v, out)) tx.rehash() return tx