from decimal import Decimal
import time
from test_framework.nex.script import anyonecanspend_locking_script
from .nodemessages import (
COIN,
CBlock,
TxOut,
CTransaction,
CTxIn,
COutPoint,
CTxOut,
)
from ..script import (
CScript,
OP_CHECKSIG,
OP_DROP,
OP_DUP,
OP_HASH160,
OP_EQUALVERIFY,
OP_RETURN,
)
from ..util import (
rpc_hex_to_uint256,
uint256_to_rpc_hex,
assert_equal,
unhexlify,
hexlify,
)
from ..serialize import uint256_from_bigendian
from ..utiltx import p2pkh, address2bin
COINBASE_REWARD = Decimal("10000000.00")
def get_anc_hash(height, node):
return rpc_hex_to_uint256(node.getblockheader(ancestor_height(height))["hash"])
def ancestor_height(height):
assert height != 0 if height & 1 == 0:
return height & (height - 1)
return max(0, height - 5040)
def create_block(
hashprev,
height,
chainwork,
coinbase,
hash_ancestor,
n_time=None,
txns=None,
ctor=True,
) -> CBlock:
block = CBlock()
if n_time is None:
block.n_time = int(time.time() + 600)
else:
if not isinstance(n_time, int):
raise ValueError(f"n_time should be int, got {type(n_time)}")
block.n_time = n_time
if isinstance(hashprev, str):
hashprev = uint256_from_bigendian(hashprev)
block.chain_work = chainwork
block.height = height
block.hash_prev_block = hashprev
block.hash_ancestor = hash_ancestor
block.n_bits = 0x207FFFFF
if coinbase:
block.vtx.append(coinbase)
if txns:
if ctor:
txns.sort(key=lambda x: uint256_to_rpc_hex(x.get_id()))
block.vtx += txns
block.tx_count = len(block.vtx)
block.nonce = b""
block.utxo_commitment = b""
block.miner_data = b""
block.nonce = bytearray(3)
block.update_fields()
return block
def gen_return_txouts():
script_pubkey = "6a4d0200" for _ in range(1024):
script_pubkey = script_pubkey + "01"
constraint = bytes.fromhex(script_pubkey) txouts = []
for _ in range(63):
tmp = TxOut(0, 0, constraint)
txouts.append(tmp)
return txouts
def make_conform_to_ctor(block):
for tx in block.vtx:
tx.rehash()
block.vtx = [block.vtx[0]] + sorted(
block.vtx[1:], key=lambda tx: uint256_to_rpc_hex(tx.GetId())
)
def create_coinbase(height) -> CTransaction:
coinbase = CTransaction()
coinbaseoutput = CTxOut()
coinbaseoutput.n_value = int(COINBASE_REWARD) * COIN
halvings = int(height / 150) coinbaseoutput.n_value >>= halvings
coinbaseoutput.script_pub_key = anyonecanspend_locking_script()
coinbaseoutput.t = TxOut.TYPE_TEMPLATE
uniquifier = TxOut(0, 0, CScript([OP_RETURN, height]))
coinbase.vout = [coinbaseoutput, uniquifier]
coinbase_size = len(coinbase.serialize())
if coinbase_size < 65:
coinbase.vout[1].script_pub_key += b"x" * (65 - coinbase_size)
coinbase.calc_id()
return coinbase
def create_transaction(
prevtx: CTransaction, n, sig, value, out, out_type=TxOut.TYPE_TEMPLATE
) -> CTransaction:
prevtx.calc_idem()
if not isinstance(value, list):
value = [value]
tx = CTransaction()
assert n < len(prevtx.vout)
outpt = COutPoint().from_idem_and_idx(prevtx.calc_idem(), n)
tx.vin.append(CTxIn(outpt, prevtx.vout[n].n_value, sig, 0xFFFFFFFF))
for v in value:
tx.vout.append(CTxOut(v, out, out_type))
tx.rehash()
return tx
def create_wasteful_output(btc_address):
data = b"""this is junk data. this is junk data. this is junk data. this is junk data. this is junk data.
this is junk data. this is junk data. this is junk data. this is junk data. this is junk data.
this is junk data. this is junk data. this is junk data. this is junk data. this is junk data."""
ret = CScript(
[
data,
OP_DROP,
OP_DUP,
OP_HASH160,
address2bin(btc_address),
OP_EQUALVERIFY,
OP_CHECKSIG,
]
)
return ret
def spend_coinbase_tx(node, coinbase, to_address, amount, in_amount=None):
if in_amount is None:
in_amount = COINBASE_REWARD
inputs = [
{
"outpoint": COutPoint().from_idem_and_idx(coinbase, 0).rpc_hex(),
"amount": in_amount,
}
]
outputs = {to_address: amount}
rawtx = node.createrawtransaction(inputs, outputs)
signresult = node.signrawtransaction(rawtx)
assert_equal(signresult["complete"], True)
return signresult["hex"]
def createrawtransaction(inputs, outputs, out_script_generator=p2pkh):
if not isinstance(inputs, list):
inputs = [inputs]
tx = CTransaction()
for i in inputs:
sig_script = i.get("sig", b"")
tx.vin.append(
CTxIn(
COutPoint(i["outpoint"]),
int(i["amount"] * COIN),
sig_script,
0xFFFFFFFF,
)
)
pairs = []
if isinstance(outputs, dict):
for addr, amount in outputs.items():
pairs.append((addr, amount))
else:
pairs = outputs
for addr, amount in pairs:
if callable(addr):
tx.vout.append(CTxOut(int(amount * COIN), addr()))
elif isinstance(addr, list):
tx.vout.append(CTxOut(int(amount * COIN), CScript(addr)))
elif addr == "data":
tx.vout.append(CTxOut(0, CScript([OP_RETURN, unhexlify(amount)])))
else:
tx.vout.append(CTxOut(int(amount * COIN), out_script_generator(addr)))
tx.rehash()
return hexlify(tx.serialize()).decode("utf-8")
def create_tx_with_script(
prevtx, n, script_sig=b"", amount=1, script_pub_key=CScript()
):
tx = CTransaction()
assert n < len(prevtx.vout)
tx.vin.append(
CTxIn(prevtx.OutpointAt(n), prevtx.vout[n].nValue, script_sig, 0xFFFFFFFF)
)
tx.vout.append(CTxOut(amount, script_pub_key))
tx.rehash()
return tx