Generate some v5 coinbase transactions.
Co-authored-by: Kris Nuttycombe <kris@nutty.land> Signed-off-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
parent
ff9e171ff3
commit
60faf1c2c5
File diff suppressed because one or more lines are too long
|
@ -160,6 +160,8 @@ class OutputDescription(object):
|
||||||
|
|
||||||
class OrchardActionDescription(object):
|
class OrchardActionDescription(object):
|
||||||
def __init__(self, rand):
|
def __init__(self, rand):
|
||||||
|
# We don't need to take account of whether this is a coinbase transaction,
|
||||||
|
# because we're only generating random fields.
|
||||||
self.cv = pallas_group_hash(b'TVRandPt', rand.b(32))
|
self.cv = pallas_group_hash(b'TVRandPt', rand.b(32))
|
||||||
self.nullifier = PallasBase(leos2ip(rand.b(32)))
|
self.nullifier = PallasBase(leos2ip(rand.b(32)))
|
||||||
self.rk = pallas_group_hash(b'TVRandPt', rand.b(32))
|
self.rk = pallas_group_hash(b'TVRandPt', rand.b(32))
|
||||||
|
@ -220,10 +222,17 @@ RAND_OPCODES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
class Script(object):
|
class Script(object):
|
||||||
def __init__(self, rand):
|
def __init__(self, rand=None):
|
||||||
self._script = bytes([
|
if rand is not None:
|
||||||
rand.a(RAND_OPCODES) for i in range(rand.i8() % 10)
|
self._script = bytes([
|
||||||
])
|
rand.a(RAND_OPCODES) for i in range(rand.i8() % 10)
|
||||||
|
])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_bytes(b):
|
||||||
|
script = Script()
|
||||||
|
script._script = b
|
||||||
|
return script
|
||||||
|
|
||||||
def raw(self):
|
def raw(self):
|
||||||
return self._script
|
return self._script
|
||||||
|
@ -233,19 +242,36 @@ class Script(object):
|
||||||
|
|
||||||
|
|
||||||
class OutPoint(object):
|
class OutPoint(object):
|
||||||
def __init__(self, rand):
|
def __init__(self, rand=None):
|
||||||
self.txid = rand.b(32)
|
if rand is not None:
|
||||||
self.n = rand.u32()
|
self.txid = rand.b(32)
|
||||||
|
self.n = rand.u32()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_components(txid, n):
|
||||||
|
outpoint = OutPoint()
|
||||||
|
outpoint.txid = txid
|
||||||
|
outpoint.n = n
|
||||||
|
return outpoint
|
||||||
|
|
||||||
def __bytes__(self):
|
def __bytes__(self):
|
||||||
return self.txid + struct.pack('<I', self.n)
|
return self.txid + struct.pack('<I', self.n)
|
||||||
|
|
||||||
|
|
||||||
class TxIn(object):
|
class TxIn(object):
|
||||||
def __init__(self, rand):
|
def __init__(self, rand=None):
|
||||||
self.prevout = OutPoint(rand)
|
if rand is not None:
|
||||||
self.scriptSig = Script(rand)
|
self.prevout = OutPoint(rand)
|
||||||
self.nSequence = rand.u32()
|
self.scriptSig = Script(rand)
|
||||||
|
self.nSequence = rand.u32()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_components(prevout, scriptSig, nSequence):
|
||||||
|
txin = TxIn()
|
||||||
|
txin.prevout = prevout
|
||||||
|
txin.scriptSig = scriptSig
|
||||||
|
txin.nSequence = nSequence
|
||||||
|
return txin
|
||||||
|
|
||||||
def __bytes__(self):
|
def __bytes__(self):
|
||||||
return (
|
return (
|
||||||
|
@ -372,6 +398,7 @@ class TransactionV5(object):
|
||||||
have_transparent_out = (flip_coins >> 1) % 2
|
have_transparent_out = (flip_coins >> 1) % 2
|
||||||
have_sapling = (flip_coins >> 2) % 2
|
have_sapling = (flip_coins >> 2) % 2
|
||||||
have_orchard = (flip_coins >> 3) % 2
|
have_orchard = (flip_coins >> 3) % 2
|
||||||
|
is_coinbase = (not have_transparent_in) and (flip_coins >> 4) % 2
|
||||||
|
|
||||||
# Common Transaction Fields
|
# Common Transaction Fields
|
||||||
self.nVersionGroupId = NU5_VERSION_GROUP_ID
|
self.nVersionGroupId = NU5_VERSION_GROUP_ID
|
||||||
|
@ -385,6 +412,11 @@ class TransactionV5(object):
|
||||||
if have_transparent_in:
|
if have_transparent_in:
|
||||||
for _ in range((rand.u8() % 3) + 1):
|
for _ in range((rand.u8() % 3) + 1):
|
||||||
self.vin.append(TxIn(rand))
|
self.vin.append(TxIn(rand))
|
||||||
|
if is_coinbase:
|
||||||
|
self.vin.append(TxIn.from_components(
|
||||||
|
OutPoint.from_components(b'\x00' * 32, 0xFFFFFFFF),
|
||||||
|
Script.from_bytes(b"\x51"),
|
||||||
|
0))
|
||||||
if have_transparent_out:
|
if have_transparent_out:
|
||||||
for _ in range((rand.u8() % 3) + 1):
|
for _ in range((rand.u8() % 3) + 1):
|
||||||
self.vout.append(TxOut(rand))
|
self.vout.append(TxOut(rand))
|
||||||
|
@ -394,8 +426,11 @@ class TransactionV5(object):
|
||||||
self.vOutputsSapling = []
|
self.vOutputsSapling = []
|
||||||
if have_sapling:
|
if have_sapling:
|
||||||
self.anchorSapling = Fq(leos2ip(rand.b(32)))
|
self.anchorSapling = Fq(leos2ip(rand.b(32)))
|
||||||
|
# We use the randomness unconditionally here to avoid unnecessary test vector changes.
|
||||||
for _ in range(rand.u8() % 3):
|
for _ in range(rand.u8() % 3):
|
||||||
self.vSpendsSapling.append(SpendDescription(rand, self.anchorSapling))
|
spend = SpendDescription(rand, self.anchorSapling)
|
||||||
|
if not is_coinbase:
|
||||||
|
self.vSpendsSapling.append(spend)
|
||||||
for _ in range(rand.u8() % 3):
|
for _ in range(rand.u8() % 3):
|
||||||
self.vOutputsSapling.append(OutputDescription(rand))
|
self.vOutputsSapling.append(OutputDescription(rand))
|
||||||
self.valueBalanceSapling = rand.u64() % (MAX_MONEY + 1)
|
self.valueBalanceSapling = rand.u64() % (MAX_MONEY + 1)
|
||||||
|
@ -411,6 +446,9 @@ class TransactionV5(object):
|
||||||
for _ in range(rand.u8() % 5):
|
for _ in range(rand.u8() % 5):
|
||||||
self.vActionsOrchard.append(OrchardActionDescription(rand))
|
self.vActionsOrchard.append(OrchardActionDescription(rand))
|
||||||
self.flagsOrchard = rand.u8() & 3 # Only two flag bits are currently defined.
|
self.flagsOrchard = rand.u8() & 3 # Only two flag bits are currently defined.
|
||||||
|
if is_coinbase:
|
||||||
|
# set enableSpendsOrchard = 0
|
||||||
|
self.flagsOrchard &= 2
|
||||||
self.valueBalanceOrchard = rand.u64() % (MAX_MONEY + 1)
|
self.valueBalanceOrchard = rand.u64() % (MAX_MONEY + 1)
|
||||||
self.anchorOrchard = PallasBase(leos2ip(rand.b(32)))
|
self.anchorOrchard = PallasBase(leos2ip(rand.b(32)))
|
||||||
self.proofsOrchard = rand.b(rand.u8() + 32) # Proof will always contain at least one element
|
self.proofsOrchard = rand.b(rand.u8() + 32) # Proof will always contain at least one element
|
||||||
|
@ -420,6 +458,8 @@ class TransactionV5(object):
|
||||||
# v^balanceOrchard is defined to be 0.
|
# v^balanceOrchard is defined to be 0.
|
||||||
self.valueBalanceOrchard = 0
|
self.valueBalanceOrchard = 0
|
||||||
|
|
||||||
|
assert is_coinbase == self.is_coinbase()
|
||||||
|
|
||||||
def version_bytes(self):
|
def version_bytes(self):
|
||||||
return NU5_TX_VERSION | (1 << 31)
|
return NU5_TX_VERSION | (1 << 31)
|
||||||
|
|
||||||
|
|
|
@ -356,10 +356,13 @@ def main():
|
||||||
txid = txid_digest(tx)
|
txid = txid_digest(tx)
|
||||||
auth = auth_digest(tx)
|
auth = auth_digest(tx)
|
||||||
|
|
||||||
# Generate amounts and scriptCodes for each transparent input.
|
# Generate amounts and scriptCodes for each non-dummy transparent input.
|
||||||
t_inputs = [TransparentInput(nIn, rand) for nIn in range(len(tx.vin))]
|
if tx.is_coinbase():
|
||||||
|
t_inputs = []
|
||||||
|
else:
|
||||||
|
t_inputs = [TransparentInput(nIn, rand) for nIn in range(len(tx.vin))]
|
||||||
|
|
||||||
# If there are any transparent inputs, derive a corresponding transparent sighash.
|
# If there are any non-dummy transparent inputs, derive a corresponding transparent sighash.
|
||||||
if len(t_inputs) > 0:
|
if len(t_inputs) > 0:
|
||||||
txin = rand.a(t_inputs)
|
txin = rand.a(t_inputs)
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue