def _binary_encode_data(self): if not self._legacy: return super()._binary_encode_data() # encoding was slightly different in legacy transactions (v0) # (NOTE: we only support the subset of v0 transactions that are actually active on the tfchain network) encoder = encoder_sia_get() # > encode coin inputs encoder.add_int(len(self.coin_inputs)) for ci in self.coin_inputs: encoder.add(ci.parentid) encoder.add_array(bytearray([1])) # FulfillmentTypeSingleSignature sub_encoder = encoder_sia_get() sub_encoder.add(ci.fulfillment.public_key) encoder.add_slice(sub_encoder.data) encoder.add(ci.fulfillment.signature) # > encode coin outputs encoder.add_int(len(self.coin_outputs)) for co in self.coin_outputs: encoder.add_all(co.value, co.condition.unlockhash) # > encode block stake inputs encoder.add_int(len(self._blockstake_inputs)) for bsi in self._blockstake_inputs: encoder.add(bsi.parentid) encoder.add_array(bytearray([1])) # FulfillmentTypeSingleSignature sub_encoder = encoder_sia_get() sub_encoder.add(bsi.fulfillment.public_key) encoder.add_slice(sub_encoder.data) encoder.add(bsi.fulfillment.signature) # > encode block stake outputs encoder.add_int(len(self._blockstake_outputs)) for bso in self.blockstake_outputs: encoder.add_all(bso.value, bso.condition.unlockhash) # > encode miner fees and arbitrary data encoder.add_all(self.miner_fees, self.data) return encoder.data
def unlockhash(self): """ Return the unlock hash generated from this public key. """ e = encoder_sia_get() self.sia_binary_encode(e) # need to encode again to add the length data = e.data e = encoder_sia_get() e.add_slice(data) hash = bytearray.fromhex(blake2_string(e.data)) return UnlockHash(type=UnlockHashType.PUBLIC_KEY, hash=hash)
def test_sia_custom(): e = encoder_sia_get() # a class that provides a custom encoding logic for its types, # required in order to be able to encode Python objects class Answer(BaseSiaObjectEncoder): def __init__(self, number=0): self._number = number def sia_binary_encode(self, encoder): if self._number == 42: return encoder.add(True) return encoder.add(False) # when we add our objects they will be encoded # using the method as provided by its type e.add(Answer()) e.add(Answer(42)) # this works for slices and arrays as well e.add_array([Answer(5), Answer(2)]) # the result is a single bytearray assert e.data == b'\x00\x01\x00\x00' # everything has limits, so do types, # that is what this test is about # no integer can be negative with pytest.raises(IntegerOutOfRange): e.add(-1) # integers have an upper bound with pytest.raises(IntegerOutOfRange): e.add(1 << 64)
def _signature_hash_input_get(self, *extra_objects): e = encoder_sia_get() # encode the transaction version e.add_byte(self.version) # encode the specifier e.add_array(TransactionV129._SPECIFIER) # encode nonce e.add_array(self._nonce.value) # extra objects if any if extra_objects: e.add_all(*extra_objects) # encode coin outputs e.add_slice(self.coin_outputs) # encode miner fees e.add_slice(self.miner_fees) # encode custom data e.add(self.data) # return the encoded data return e.data
def _outputid_new(self, specifier, index): encoder = encoder_sia_get() encoder.add_array(specifier) encoder.add_array(self._id_input_compute()) encoder.add_int(index) hash = bytearray.fromhex(blake2_string(encoder.data)) return Hash(value=hash)
def _legacy_signature_hash_input_get(self, *extra_objects): e = encoder_sia_get() # encode extra objects if exists if extra_objects: e.add_all(*extra_objects) # encode coin inputs for ci in self.coin_inputs: e.add_all(ci.parentid, ci.fulfillment.public_key.unlockhash) # encode coin outputs e.add(len(self.coin_outputs)) for co in self.coin_outputs: e.add_all(co.value, co.condition.unlockhash) # encode blockstake inputs for bsi in self.blockstake_inputs: e.add_all(bsi.parentid, bsi.fulfillment.public_key.unlockhash) # encode blockstake outputs e.add(len(self.blockstake_outputs)) for bso in self.blockstake_outputs: e.add_all(bso.value, bso.condition.unlockhash) # encode miner fees e.add_slice(self.miner_fees) # encode custom data e.add(self.data) # return the encoded data return e.data
def sia_binary_encode(self, encoder): """ Encode this Fulfillment according to the Sia Binary Encoding format. """ encoder.add_array(bytearray([int(self.type)])) data_enc = encoder_sia_get() self.sia_binary_encode_data(data_enc) encoder.add_slice(data_enc.data)
def from_unlockhash(cls, unlockhash): """ Create an ERC20 Address from a TFT Address (type: UnlockHash). """ e = encoder_sia_get() unlockhash.sia_binary_encode(e) hash = bytes.fromhex(blake2_string(e.data)) return cls(value=hash[Hash.SIZE - ERC20Address.SIZE:])
def unlockhash(self): e = encoder_rivine_get() self.sia_binary_encode_data(e) # need to encode again to add the length data = e.data e = encoder_sia_get() e.add_slice(data) hash = bytearray.fromhex(blake2_string(e.data)) return UnlockHash(type=UnlockHashType.ATOMIC_SWAP, hash=hash)
def _binary_encode_data(self): encoder = encoder_sia_get() encoder.add_array(self._nonce.value) encoder.add_all( self.mint_fulfillment, self.coin_outputs, self.miner_fees, self.data, ) return encoder.data
def binary_encode(self): """ Binary encoding of a Transaction, overriden to specify the version correctly """ if self._legacy: return bytearray([int(TransactionVersion.LEGACY)]) + self._binary_encode_data() encoder = encoder_sia_get() encoder.add_array(bytearray([int(TransactionVersion.STANDARD)])) encoder.add_slice(self._binary_encode_data()) return encoder.data
def test_sia_basic_encoding(): e = encoder_sia_get() # you can add integers, booleans, iterateble objects, strings, # bytes and byte arrays. Dictionaries and objects are not supported. e.add(False) e.add("a") e.add([1, True, "foo"]) e.add(b"123") # the result is a single bytearray assert e.data == b'\x00\x01\x00\x00\x00\x00\x00\x00\x00a\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00foo\x03\x00\x00\x00\x00\x00\x00\x00123'
def test_sia_custom(): e = encoder_sia_get() # everything has limits, so do types, # that is what this test is about # no integer can be negative with pytest.raises(IntegerOutOfRange): e.add(-1) # integers have an upper bound with pytest.raises(IntegerOutOfRange): e.add(1 << 64)
def _binary_encode_data(self): """ Default Binary encoding of a Transaction Data, can be overriden if required. """ encoder = encoder_sia_get() encoder.add_all( self.coin_inputs, self.coin_outputs, self.blockstake_inputs, self.blockstake_outputs, self.miner_fees, self.data, ) return encoder.data
def _signature_hash_input_get(self, *extra_objects): if self._legacy: return self._legacy_signature_hash_input_get(*extra_objects) e = encoder_sia_get() # encode the transaction version e.add_byte(self.version) # encode extra objects if exists if extra_objects: e.add_all(*extra_objects) # encode the number of coins inputs e.add(len(self.coin_inputs)) # encode coin inputs parent_ids for ci in self.coin_inputs: e.add(ci.parentid) # encode coin outputs e.add_slice(self.coin_outputs) # encode the number of blockstake inputs e.add(len(self.blockstake_inputs)) # encode blockstake inputs parent_ids for bsi in self.blockstake_inputs: e.add(bsi.parentid) # encode blockstake outputs e.add_slice(self.blockstake_outputs) # encode miner fees e.add_slice(self.miner_fees) # encode custom data e.add(self.data) # return the encoded data return e.data
def test_sia_types(): e = encoder_sia_get() # in the sia_basic test we saw we can # serialise anything using the add method. # by default strings, byte arrays and iterateable objects # are encoded as slices. # # array are like slices, but have no length prefix, # therefore this is only useful if there is a fixed amount of elements, # known by all parties e.add_array([False, True, True]) # a single byte can be added as well e.add_byte(6) e.add_byte('4') e.add_byte(b'2') # the result is a single bytearray assert e.data == b'\x00\x01\x01\x0642'
def test_sia_custom(): e = encoder_sia_get() # a class that provides a custom encoding logic for its types, # required in order to be able to encode Python objects class Answer(BaseSiaObjectEncoder): def __init__(self, number=0): self._number = number def sia_binary_encode(self, encoder): if self._number == 42: return encoder.add(True) return encoder.add(False) # when we add our objects they will be encoded # using the method as provided by its type e.add(Answer()) e.add(Answer(42)) # this works for slices and arrays as well e.add_array([Answer(5), Answer(2)]) # the result is a single bytearray assert e.data == b'\x00\x01\x00\x00'
def _signature_hash_input_get(self, *extra_objects): e = encoder_sia_get() # encode the transaction version e.add_array(bytearray([int(self.version)])) # encode the specifier e.add_array(TransactionV209._SPECIFIER) # extra objects if any if extra_objects: e.add_all(*extra_objects) # encode the address and value e.add_all(self.address, self.value) # encode transaction fee e.add_all(self.transaction_fee) # encode the block- and transaction identifier e.add_all(self.blockid, self.transactionid) # return data return e.data
def test_sia_encoded(obj, expected): test_encoded(encoder_sia_get(), obj, expected)