def verify_proof(proof, index): index = index # This is 1 indexed # TODO: making creating and verifying indexes the same root = proof[-32:] current = proof[0:32] # For all hashes between first and last for i in range(1, len(proof) // 32 - 1): # If the current index is even, # The next hash goes before the current one if index % 2 == 0: current = utils.hash256( proof[i * 32: (i + 1) * 32] + current ) # Halve and floor the index index = index // 2 else: # The next hash goes after the current one current = utils.hash256( current + proof[i * 32: (i + 1) * 32] ) # Halve and ceil the index index = index // 2 + 1 # At the end we should have made the root if current != root: return False return True
def verify_proof(proof: bytes, index: int) -> bool: ''' Verifies a hash256 merkle proof. The proof is encoded as a bytestring. The first 32 bytes are the leaf hash, the last 32 bytes are the roothash. Args: proof (bytes): The merkle proof as a bytestring index (int): The 0-indexed position of the leaf in the leafset Returns: (bool): True if valid proof, else False ''' idx = index length = (len(proof) // 32) - 1 if len(proof) % 32 != 0: return False if len(proof) == 32: return True # Should never occur if len(proof) == 64: return False current = proof[:32] root = proof[-32:] # For all hashes between first and last for i in range(1, length): next = proof[i * 32:i * 32 + 32] if idx % 2 == 1: current = rutils.hash256(next + current) else: current = rutils.hash256(current + next) idx = idx >> 1 return current == root
def _hash_outputs(self, index, sighash_type): '''BIP143 hashOutputs implementation Args: index (int): index of input being signed sighash_type (int): SIGHASH_SINGLE or SIGHASH_ALL Returns: (bytes): the hashOutputs, a 32 byte hash ''' if sighash_type == shared.SIGHASH_ALL: # If the sighash type is ALL, # hashOutputs is the double SHA256 of all output amounts # paired up with their scriptPubKey; outputs = ByteData() for tx_out in self.tx_outs: outputs += tx_out.to_bytes() return utils.hash256(outputs.to_bytes()) elif (sighash_type == shared.SIGHASH_SINGLE and index < len(self.tx_outs)): # if sighash type is SINGLE # and the input index is smaller than the number of outputs, # hashOutputs is the double SHA256 of the output at the same index return utils.hash256(self.tx_outs[index].to_bytes()) else: # Otherwise, hashOutputs is a uint256 of 0x0000......0000 raise NotImplementedError( 'I refuse to implement the SIGHASH_SINGLE bug.')
def verify_proof(proof: bytes, index: int) -> bool: ''' verifies a merkle leaf occurs at a specified index given a merkle proof ''' index = index # This is 1 indexed # TODO: making creating and verifying indexes the same root = proof[-32:] current = proof[0:32] # For all hashes between first and last for i in range(1, len(proof) // 32 - 1): # If the current index is even, # The next hash goes before the current one if index % 2 == 0: current = rutils.hash256(proof[i * 32:(i + 1) * 32] + current) # Halve and floor the index index = index // 2 else: # The next hash goes after the current one current = rutils.hash256(current + proof[i * 32:(i + 1) * 32]) # Halve and ceil the index index = index // 2 + 1 print(current.hex()) # At the end we should have made the root if current != root: return False return True
def create_proof(tx_hashes, index): idx = index - 1 # This is 0-indexed # TODO: making creating and verifying indexes the same hashes = [h for h in tx_hashes] # copy the list proof = bytearray(hashes[idx]) while len(hashes) > 1: next_tree_row = [] # if length is odd, duplicate last entry if len(hashes) % 2 != 0: hashes.append(hashes[-1]) # Append next hash to proof proof += hashes[idx + (1 if idx % 2 == 0 else -1)] # Half the index idx = idx // 2 # Take each pair in order, and hash them for i in range(0, len(hashes), 2): next_tree_row.append(utils.hash256(hashes[i] + hashes[i + 1])) # update tx_hashes hashes = next_tree_row # Put the root on the end proof.extend(hashes[0]) return proof
def validate_header(header: RelayHeader) -> bool: ''' Verifies a bitcoin header Args: header (RelayHeader): The header as an object Returns: (bool): True if valid header, else False ''' # Check that HashLE is the correct hash of the raw header header_hash = rutils.hash256(header['raw']) if header_hash != header['hash']: return False # Check that the MerkleRootLE is the correct MerkleRoot for the header extracted_merkle_root = extract_merkle_root_le(header['raw']) if extracted_merkle_root != header['merkle_root']: return False # Check that PrevHash is the correct PrevHash for the header extracted_prevhash = extract_prev_block_le(header['raw']) if extracted_prevhash != header['prevhash']: return False return True
def parse_header(header: str) -> Header: ''' Parses a header to a dict Args: header (str): hex formatted 80 byte header Returns: dict: hash (str): the header hash 0000-first version (int): the block version as an int prev_block (str): the previous block hash 0000-first merkle_root (str): the block transaction merkle tree root timestamp (int): the block header timestamp nbits (str): the difficulty bits nonce (str): the nonce difficulty (int): the difficulty as an int hex (str): the full header as hex height (int): the block height (always 0) ''' if len(header) != 160: raise ValueError('Invalid header received') as_bytes = bytes.fromhex(header) nbits = as_bytes[72:76] return { 'hash': rutils.hash256(bytes.fromhex(header))[::-1].hex(), 'version': rutils.le2i(as_bytes[0:4]), 'prev_block': as_bytes[4:36][::-1].hex(), 'merkle_root': as_bytes[36:68].hex(), 'timestamp': rutils.le2i(as_bytes[68:72]), 'nbits': nbits.hex(), 'nonce': as_bytes[76:80].hex(), 'difficulty': parse_difficulty(nbits), 'hex': header, 'height': 0, 'accumulated_work': 0 }
def validate_spvproof(proof: SPVProof) -> bool: ''' Verifies an SPV proof object Args: proof (SPVProof): The SPV Proof as an object Returns: (bool): True if valid proof, else False ''' if not validate_vin(proof['vin']): return False if not validate_vout(proof['vout']): return False tx_id = rutils.hash256(proof['version'] + proof['vin'] + proof['vout'] + proof['locktime']) if tx_id != proof['tx_id_le']: return False if not validate_header(proof['confirming_header']): return False valid_proof = prove(proof['tx_id_le'], proof['confirming_header']['merkle_root_le'], proof['intermediate_nodes'], proof['index']) if not valid_proof: return False return True
def encode(data: bytes, checksum: bool = True) -> str: '''Convert binary to base58 using BASE58_ALPHABET.''' if checksum: data = data + utils.hash256(data)[:4] v, prefix = to_long(256, lambda x: x, data) data = from_long(v, prefix, BASE58_BASE, lambda v: BASE58_ALPHABET[v]) return data.decode("utf8")
def _sighash_final_hashing(self, copy_tx, sighash_type): ''' SproutTx, int -> bytes Returns the hash that should be signed https://en.bitcoin.it/wiki/OP_CHECKSIG#Procedure_for_Hashtype_SIGHASH_ANYONECANPAY ''' sighash = z.ZcashByteData() sighash += copy_tx.to_bytes() sighash += utils.i2le_padded(sighash_type, 4) return utils.hash256(sighash.to_bytes())
def _hash_prevouts(self, anyone_can_pay): if anyone_can_pay: # If the ANYONECANPAY flag is set, # hashPrevouts is a uint256 of 0x0000......0000. hash_prevouts = b'\x00' * 32 else: # hashPrevouts is the double SHA256 of all outpoints; outpoints = ByteData() for tx_in in self.tx_ins: outpoints += tx_in.outpoint hash_prevouts = utils.hash256(outpoints.to_bytes()) return hash_prevouts
def segwit_sighash(self, index: int, sighash_type: int, prevout_value: bytes, script: bytes, anyone_can_pay: bool = False) -> bytes: ''' Implements bip143 (witness) sighash. Prefer calling `sighash_all` or `sighash_single`. For documentation see here: https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki For an excellent pasta dinner, see here: https://ricette.giallozafferano.it/Spaghetti-alla-Norma.html ''' data = ByteData() # 1. nVersion of the transaction (4-byte little endian) data += self.version # 2. hashPrevouts (32-byte hash) data += self._hash_prevouts(anyone_can_pay=anyone_can_pay) # 3. hashSequence (32-byte hash) data += self._hash_sequence(sighash_type=sighash_type, anyone_can_pay=anyone_can_pay) # 4. outpoint (32-byte hash + 4-byte little endian) data += self.tx_ins[index].outpoint # 5. scriptCode of the input (serialized as scripts inside CTxOuts) data += script # 6. value of the output spent by this input (8-byte little endian) data += prevout_value # 7. nSequence of the input (4-byte little endian) data += self.tx_ins[index].sequence # 8. hashOutputs (32-byte hash) data += self._hash_outputs(index=index, sighash_type=sighash_type) # 9. nLocktime of the transaction (4-byte little endian) data += self.lock_time # 10. sighash type of the signature (4-byte little endian) data += self._segwit_sighash_adjustment(sighash_type=sighash_type, anyone_can_pay=anyone_can_pay) return utils.hash256(data.to_bytes())
def decode(s: str, checksum: bool = True) -> bytes: '''Convert base58 to binary using BASE58_ALPHABET.''' v, prefix = to_long( BASE58_BASE, lambda c: BASE58_LOOKUP[c], s.encode("utf8")) data = from_long(v, prefix, 256, lambda x: x) if checksum: data, the_hash = data[:-4], data[-4:] if utils.hash256(data)[:4] == the_hash: return data raise ValueError("hashed base58 has bad checksum %s" % s) return data
def do_work(prev_block_hash_be, merkle_root, nbits): nonce = 0 b_header = make_header(bytes.fromhex(prev_block_hash_be), bytes.fromhex(merkle_root), bytes.fromhex(nbits)) target = parse_nbits(nbits) while True: print("Checking nonce {}".format(nonce), end='\r') # noqa: E999 b_header_nonced = b_header + rutils.i2le_padded(nonce, 4) b_header_digest = rutils.hash256(b_header_nonced) if rutils.be2i(b_header_digest[::-1]) < target: return b_header_nonced, b_header_digest nonce = nonce + 1
def _sighash_forkid( self, index: int, script: bytes, prevout_value: bytes, sighash_type: int, anyone_can_pay: bool = False): ''' Tx, int, byte-like, byte-like, int, bool -> bytes https://github.com/bitcoincashorg/spec/blob/master/replay-protected-sighash.md ''' self.validate_bytes(prevout_value, 8) data = ByteData() # 1. nVersion of the transaction (4-byte little endian) data += self.version # 2. hashPrevouts (32-byte hash) data += self._hash_prevouts(anyone_can_pay=anyone_can_pay) # 3. hashSequence (32-byte hash) data += self._hash_sequence(sighash_type=sighash_type, anyone_can_pay=anyone_can_pay) # 4. outpoint (32-byte hash + 4-byte little endian) data += self.tx_ins[index].outpoint # 5. scriptCode of the input (serialized as scripts inside CTxOuts) data += script # 6. value of the output spent by this input (8-byte little endian) data += prevout_value # 7. nSequence of the input (4-byte little endian) data += self.tx_ins[index].sequence # 8. hashOutputs (32-byte hash) data += self._hash_outputs(index=index, sighash_type=sighash_type) # 9. nLocktime of the transaction (4-byte little endian) data += self.lock_time # 10. sighash type of the signature (4-byte little endian) data += self._forkid_sighash_adjustment(sighash_type=sighash_type, anyone_can_pay=anyone_can_pay) return utils.hash256(data.to_bytes())
def segwit_sighash(self, index, script, prevout_value=None, sighash_type=None, anyone_can_pay=False): ''' this function sets up sighash in BIP143 style https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki https://ricette.giallozafferano.it/Spaghetti-alla-Norma.html ''' data = ByteData() # 1. nVersion of the transaction (4-byte little endian) data += self.version # 2. hashPrevouts (32-byte hash) data += self._hash_prevouts(anyone_can_pay=anyone_can_pay) # 3. hashSequence (32-byte hash) data += self._hash_sequence(sighash_type=sighash_type, anyone_can_pay=anyone_can_pay) # 4. outpoint (32-byte hash + 4-byte little endian) data += self.tx_ins[index].outpoint # 5. scriptCode of the input (serialized as scripts inside CTxOuts) data += self._adjusted_script_code(script=script) # 6. value of the output spent by this input (8-byte little endian) data += prevout_value # 7. nSequence of the input (4-byte little endian) data += self.tx_ins[index].sequence # 8. hashOutputs (32-byte hash) data += self._hash_outputs(index=index, sighash_type=sighash_type) # 9. nLocktime of the transaction (4-byte little endian) data += self.lock_time # 10. sighash type of the signature (4-byte little endian) data += self._segwit_sighash_adjustment(sighash_type=sighash_type, anyone_can_pay=anyone_can_pay) return utils.hash256(data.to_bytes())
def _hash_sequence(self, sighash_type, anyone_can_pay): '''BIP143 hashSequence implementation Args: sighash_type (int): SIGHASH_SINGLE or SIGHASH_ALL anyone_can_pay (bool): true if ANYONECANPAY should be set Returns: (bytes): the hashSequence, a 32 byte hash ''' if anyone_can_pay or sighash_type == shared.SIGHASH_SINGLE: # If any of ANYONECANPAY, SINGLE sighash type is set, # hashSequence is a uint256 of 0x0000......0000. return b'\x00' * 32 else: # hashSequence is the double SHA256 of nSequence of all inputs; sequences = ByteData() for tx_in in self.tx_ins: sequences += tx_in.sequence return utils.hash256(sequences.to_bytes())
def __init__(self, tx_ins, tx_outs, lock_time, expiry_height, value_balance, tx_shielded_spends, tx_shielded_outputs, tx_joinsplits, joinsplit_pubkey, joinsplit_sig, binding_sig): super().__init__() if 'sapling' not in riemann.get_current_network_name(): raise ValueError( 'SaplingTx not supported by network {}.' .format(riemann.get_current_network_name())) self.validate_bytes(lock_time, 4) self.validate_bytes(expiry_height, 4) self.validate_bytes(value_balance, 8) if utils.le2i(expiry_height) > 499999999: raise ValueError('Expiry time too high.' 'Expected <= 499999999. Got {}' .format(utils.le2i(expiry_height))) if (len(tx_shielded_spends) + len(tx_shielded_outputs) == 0 and value_balance != b'\x00' * 8): raise ValueError('If no shielded inputs or outputs, value balance ' 'must be 8 0-bytes. Got {}' .format(value_balance.hex())) elif binding_sig is not None: self.validate_bytes(binding_sig, 64) for tx_in in tx_ins: if not isinstance(tx_in, TxIn): raise ValueError( 'Invalid TxIn. ' 'Expected instance of TxOut. Got {}' .format(type(tx_in).__name__)) for tx_out in tx_outs: if not isinstance(tx_out, TxOut): raise ValueError( 'Invalid TxOut. ' 'Expected instance of TxOut. Got {}' .format(type(tx_out).__name__)) if len(tx_joinsplits) > 5: raise ValueError('Too many joinsplits. Stop that.') for shielded_spend in tx_shielded_spends: if not isinstance(shielded_spend, SaplingShieldedSpend): raise ValueError( 'Invalid shielded spend. ' 'Expected instance of SaplingShieldedSpend. Got {}' .format(type(shielded_spend).__name__)) for shielded_output in tx_shielded_outputs: if not isinstance(shielded_output, SaplingShieldedOutput): raise ValueError( 'Invalid shielded output. ' 'Expected instance of SaplingShieldedOutput. Got {}' .format(type(shielded_output).__name__)) for tx_joinsplit in tx_joinsplits: if not isinstance(tx_joinsplit, SaplingJoinsplit): raise ValueError( 'Invalid Joinsplit. ' 'Expected instance of SaplingJoinsplit. Got {}' .format(type(tx_joinsplit).__name__)) if len(tx_joinsplits) != 0: self.validate_bytes(joinsplit_pubkey, 32) self.validate_bytes(joinsplit_sig, 64) if len(tx_joinsplits) + len(tx_ins) + len(tx_shielded_spends) == 0: raise ValueError('Transaction must have some input value.') self += b'\x04\x00\x00\x00' # Sapling is always v4 self += b'\x85\x20\x2f\x89' # Sapling version group id self += shared.VarInt(len(tx_ins)) for tx_in in tx_ins: self += tx_in self += shared.VarInt(len(tx_outs)) for tx_out in tx_outs: self += tx_out self += lock_time self += expiry_height self += value_balance self += shared.VarInt(len(tx_shielded_spends)) if len(tx_shielded_spends) != 0: for shielded_spend in tx_shielded_spends: self += shielded_spend self += shared.VarInt(len(tx_shielded_outputs)) if len(tx_shielded_outputs) != 0: for shielded_output in tx_shielded_outputs: self += shielded_output self += shared.VarInt(len(tx_joinsplits)) if len(tx_joinsplits) != 0: for tx_joinsplit in tx_joinsplits: self += tx_joinsplit self += joinsplit_pubkey self += joinsplit_sig if len(tx_shielded_outputs) + len(tx_shielded_spends) != 0: self += binding_sig self.binding_sig = binding_sig self.header = b'\x04\x00\x00\x80' # Sapling is always v4 self.group_id = b'\x85\x20\x2f\x89' # Sapling version group id self.tx_ins = tuple(tx_in for tx_in in tx_ins) self.tx_outs = tuple(tx_out for tx_out in tx_outs) self.lock_time = lock_time self.expiry_height = expiry_height self.value_balance = value_balance if len(tx_shielded_spends) != 0: self.tx_shielded_spends = tuple(ss for ss in tx_shielded_spends) else: self.tx_shielded_spends = tuple() if len(tx_shielded_outputs) != 0: self.tx_shielded_outputs = tuple(so for so in tx_shielded_outputs) else: self.tx_shielded_outputs = tuple() if len(tx_joinsplits) != 0: self.tx_joinsplits = tuple(js for js in tx_joinsplits) self.joinsplit_pubkey = joinsplit_pubkey self.joinsplit_sig = joinsplit_sig # Zcash spec 5.4.1.4 Hsig hash function self.hsigs = (tuple(self._hsig(i) for i in range(len(self.tx_joinsplits)))) self.primary_inputs = (tuple(self._primary_input(i) for i in range(len(self.tx_joinsplits)))) else: self.tx_joinsplits = tuple() self.joinsplit_pubkey = None self.joinsplit_sig = None self.hsigs = tuple() self.primary_inputs = tuple() if len(tx_shielded_outputs) + len(tx_shielded_spends) != 0: self.binding_sig = binding_sig else: self.binding_sig = None self.tx_id_le = self.tx_id_le = utils.hash256(self.to_bytes()) self.tx_id = self.tx_id_le[::-1] self._make_immutable() if len(self) > 100000: raise ValueError( # pragma: no cover 'Tx is too large. ' 'Expect less than 100kB. Got: {} bytes'.format(len(self)))
def __init__(self, version, flag, tx_ins, tx_outs, tx_witnesses, lock_time): super().__init__() self.validate_bytes(version, 4) self.validate_bytes(lock_time, 4) if flag is not None: if flag != riemann.network.SEGWIT_TX_FLAG: raise ValueError('Invald segwit flag. ' 'Expected None or {}. Got: {}'.format( riemann.network.SEGWIT_TX_FLAG, flag)) if tx_witnesses is not None: if flag is None: raise ValueError('Got witnesses but no segwit flag.') if len(tx_witnesses) != len(tx_ins): raise ValueError('Witness and TxIn lists must be same length. ' 'Got {} inputs and {} witnesses.'.format( len(tx_ins), len(tx_witnesses))) for witness in tx_witnesses: if not isinstance(witness, InputWitness): raise ValueError( 'Invalid InputWitness. ' 'Expected instance of InputWitness. Got {}'.format( type(witness))) if min(len(tx_ins), len(tx_outs)) == 0: raise ValueError('Too few inputs or outputs. Stop that.') for tx_in in tx_ins: if not isinstance(tx_in, TxIn): raise ValueError('Invalid TxIn. ' 'Expected instance of TxIn. Got {}'.format( type(tx_in).__name__)) for tx_out in tx_outs: if not isinstance(tx_out, TxOut): raise ValueError('Invalid TxOut. ' 'Expected instance of TxOut. Got {}'.format( type(tx_out).__name__)) self += version if flag is not None: self += flag self += VarInt(len(tx_ins)) for tx_in in tx_ins: self += tx_in self += VarInt(len(tx_outs)) for tx_out in tx_outs: self += tx_out if tx_witnesses is not None: for witness in tx_witnesses: self += witness self += lock_time self.version = version self.flag = flag self.tx_ins_len = len(tx_ins) self.tx_ins = tuple(tx_in for tx_in in tx_ins) self.tx_outs_len = len(tx_outs) self.tx_outs = tuple(tx_out for tx_out in tx_outs) self.tx_witnesses_len = self.tx_ins_len self.tx_witnesses = \ tuple(wit for wit in tx_witnesses) if tx_witnesses is not None \ else None self.lock_time = lock_time if flag is not None: self.tx_id_le = utils.hash256(self.no_witness()) self.wtx_id_le = utils.hash256(self.to_bytes()) self.tx_id = utils.change_endianness(self.tx_id_le) self.wtx_id = utils.change_endianness(self.wtx_id_le) else: self.tx_id_le = utils.hash256(self.to_bytes()) self.tx_id = utils.change_endianness(self.tx_id_le) self.wtx_id = None self.wtx_le = None self._make_immutable()
def __init__(self, version, tx_ins, tx_outs, lock_time, tx_joinsplits, joinsplit_pubkey, joinsplit_sig): super().__init__() if 'sprout' not in riemann.get_current_network_name(): raise ValueError('SproutTx not supported by network {}.'.format( riemann.get_current_network_name())) self.validate_bytes(version, 4) self.validate_bytes(lock_time, 4) for tx_in in tx_ins: if not isinstance(tx_in, TxIn): raise ValueError('Invalid TxIn. ' 'Expected instance of TxOut. Got {}'.format( type(tx_in).__name__)) for tx_out in tx_outs: if not isinstance(tx_out, TxOut): raise ValueError('Invalid TxOut. ' 'Expected instance of TxOut. Got {}'.format( type(tx_out).__name__)) if utils.le2i(version) == 1: if tx_joinsplits is not None and len(tx_joinsplits) != 0: raise ValueError('Joinsplits not allowed in version 1 txns.') if tx_ins is None or len(tx_ins) == 0: raise ValueError('Version 1 txns must have at least 1 input.') if utils.le2i(version) == 2: if len(tx_joinsplits) > 5: raise ValueError('Too many joinsplits. Stop that.') for tx_joinsplit in tx_joinsplits: if not isinstance(tx_joinsplit, z.SproutJoinsplit): raise ValueError( 'Invalid Joinsplit. ' 'Expected instance of SproutJoinsplit. Got {}'.format( type(tx_joinsplit).__name__)) self.validate_bytes(joinsplit_pubkey, 32) if joinsplit_sig is not None and joinsplit_sig != b'': self.validate_bytes(joinsplit_sig, 64) if utils.le2i(version) not in [1, 2]: raise ValueError('Version must be 1 or 2. ' 'Got: {}'.format(utils.le2i(version))) self += version self += shared.VarInt(len(tx_ins)) for tx_in in tx_ins: self += tx_in self += shared.VarInt(len(tx_outs)) for tx_out in tx_outs: self += tx_out self += lock_time if version == utils.i2le_padded(2, 4): self += shared.VarInt(len(tx_joinsplits)) for tx_joinsplit in tx_joinsplits: self += tx_joinsplit self += joinsplit_pubkey self += joinsplit_sig self.version = version self.tx_ins = tuple(tx_in for tx_in in tx_ins) self.tx_outs = tuple(tx_out for tx_out in tx_outs) self.tx_joinsplits = tuple(js for js in tx_joinsplits) self.lock_time = lock_time if version == utils.i2le_padded(2, 4): self.joinsplit_pubkey = joinsplit_pubkey self.joinsplit_sig = joinsplit_sig # Zcash spec 5.4.1.4 Hsig hash function self.hsigs = (tuple( self._hsig(i) for i in range(len(self.tx_joinsplits)))) self.primary_inputs = (tuple( self._primary_input(i) for i in range(len(self.tx_joinsplits)))) else: self.joinsplit_pubkey = None self.joinsplit_sig = None self.hsigs = None self.primary_inputs = None self.tx_id_le = utils.hash256(self.to_bytes()).hex() self.tx_id = utils.hash256(self.to_bytes())[::-1].hex() self._make_immutable() if len(self) > 100000: raise ValueError( # pragma: no cover 'Tx is too large. ' 'Expect less than 100kB. Got: {} bytes'.format(len(self)))
def __init__(self, tx_ins, tx_outs, lock_time, expiry_height, tx_joinsplits, joinsplit_pubkey, joinsplit_sig): super().__init__() if 'overwinter' not in riemann.get_current_network_name(): raise ValueError( 'OverwinterTx not supported by network {}.' .format(riemann.get_current_network_name())) self.validate_bytes(lock_time, 4) self.validate_bytes(expiry_height, 4) if utils.le2i(expiry_height) > 499999999: raise ValueError('Expiry time too high.' 'Expected <= 499999999. Got {}' .format(utils.le2i(expiry_height))) for tx_in in tx_ins: if not isinstance(tx_in, TxIn): raise ValueError( 'Invalid TxIn. ' 'Expected instance of TxIn. Got {}' .format(type(tx_in).__name__)) for tx_out in tx_outs: if not isinstance(tx_out, TxOut): raise ValueError( 'Invalid TxOut. ' 'Expected instance of TxOut. Got {}' .format(type(tx_out).__name__)) if len(tx_joinsplits) > 5: raise ValueError('Too many joinsplits. Stop that.') for tx_joinsplit in tx_joinsplits: if not isinstance(tx_joinsplit, z.SproutJoinsplit): raise ValueError( 'Invalid Joinsplit. ' 'Expected instance of SproutJoinsplit. Got {}' .format(type(tx_joinsplit).__name__)) if len(tx_joinsplits) != 0: self.validate_bytes(joinsplit_pubkey, 32) self.validate_bytes(joinsplit_sig, 64) if len(tx_joinsplits) == 0 and len(tx_ins) == 0: raise ValueError('Transaction must have tx_ins or joinsplits.') self += b'\x03\x00\x00\x80' # Version 3 + fOverwintered self += b'\x70\x82\xc4\x03' # Overwinter Group ID self += shared.VarInt(len(tx_ins)) for tx_in in tx_ins: self += tx_in self += shared.VarInt(len(tx_outs)) for tx_out in tx_outs: self += tx_out self += lock_time self += expiry_height self += shared.VarInt(len(tx_joinsplits)) if len(tx_joinsplits) != 0: for tx_joinsplit in tx_joinsplits: self += tx_joinsplit self += joinsplit_pubkey self += joinsplit_sig self.header = b'\x03\x00\x00\x80' self.group_id = b'\x70\x82\xc4\x03' self.version = b'\x03\x00' self.tx_ins = tuple(tx_in for tx_in in tx_ins) self.tx_outs = tuple(tx_out for tx_out in tx_outs) self.lock_time = lock_time self.expiry_height = expiry_height if len(tx_joinsplits) != 0: self.tx_joinsplits = tuple(js for js in tx_joinsplits) self.joinsplit_pubkey = joinsplit_pubkey self.joinsplit_sig = joinsplit_sig # Zcash spec 5.4.1.4 Hsig hash function self.hsigs = (tuple(self._hsig(i) for i in range(len(self.tx_joinsplits)))) self.primary_inputs = (tuple(self._primary_input(i) for i in range(len(self.tx_joinsplits)))) else: self.tx_joinsplits = tuple() self.joinsplit_pubkey = None self.joinsplit_sig = None self.hsigs = tuple() self.primary_inputs = tuple() self.tx_id_le = self.tx_id_le = utils.hash256(self.to_bytes()) self.tx_id = self.tx_id_le[::-1] self._make_immutable() if len(self) > 100000: raise ValueError( # pragma: no cover 'Tx is too large. ' 'Expect less than 100kB. Got: {} bytes'.format(len(self)))