Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to TXID generation to account for ZSA additions #22

Merged
merged 36 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e1674dd
updating zip_0244 functions to support generating both V5 and V6 dige…
vivek-arte May 4, 2024
e08b89c
adding V6 transaction class to transaction.py -- code reuse needs to …
vivek-arte May 4, 2024
97ba30d
changes to transaction.py and zip_0244.py
vivek-arte May 15, 2024
9fbb1b0
minor update
vivek-arte May 20, 2024
9f308ee
initial attempt at inheritance, to be improved
vivek-arte Jun 10, 2024
4744106
changing inheritance structure of TransactionV5 and TransactionV6, an…
vivek-arte Jun 14, 2024
26616ee
reverting zip_0244.py to only include V5 details
vivek-arte Jun 14, 2024
1130d66
separate txid vectors for zsa into separate files
vivek-arte Jun 14, 2024
aea6de2
adding ZSA version of zip 244 vectors
vivek-arte Jun 16, 2024
1a2a58e
renaming to NU7 and so on
vivek-arte Jun 17, 2024
c33d00a
still WIP, updating to work with librustzcash code
vivek-arte Jun 18, 2024
49aeb58
updating get_random_unicode_bytes to give a slightly wider range of v…
vivek-arte Jul 10, 2024
256c06f
changes to the transaction format serialization and the txid generati…
vivek-arte Jul 10, 2024
029cfae
updating the generated test vectors
vivek-arte Jul 10, 2024
cca44d8
some cleanup changes
vivek-arte Jul 22, 2024
2c5e31d
removing unichr option from get_random_unicode_bytes
vivek-arte Jul 27, 2024
6bd821d
cleaner import
vivek-arte Jul 27, 2024
2ebe23d
simple changes based on review comments
vivek-arte Jul 27, 2024
4ad2361
moving common transaction fields in a way that reduces code duplication
vivek-arte Jul 29, 2024
10ef794
reducing code duplication by creating an OrchardActionBase class whic…
vivek-arte Jul 29, 2024
59b0372
reducing code duplication in transaction, transaction_zsa, zip_0244 a…
vivek-arte Jul 30, 2024
df173c0
reducing code duplication inside the main function of zip_0244 and zi…
vivek-arte Jul 30, 2024
ea6e099
regenerating test vectors
vivek-arte Jul 30, 2024
0448e1e
minor spacing fix to reduce diff
vivek-arte Jul 30, 2024
7a48c13
resolving review comments, first pass
vivek-arte Aug 15, 2024
5cedbba
resolving review comments, second pass
vivek-arte Aug 18, 2024
fcc4f94
resolving review comments, third pass
vivek-arte Aug 24, 2024
62a239e
updating test vector files
vivek-arte Aug 24, 2024
ede0e0a
changing txn version back to V6
vivek-arte Aug 26, 2024
de12205
resolving review comments
vivek-arte Sep 5, 2024
ea48161
renaming rho to nf_old in note_encryption files
vivek-arte Sep 17, 2024
0409263
making changes based on review
vivek-arte Sep 18, 2024
0d1e967
further changes
vivek-arte Sep 18, 2024
1d936d1
adding Offsets class, other review changes
vivek-arte Sep 18, 2024
737b366
removing redundant import
vivek-arte Sep 18, 2024
ff64cde
based on review comments
vivek-arte Sep 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ unified_incoming_viewing_keys = "zcash_test_vectors.unified_incoming_viewing_key
zip_0143 = "zcash_test_vectors.zip_0143:main"
zip_0243 = "zcash_test_vectors.zip_0243:main"
zip_0244 = "zcash_test_vectors.zip_0244:main"
zip_0244_zsa = "zcash_test_vectors.zip_0244_zsa:main"

# Transparent test vectors
bip_0032 = "zcash_test_vectors.transparent.bip_0032:main"
Expand Down
1 change: 1 addition & 0 deletions regenerate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ tv_scripts=(
zip_0143
zip_0243
zip_0244
zip_0244_zsa
zip_0316
zip_0320)

Expand Down
40 changes: 20 additions & 20 deletions test-vectors/json/orchard_zsa_asset_base.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions test-vectors/json/zip_0244.json

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions test-vectors/json/zip_0244_zsa.json

Large diffs are not rendered by default.

118 changes: 59 additions & 59 deletions test-vectors/rust/orchard_zsa_asset_base.rs

Large diffs are not rendered by default.

268 changes: 118 additions & 150 deletions test-vectors/rust/zip_0244.rs

Large diffs are not rendered by default.

425 changes: 425 additions & 0 deletions test-vectors/rust/zip_0244_zsa.rs

Large diffs are not rendered by default.

40 changes: 20 additions & 20 deletions test-vectors/zcash/orchard_zsa_asset_base.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions test-vectors/zcash/zip_0244.json

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions test-vectors/zcash/zip_0244_zsa.json

Large diffs are not rendered by default.

14 changes: 6 additions & 8 deletions zcash_test_vectors/orchard_zsa/asset_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from hashlib import blake2b
from ..orchard.group_hash import group_hash
from ..output import render_args, render_tv, option
from ..output import render_args, render_tv


def native_asset():
Expand All @@ -29,13 +29,11 @@ def zsa_value_base(asset_digest_value):
return group_hash(b"z.cash:OrchardZSA", asset_digest_value)


def get_random_unicode_bytes(length):
try:
get_char = unichr
except NameError:
get_char = chr
def get_random_unicode_bytes(length, rand):
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved

random.seed(0xabad533d)
get_char = chr

random.seed(rand.u8())
PaulLaux marked this conversation as resolved.
Show resolved Hide resolved

# Update this to include code point ranges to be sampled
include_ranges = [
Expand Down Expand Up @@ -84,7 +82,7 @@ def randbytes(l):
isk = IssuanceKeys(rand.b(32))

key_bytes = bytes(isk.ik)
description_bytes = get_random_unicode_bytes(512)
description_bytes = get_random_unicode_bytes(512, rand)
asset_base = zsa_value_base(asset_digest(encode_asset_id(key_bytes, description_bytes)))

test_vectors.append({
Expand Down
81 changes: 53 additions & 28 deletions zcash_test_vectors/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,16 @@ def __bytes__(self):
bytes(self.proof)
)

class OrchardActionDescription(object):
def __init__(self, rand):
class OrchardActionBase(object):
def __init__(self, enc_ciphertext_size, 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.nullifier = PallasBase(leos2ip(rand.b(32)))
self.rk = pallas_group_hash(b'TVRandPt', rand.b(32))
self.cmx = PallasBase(leos2ip(rand.b(32)))
self.ephemeralKey = pallas_group_hash(b'TVRandPt', rand.b(32))
self.encCiphertext = rand.b(ZC_SAPLING_ENCCIPHERTEXT_SIZE)
self.encCiphertext = rand.b(enc_ciphertext_size)
self.outCiphertext = rand.b(ZC_SAPLING_OUTCIPHERTEXT_SIZE)
self.spendAuthSig = RedPallasSignature(rand)

Expand All @@ -182,6 +182,10 @@ def __bytes__(self):
self.outCiphertext
)

class OrchardActionDescription(OrchardActionBase):
def __init__(self, rand):
super().__init__(ZC_SAPLING_ENCCIPHERTEXT_SIZE, rand)

class JoinSplit(object):
def __init__(self, rand, fUseGroth = False):
self.vpub_old = 0
Expand Down Expand Up @@ -311,7 +315,6 @@ def __init__(self, rand):
def __bytes__(self):
return struct.pack('<Q', self.nValue) + bytes(self.scriptPubKey)


vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
class LegacyTransaction(object):
def __init__(self, rand, version):
if version == OVERWINTER_TX_VERSION:
Expand Down Expand Up @@ -411,20 +414,17 @@ def __bytes__(self):

return ret


class TransactionV5(object):
def __init__(self, rand, consensus_branch_id):
# Creating a base transaction class with common fields that V5 and subsequent versions can inherit.
class TransactionBase(object):
def __init__(self, rand, have_orchard=True):
# Decide which transaction parts will be generated.
flip_coins = rand.u8()
have_transparent_in = (flip_coins >> 0) % 2
have_transparent_out = (flip_coins >> 1) % 2
have_sapling = (flip_coins >> 2) % 2
have_orchard = (flip_coins >> 3) % 2
is_coinbase = (not have_transparent_in) and (flip_coins >> 4) % 2

# Common Transaction Fields
self.nVersionGroupId = NU5_VERSION_GROUP_ID
self.nConsensusBranchId = consensus_branch_id
self.nLockTime = rand.u32()
self.nExpiryHeight = rand.u32() % TX_EXPIRY_HEIGHT_THRESHOLD

Expand Down Expand Up @@ -462,15 +462,12 @@ def __init__(self, rand, consensus_branch_id):
# v^balanceSapling is defined to be 0.
self.valueBalanceSapling = 0

# Orchard Transaction Fields
assert is_coinbase == self.is_coinbase()
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved

# Orchard Transaction Fields that are present in both V5 and V6
self.vActionsOrchard = []
if have_orchard:
for _ in range(rand.u8() % 5):
self.vActionsOrchard.append(OrchardActionDescription(rand))
self.flagsOrchard = rand.u8() & 3 # Only two flag bits are currently defined.
if is_coinbase:
# set enableSpendsOrchard = 0
self.flagsOrchard &= 2
self.flagsOrchard = rand.u8() # This needs to be constrained correctly in the child classes.
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
self.valueBalanceOrchard = rand.u64() % (MAX_MONEY + 1)
self.anchorOrchard = PallasBase(leos2ip(rand.b(32)))
self.proofsOrchard = rand.b(rand.u8() + 32) # Proof will always contain at least one element
Expand All @@ -480,23 +477,17 @@ def __init__(self, rand, consensus_branch_id):
# v^balanceOrchard is defined to be 0.
self.valueBalanceOrchard = 0

assert is_coinbase == self.is_coinbase()

def version_bytes(self):
return NU5_TX_VERSION | (1 << 31)

def is_coinbase(self):
# <https://github.com/zcash/zcash/blob/d8c818bfa507adb845e527f5beb38345c490b330/src/primitives/transaction.h#L969-L972>
return len(self.vin) == 1 and bytes(self.vin[0].prevout.txid) == b'\x00'*32 and self.vin[0].prevout.n == 0xFFFFFFFF

# TODO: Update ZIP 225 to document endianness
def __bytes__(self):
def __bytes__(self, version_bytes, nVersionGroupId, nConsensusBranchId):
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
ret = b''

# Common Transaction Fields
ret += struct.pack('<I', self.version_bytes())
ret += struct.pack('<I', self.nVersionGroupId)
ret += struct.pack('<I', self.nConsensusBranchId)
ret += struct.pack('<I', version_bytes)
ret += struct.pack('<I', nVersionGroupId)
ret += struct.pack('<I', nConsensusBranchId)
ret += struct.pack('<I', self.nLockTime)
ret += struct.pack('<I', self.nExpiryHeight)

Expand Down Expand Up @@ -545,10 +536,44 @@ def __bytes__(self):
ret += self.proofsOrchard
for desc in self.vActionsOrchard:
ret += bytes(desc.spendAuthSig)
ret += bytes(self.bindingSigOrchard)

return ret

class TransactionV5(TransactionBase):
def __init__(self, rand, consensus_branch_id):

vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
have_orchard = rand.bool()
# All Transparent and Sapling Transaction Fields are initialized in the super class.
# The super class will also initialize some of the Orchard Transaction Fields.
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
super().__init__(rand, have_orchard)

# Common Transaction Fields
self.nVersionGroupId = NU5_VERSION_GROUP_ID
self.nConsensusBranchId = consensus_branch_id

# Orchard Transaction Fields
if have_orchard:
for _ in range(rand.u8() % 5):
self.vActionsOrchard.append(OrchardActionDescription(rand))
self.flagsOrchard &= 3 # Only two flag bits are currently defined.
if self.is_coinbase():
# set enableSpendsOrchard = 0
self.flagsOrchard &= 2

def version_bytes(self):
return NU5_TX_VERSION | (1 << 31)

def __bytes__(self):
ret = b''

# Fields that are in TransactionBase: Common, Transparent, Sapling, most Orchard
ret += super().__bytes__(self.version_bytes(), self.nVersionGroupId, self.nConsensusBranchId)

# Orchard remaining Transaction Fields (if the Orchard bundle exists)
if len(self.vActionsOrchard) > 0:
PaulLaux marked this conversation as resolved.
Show resolved Hide resolved
ret += bytes(self.bindingSigOrchard)

return ret

class Transaction(object):
def __init__(self, rand, version, consensus_branch_id=None):
Expand Down
157 changes: 157 additions & 0 deletions zcash_test_vectors/transaction_zsa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import struct

from .orchard.key_components import FullViewingKey, SpendingKey
from .orchard_zsa.key_components import IssuanceKeys
from .orchard.pallas import Fp as PallasBase, Point
from .orchard.sinsemilla import group_hash as pallas_group_hash
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
from .orchard_zsa.asset_base import zsa_value_base, asset_digest, encode_asset_id, get_random_unicode_bytes
from .utils import leos2ip
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
from .zc_utils import write_compact_size
from .transaction import (
MAX_MONEY, NOTEENCRYPTION_AUTH_BYTES,
ZC_SAPLING_ENCPLAINTEXT_SIZE, ZC_SAPLING_OUTCIPHERTEXT_SIZE,
OrchardActionBase,
RedPallasSignature,
TransactionBase,
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
)

NU7_VERSION_GROUP_ID = 0x124A69F8
NU7_TX_VERSION = 7

# Orchard ZSA note values
ZC_ORCHARD_ZSA_ASSET_SIZE = 32
ZC_ORCHARD_ZSA_ENCPLAINTEXT_SIZE = ZC_SAPLING_ENCPLAINTEXT_SIZE + ZC_ORCHARD_ZSA_ASSET_SIZE
ZC_ORCHARD_ZSA_ENCCIPHERTEXT_SIZE = ZC_ORCHARD_ZSA_ENCPLAINTEXT_SIZE + NOTEENCRYPTION_AUTH_BYTES

class OrchardZSAActionDescription(OrchardActionBase):
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
def __init__(self, rand):
super().__init__(ZC_ORCHARD_ZSA_ENCCIPHERTEXT_SIZE, rand)

class AssetBurnDescription(object):
def __init__(self, rand):
isk = IssuanceKeys(rand.b(32))
desc_size = rand.u32() % 512 + 1
desc_bytes = get_random_unicode_bytes(desc_size, rand)
asset_digest_bytes = asset_digest(encode_asset_id(isk.ik, desc_bytes))
self.assetBase : Point = zsa_value_base(asset_digest_bytes)
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
self.valueBurn = rand.u64()

def __bytes__(self):
return bytes(self.assetBase) + struct.pack('<Q', self.valueBurn)

class IssueActionDescription(object):
def __init__(self, rand, ik):
self.assetDescSize = rand.u32() % 512 + 1
self.asset_desc = get_random_unicode_bytes(self.assetDescSize, rand)
self.vNotes = []
for _ in range(rand.u8() % 5):
self.vNotes.append(IssueNote(rand, ik, self.asset_desc))
self.flagsIssuance = rand.u8() & 1 # Only one bit is reserved for the finalize flag currently

def __bytes__(self):
ret = b''

ret += write_compact_size(self.assetDescSize)
ret += bytes(self.asset_desc)
ret += write_compact_size(len(self.vNotes))
if len(self.vNotes) > 0:
for note in self.vNotes:
ret += bytes(note)
ret += struct.pack('B', self.flagsIssuance)

return ret

class IssueNote(object):
def __init__(self, rand, ik, asset_desc):
fvk_r = FullViewingKey.from_spending_key(SpendingKey(rand.b(32)))
self.recipient = fvk_r.default_d() + bytes(fvk_r.default_pkd())
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
self.value = rand.u64()
asset_digest_bytes = asset_digest(encode_asset_id(ik, asset_desc))
self.assetBase = zsa_value_base(asset_digest_bytes)
self.rho = Point.rand(rand).extract()
self.rseed = rand.b(32)

def __bytes__(self):
ret = b''
ret += bytes(self.recipient)
ret += struct.pack('<Q', self.value)
ret += bytes(self.assetBase)
ret += bytes(self.rho)
ret += self.rseed

return ret


class TransactionZSA(TransactionBase):
def __init__(self, rand, consensus_branch_id, have_orchard_zsa = True, have_burn = True, have_issuance = True):

# Since burn is part of the OrchardZSA bundle, ensure that there are no burn fields
# when there are no OrchardZSA fields
assert have_orchard_zsa or not have_burn
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved

# All the Transparent and Sapling Transaction Fields are initialized in the super (TransactionBase) class.
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
super().__init__(rand, have_orchard_zsa)

# Common Transaction Fields
self.nVersionGroupId = NU7_VERSION_GROUP_ID
self.nConsensusBranchId = consensus_branch_id

# Orchard-ZSA Transaction Fields
if have_orchard_zsa:
for _ in range(rand.u8() % 5):
self.vActionsOrchard.append(OrchardZSAActionDescription(rand))
self.flagsOrchard = (self.flagsOrchard & 7) | 4 # Only three flag bits are currently defined, and we set enableZSA to true.
if self.is_coinbase():
# set enableSpendsOrchard = 0
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
self.flagsOrchard &= 2

# OrchardZSA Burn Fields
self.vAssetBurnOrchardZSA = []
if have_burn:
for _ in range(rand.u8() % 5):
self.vAssetBurnOrchardZSA.append(AssetBurnDescription(rand))

# ZSA Issuance Fields
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
self.vIssueActions = []
if have_issuance:
self.ik = IssuanceKeys(rand.b(32)).ik
for _ in range(rand.u8() % 5):
self.vIssueActions.append(IssueActionDescription(rand, self.ik))
self.issueAuthSig = rand.b(64)
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved

def version_bytes(self):
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
return NU7_TX_VERSION | (1 << 31)

def orchard_zsa_burn_field_bytes(self):
ret = b''
ret += write_compact_size(len(self.vAssetBurnOrchardZSA))
if len(self.vAssetBurnOrchardZSA) > 0:
for desc in self.vAssetBurnOrchardZSA:
ret += bytes(desc)
return ret

def issuance_field_bytes(self):
ret = b''
ret += write_compact_size(len(self.vIssueActions))
if len(self.vIssueActions) > 0:
for desc in self.vIssueActions:
ret += bytes(desc)
ret += self.ik
ret += bytes(self.issueAuthSig)
return ret

def __bytes__(self):
ret = b''

# Fields that are in TransactionBase: Common, Transparent, Sapling, most Orchard
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
ret += super().__bytes__(self.version_bytes(), self.nVersionGroupId, self.nConsensusBranchId)

# OrchardZSA remaining Transaction Fields (if the Orchard bundle exists)
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
if len(self.vActionsOrchard) > 0:
ret += self.orchard_zsa_burn_field_bytes()
ret += bytes(self.bindingSigOrchard)

# ZSA Issuance Fields
vivek-arte marked this conversation as resolved.
Show resolved Hide resolved
ret += self.issuance_field_bytes()

return ret
Loading
Loading