class BotTransactionBaseClass(TransactionBaseClass, SignatureCallbackBase): BOT_FEE_NETWORK_ADDRESS_UPDATE = Currency(value='20 TFT') BOT_FEE_ADDITIONAL_NAME = Currency(value='50 TFT') BOT_FEE_REGISTRATION = Currency(value='90 TFT') BOT_FEE_MONTHLY = Currency(value='10 TFT') MAX_NAMES_PER_BOT = 5 MAX_ADDRESSES_PER_BOT = 10 SPECIFIER_SENDER = BinaryData(value=b'sender', fixed_size=0) SPECIFIER_RECEIVER = BinaryData(value=b'receiver', fixed_size=0) @staticmethod def compute_monthly_bot_fees(months): """ computes the total monthly fees required for the given months, using the given oneCoin value as the currency's unit value. """ fees = BotTransactionBaseClass.BOT_FEE_MONTHLY * months if months < 12: return fees if months < 24: return fees * 0.7 return fees * 0.5 @property @abstractmethod def required_bot_fees(self): """ The bot fees required to pay for this Bot Transaction. """ pass
def __init__(self): self._mint_fulfillment = None self._mint_condition = None self._miner_fees = [] self._data = None self._nonce = BinaryData(crandom(8), strencoding='base64') # current mint condition self._parent_mint_condition = None super().__init__()
def __init__(self): self._mint_fulfillment = None self._coin_outputs = [] self._miner_fees = [] self._data = None self._nonce = BinaryData(generateXByteID(8), strencoding='base64') # current mint condition self._parent_mint_condition = None super().__init__()
def _from_json_data_object(self, data): self._nonce = BinaryData.from_json(data.get('nonce', ''), strencoding='base64') self._mint_condition = conditions.from_json( data.get('mintcondition', {})) self._mint_fulfillment = fulfillments.from_json( data.get('mintfulfillment', {})) self._miner_fees = [ Currency.from_json(fee) for fee in data.get('minerfees', []) or [] ] self._data = BinaryData.from_json(data.get('arbitrarydata', None) or '', strencoding='base64')
def _from_json_data_object(self, data): self._nonce = BinaryData.from_json(data.get('nonce', ''), strencoding='base64') self._mint_fulfillment = FulfillmentTypes.from_json( data.get('mintfulfillment', {})) self._coin_outputs = [ CoinOutput.from_json(co) for co in data.get('coinoutputs', []) or [] ] self._miner_fees = [ Currency.from_json(fee) for fee in data.get('minerfees', []) or [] ] self._data = BinaryData.from_json(data.get('arbitrarydata', None) or '', strencoding='base64')
def data(self): """ Optional binary data attached to this Transaction, with a max length of 83 bytes. """ if self._data is None: return BinaryData(strencoding='base64') return self._data
def test_types(): # currency values can be created from both # int and str values, but are never allowed to be negative jsass.equals(Currency().str(), '0') jsass.equals(Currency(value=123).str(), '123') jsass.equals(Currency(value='1').str(), '1') jsass.equals(Currency(value='0.1').json(), '100000000') # in the string versions you can also add the TFT currency notation, # or use decimal notation to express the currency in the TFT Currency Unit, # rather than the primitive unit jsass.equals(Currency(value='1 TFT').str(), '1') jsass.equals(Currency(value='0.123456789').str(), '0.123456789') jsass.equals(Currency(value='0.123456789').json(), '123456789') jsass.equals(Currency(value='9.123456789').str(), '9.123456789') jsass.equals(Currency(value='1234.34').str(), '1234.34') jsass.equals(Currency(value='1.00000').str(), '1') jsass.equals(Currency(value='1.0 tft').str(), '1') jsass.equals(Currency(value=1).str(), '1') jsass.equals(Currency(value=12344).str(), '12344') # hash values can be created directly from binary data, # or from a hex-encoded string, by default the nil hash will be created jsass.equals(Hash().str(), '0' * 64) jsass.equals( Hash(b'12345678901234567890123456789001').value, b'12345678901234567890123456789001') # binary data is very similar to a hash, # except that it doesn't have a fixed length and it is binary serialized # as a slice, not an array jsass.equals(BinaryData().str(), '') jsass.equals(BinaryData(b'1').str(), '31') jsass.equals(BinaryData(b'1', fixed_size=0).str(), '31') jsass.equals(BinaryData(b'1', fixed_size=1).str(), '31') # raw data is pretty much binary data, except that it is # base64 encoded/decoded for str/json purposes jsass.equals(BinaryData(b'data', strencoding='base64').str(), 'ZGF0YQ==') # block stake values can be created from both # int and str values, but are never allowed to be negative jsass.equals(Blockstake().str(), '0') jsass.equals(Blockstake(value=123).str(), '123') jsass.equals(Blockstake(value='1').str(), '1')
def data(self, value): if value is None: self._data = None return if isinstance(value, BinaryData): value = value.value elif isinstance(value, str): value = value.encode('utf-8') if len(value) > 83: raise ValueError( "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit".format(len(value))) self._data = BinaryData(value=value, strencoding='base64')
def _custom_data_setter(self, value): if value == None: self._data = None return if isinstance(value, BinaryData): value = value.value elif isinstance(value, str): value = jsstr.to_utf8(value) if len(value) > 83: raise ValueError( "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit".format(len(value))) self._data = BinaryData(value=value, strencoding='base64')
def __init__(self): self._coin_inputs = [] self._coin_outputs = [] self._blockstake_inputs = [] self._blockstake_outputs = [] self._miner_fees = [] self._data = BinaryData(strencoding='base64') # hidden flag, that indicates if this Txn was a Legacy v0 Txn or not, # False by default as we do not wish to produce new legacy Txns, only decode existing ones self._legacy = False super().__init__()
def _from_json_data_object(self, data): self._coin_inputs = [CoinInput.from_json( ci) for ci in data.get('coininputs', []) or []] self._coin_outputs = [CoinOutput.from_json( co) for co in data.get('coinoutputs', []) or []] self._blockstake_inputs = [BlockstakeInput.from_json( bsi) for bsi in data.get('blockstakeinputs', []) or []] self._blockstake_outputs = [BlockstakeOutput.from_json( bso) for bso in data.get('blockstakeoutputs', []) or []] self._miner_fees = [Currency.from_json( fee) for fee in data.get('minerfees', []) or []] self._data = BinaryData.from_json( data.get('arbitrarydata', None) or '', strencoding='base64')
def hashed_secret(self): if self._hashed_secret == None: return BinaryData() return self._hashed_secret
class TransactionV129(TransactionBaseClass): _SPECIFIER = b'coin mint tx\0\0\0\0' def __init__(self): self._mint_fulfillment = None self._coin_outputs = [] self._miner_fees = [] self._data = None self._nonce = BinaryData(generateXByteID(8), strencoding='base64') # current mint condition self._parent_mint_condition = None super().__init__() @property def version(self): return TransactionVersion.MINTER_COIN_CREATION @property def miner_fees(self): """ Miner fees, paid to the block creator of this Transaction, funded by this Transaction's coin inputs. """ return self._miner_fees @property def data(self): """ Optional binary data attached to this Transaction, with a max length of 83 bytes. """ if self._data is None: return BinaryData(strencoding='base64') return self._data @data.setter def data(self, value): if value is None: self._data = None return if isinstance(value, BinaryData): value = value.value elif isinstance(value, str): value = value.encode('utf-8') if len(value) > 83: raise ValueError( "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit" .format(len(value))) self._data = BinaryData(value=value, strencoding='base64') @property def coin_outputs(self): """ Coin outputs of this Transaction, funded by the Transaction's coin inputs. """ return self._coin_outputs @coin_outputs.setter def coin_outputs(self, value): self._coin_outputs = [] if not value: return for co in value: self.coin_output_add(co.value, co.condition, id=co.id) def coin_output_add(self, value, condition, id=None): co = CoinOutput(value=value, condition=condition) co.id = id self._coin_outputs.append(co) def miner_fee_add(self, value): self._miner_fees.append(Currency(value=value)) def mint_fulfillment_defined(self): return self._mint_fulfillment is not None @property def mint_fulfillment(self): """ Retrieve the current mint fulfillment """ if self._mint_fulfillment is None: return FulfillmentSingleSignature() return self._mint_fulfillment @mint_fulfillment.setter def mint_fulfillment(self, value): if value is None: self._mint_fulfillment = None return if not isinstance(value, FulfillmentBaseClass): raise TypeError( "CoinCreation (v129) Transaction's mint fulfillment has to be a subtype of FulfillmentBaseClass, not {}" .format(type(value))) self._mint_fulfillment = value @property def parent_mint_condition(self): """ Retrieve the parent mint condition which will be set """ if self._parent_mint_condition is None: return ConditionNil() return self._parent_mint_condition @parent_mint_condition.setter def parent_mint_condition(self, value): if value is None: self._parent_mint_condition = None return if not isinstance(value, ConditionBaseClass): raise TypeError( "CoinCreation (v129) Transaction's parent mint condition has to be a subtype of ConditionBaseClass, not {}" .format(type(value))) self._parent_mint_condition = value 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 _id_input_compute(self): return bytearray( TransactionV129._SPECIFIER) + self._binary_encode_data() 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 _from_json_data_object(self, data): self._nonce = BinaryData.from_json(data.get('nonce', ''), strencoding='base64') self._mint_fulfillment = fulfillments.from_json( data.get('mintfulfillment', {})) self._coin_outputs = [ CoinOutput.from_json(co) for co in data.get('coinoutputs', []) or [] ] self._miner_fees = [ Currency.from_json(fee) for fee in data.get('minerfees', []) or [] ] self._data = BinaryData.from_json(data.get('arbitrarydata', None) or '', strencoding='base64') def _json_data_object(self): return { 'nonce': self._nonce.json(), 'mintfulfillment': self.mint_fulfillment.json(), 'coinoutputs': [co.json() for co in self.coin_outputs], 'minerfees': [fee.json() for fee in self.miner_fees], 'arbitrarydata': self.data.json(), } def _extra_signature_requests_new(self): if self._parent_mint_condition is None: return [] # nothing to be signed return self._mint_fulfillment.signature_requests_new( # no extra objects are to be included within txn scope input_hash_func=self.signature_hash_get, parent_condition=self._parent_mint_condition, ) def _extra_is_fulfilled(self): if self._parent_mint_condition is None: return False return self.mint_fulfillment.is_fulfilled( parent_condition=self._parent_mint_condition)
class TransactionV128(TransactionBaseClass): _SPECIFIER = b'minter defin tx\0' def __init__(self): self._mint_fulfillment = None self._mint_condition = None self._miner_fees = [] self._data = None self._nonce = BinaryData(crandom(8), strencoding='base64') # current mint condition self._parent_mint_condition = None super().__init__() def _custom_version_getter(self): return TransactionVersion.MINTER_DEFINITION def _custom_miner_fees_getter(self): """ Miner fees, paid to the block creator of this Transaction, funded by this Transaction's coin inputs. """ return self._miner_fees def _custom_data_getter(self): """ Optional binary data attached to this Transaction, with a max length of 83 bytes. """ if self._data == None: return BinaryData(strencoding='base64') return self._data def _custom_data_setter(self, value): if value == None: self._data = None return if isinstance(value, BinaryData): value = value.value elif isinstance(value, str): value = jsstr.to_utf8(value) if len(value) > 83: raise ValueError( "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit" .format(len(value))) self._data = BinaryData(value=value, strencoding='base64') @property def mint_condition(self): """ Retrieve the new mint condition which will be set """ if self._mint_condition == None: return ConditionNil() return self._mint_condition @mint_condition.setter def mint_condition(self, value): if value == None: self._mint_condition = None return if not isinstance(value, ConditionBaseClass): raise TypeError( "MintDefinition (v128) Transaction's mint condition has to be a subtype of ConditionBaseClass, not {}" .format(type(value))) self._mint_condition = value @property def parent_mint_condition(self): """ Retrieve the parent mint condition which will be set """ if self._parent_mint_condition == None: return ConditionNil() return self._parent_mint_condition @parent_mint_condition.setter def parent_mint_condition(self, value): if value == None: self._parent_mint_condition = None return if not isinstance(value, ConditionBaseClass): raise TypeError( "MintDefinition (v128) Transaction's parent mint condition has to be a subtype of ConditionBaseClass, not {}" .format(type(value))) self._parent_mint_condition = value def mint_fulfillment_defined(self): return self._mint_fulfillment != None @property def mint_fulfillment(self): """ Retrieve the current mint fulfillment """ if self._mint_fulfillment == None: return FulfillmentSingleSignature() return self._mint_fulfillment @mint_fulfillment.setter def mint_fulfillment(self, value): if value == None: self._mint_fulfillment = None return if not isinstance(value, FulfillmentBaseClass): raise TypeError( "MintDefinition (v128) Transaction's mint fulfillment has to be a subtype of FulfillmentBaseClass, not {}" .format(type(value))) self._mint_fulfillment = value def miner_fee_add(self, value): self._miner_fees.append(Currency(value=value)) def _signature_hash_input_get(self, *extra_objects): e = SiaBinaryEncoder() # encode the transaction version e.add_byte(self.version.value) # encode the specifier e.add_array(TransactionV128._SPECIFIER) # encode nonce e.add_array(self._nonce.value) # extra objects if any if extra_objects: e.add_all(*extra_objects) # encode new mint condition e.add(self.mint_condition) # encode miner fees e.add_slice(self.miner_fees) # encode custom data e.add(self.data) # return the encoded data return e.data def _id_input_compute(self): return jsarr.concat(TransactionV128._SPECIFIER, self._binary_encode_data()) def _binary_encode_data(self): encoder = SiaBinaryEncoder() encoder.add_array(self._nonce.value) encoder.add_all( self.mint_fulfillment, self.mint_condition, self.miner_fees, self.data, ) return encoder.data def _from_json_data_object(self, data): self._nonce = BinaryData.from_json(data.get_or('nonce', ''), strencoding='base64') self._mint_condition = ConditionTypes.from_json( data.get_or('mintcondition', jsobj.new_dict())) self._mint_fulfillment = FulfillmentTypes.from_json( data.get_or('mintfulfillment', jsobj.new_dict())) self._miner_fees = [ Currency.from_json(fee) for fee in data.get_or('minerfees', []) or [] ] self._data = BinaryData.from_json(data.get_or('arbitrarydata', None) or '', strencoding='base64') def _json_data_object(self): return { 'nonce': self._nonce.json(), 'mintfulfillment': self.mint_fulfillment.json(), 'mintcondition': self.mint_condition.json(), 'minerfees': [fee.json() for fee in self._miner_fees], 'arbitrarydata': self.data.json(), } def _extra_signature_requests_new(self): if self._parent_mint_condition == None: return [] # nothing to be signed return self._mint_fulfillment.signature_requests_new( # no extra objects are to be included within txn scope input_hash_func=self.signature_hash_get, parent_condition=self._parent_mint_condition, ) def _extra_is_fulfilled(self): if self._parent_mint_condition == None: return False return self.mint_fulfillment.is_fulfilled( parent_condition=self._parent_mint_condition)
class TransactionV210(TransactionBaseClass, SignatureCallbackBase): _SPECIFIER = b'erc20 addrreg tx' HARDCODED_REGISTRATION_FEE = '10 TFT' SPECIFIER_REGISTRATION_SIGNATURE = BinaryData(value=b'registration', fixed_size=0) def __init__(self): self._public_key = None self._signature = None self._registration_fee = Currency( value=TransactionV210.HARDCODED_REGISTRATION_FEE) self._transaction_fee = None self._coin_inputs = None self._refund_coin_output = None super().__init__() @property def version(self): return TransactionVersion.ERC20_ADDRESS_REGISTRATION @property def public_key(self): if self._public_key is None: return PublicKey() return self._public_key @public_key.setter def public_key(self, value): if value is None: self._public_key = None return if not isinstance(value, PublicKey): raise TypeError( "cannot assign value of type {} as BotRegistration's public key (expected type: PublicKey)" .format(type(value))) self._public_key = PublicKey(specifier=value.specifier, hash=value.hash) @property def signature(self): if self._signature is None: return ED25519Signature(as_array=True) return self._signature @signature.setter def signature(self, value): if value is None: self._signature = None return self._signature = ED25519Signature(value=value, as_array=True) @property def coin_inputs(self): """ Coin inputs of this Transaction, used as funding for coin outputs, fees and any other kind of coin output. """ return self._coin_inputs @coin_inputs.setter def coin_inputs(self, value): self._coin_inputs = [] if not value: return for ci in value: self.coin_input_add(ci.parentid, ci.fulfillment, parent_output=ci.parent_output) @property def refund_coin_output(self): if self._refund_coin_output is None: return CoinOutput() return self._refund_coin_output @property def coin_outputs(self): """ Empty list, or a singleton with the refund coin output if that one exists. """ if self._refund_coin_output is None: return [] return [self._refund_coin_output] @coin_outputs.setter def coin_outputs(self, value): if isinstance(value, list): lvalue = len(value) if lvalue == 0: value = None elif lvalue == 1: value = value[0] else: raise ValueError( "ThreeBot only can have one coin output, a refund coin output" ) if value is None: self._refund_coin_output = None elif isinstance(value, CoinOutput): self._refund_coin_output = CoinOutput(value=value.value, condition=value.condition) self._refund_coin_output.id = value.id else: raise TypeError( "cannot assign a value of type {} to coin outputs".format( type(value))) def coin_input_add(self, parentid, fulfillment, parent_output=None): ci = CoinInput(parentid=parentid, fulfillment=fulfillment) ci.parent_output = parent_output self._coin_inputs.append(ci) def refund_coin_output_set(self, value, condition, id=None): co = CoinOutput(value=value, condition=condition) co.id = id self._refund_coin_output = co @property def registration_fee(self): return self._registration_fee @registration_fee.setter def registration_fee(self, txfee): if txfee is not None: fee = Currency(value=txfee) if fee != self._registration_fee: raise ValueError( "registration fee is hardcoded at {}, cannot be set to {}". format(fee.str(with_unit=True), self._registration_fee.str(with_unit=True))) @property def transaction_fee(self): if self._transaction_fee is None: return Currency() return self._transaction_fee @transaction_fee.setter def transaction_fee(self, txfee): if txfee is None: self._transaction_fee = None else: self._transaction_fee = Currency(value=txfee) @property def miner_fees(self): if self._transaction_fee is None: return [] return [self._transaction_fee] def signature_add(self, public_key, signature): """ Implements SignatureCallbackBase. """ if self._public_key.unlockhash != public_key.unlockhash: raise ValueError( "given public key ({}) does not equal public key ({})".format( str(self._public_key.unlockhash), str(public_key.unlockhash))) self.signature = signature def _signature_hash_input_get(self, *extra_objects): e = encoder_rivine_get() # encode the transaction version e.add_int8(self.version) # encode the specifier e.add_array(TransactionV210._SPECIFIER) # encode the public key e.add_all(self.public_key) # extra objects if any if extra_objects: e.add_all(*extra_objects) # encode coin inputs e.add(len(self.coin_inputs)) for ci in self.coin_inputs: e.add(ci.parentid) # encode registration and transaction fee e.add_all(self.registration_fee, self.transaction_fee) # encode refund coin output if self._refund_coin_output is None: e.add_int8(0) else: e.add_int8(1) e.add(self._refund_coin_output) # return data return e.data def _id_input_compute(self): return bytearray( TransactionV210._SPECIFIER) + self._binary_encode_data() def _binary_encode_data(self): e = encoder_rivine_get() # encode all easy properties e.add_all(self.public_key, self.signature, self.registration_fee, self.transaction_fee, self.coin_inputs) # encode the only "pointer" property if self._refund_coin_output is None: e.add_int8(0) else: e.add_int8(1) e.add(self._refund_coin_output) # return encoded data return e.data def _from_json_data_object(self, data): # decode public key if 'pubkey' in data: self._public_key = PublicKey.from_json(data['pubkey']) else: self._public_key = None # decode signature if 'signature' in data: self._signature = ED25519Signature.from_json(data['signature']) else: self._signature = None # decode registration fee if 'regfee' in data: self.registration_fee = Currency.from_json(data['regfee']) else: self.registration_fee = None # decode transaction fee if 'txfee' in data: self._transaction_fee = Currency.from_json(data['txfee']) else: self._transaction_fee = None # decode coin inputs self._coin_inputs = [ CoinInput.from_json(ci) for ci in data.get('coininputs', []) or [] ] # decode refund coin output (if it exists) if 'refundcoinoutput' in data: self._refund_coin_output = CoinOutput.from_json( data['refundcoinoutput']) else: self._refund_coin_output = None def _json_data_object(self): tftaddress = self.public_key.unlockhash erc20address = ERC20Address.from_unlockhash(tftaddress) output = { 'pubkey': self.public_key.json(), 'tftaddress': tftaddress.json(), 'erc20address': erc20address.json(), 'signature': self.signature.json(), 'regfee': self.registration_fee.json(), 'txfee': self.transaction_fee.json(), 'coininputs': [ci.json() for ci in self.coin_inputs], } if self._refund_coin_output is not None: output['refundcoinoutput'] = self._refund_coin_output.json() return output def _extra_signature_requests_new(self): if self._public_key is None: # if no parent public key is defined, cannot do anything return [] if self._signature is not None: return [] # nothing to do # generate the input hash func input_hash_func = InputSignatureHashFactory( self, TransactionV210.SPECIFIER_REGISTRATION_SIGNATURE ).signature_hash_new # define the input_hash_new generator function, # used to create the input hash for creating the signature unlockhash = self._public_key.unlockhash def input_hash_gen(public_key): return input_hash_func() # create the only signature request return [ SignatureRequest(unlockhash=unlockhash, input_hash_gen=input_hash_gen, callback=self) ] def _extra_is_fulfilled(self): return self._signature is not None
def legacy_from_json(cls, obj): """ Class method to decode v1 Tx from a legacy v0 Tx. """ tv = obj.get('version', -1) if TransactionVersion.LEGACY != tv: raise ValueError("legacy v0 transaction is expected to be of version {}, not version {}".format( TransactionVersion.LEGACY, tv)) txn = cls() if 'data' not in obj: raise ValueError("no data object found in Legacy Transaction (v{})".format( TransactionVersion.LEGACY)) txn_data = obj['data'] if 'coininputs' in txn_data: for legacy_ci_info in (txn_data['coininputs'] or []): unlocker = legacy_ci_info.get('unlocker', {}) ci_info = { 'parentid': legacy_ci_info.get('parentid', ''), 'fulfillment': { 'type': 1, 'data': { 'publickey': unlocker.get('condition', {}).get('publickey'), 'signature': unlocker.get('fulfillment', {}).get('signature'), } } } ci = CoinInput.from_json(ci_info) txn._coin_inputs.append(ci) if 'coinoutputs' in txn_data: for legacy_co_info in (txn_data['coinoutputs'] or []): co_info = { 'value': legacy_co_info.get('value', '0'), 'condition': { 'type': 1, 'data': { 'unlockhash': legacy_co_info.get('unlockhash', ''), } } } co = CoinOutput.from_json(co_info) txn._coin_outputs.append(co) if 'blockstakeinputs' in txn_data: for legacy_bsi_info in (txn_data['blockstakeinputs'] or []): unlocker = legacy_bsi_info.get('unlocker', {}) bsi_info = { 'parentid': legacy_bsi_info.get('parentid', ''), 'fulfillment': { 'type': 1, 'data': { 'publickey': unlocker.get('condition', {}).get('publickey'), 'signature': unlocker.get('fulfillment', {}).get('signature'), } } } bsi = BlockstakeInput.from_json(bsi_info) txn._blockstake_inputs.append(bsi) if 'blockstakeoutputs' in txn_data: for legacy_bso_info in (txn_data['blockstakeoutputs'] or []): bso_info = { 'value': legacy_bso_info.get('value', '0'), 'condition': { 'type': 1, 'data': { 'unlockhash': legacy_bso_info.get('unlockhash', ''), } } } bso = BlockstakeOutput.from_json(bso_info) txn._blockstake_outputs.append(bso) if 'minerfees' in txn_data: for miner_fee in (txn_data['minerfees'] or []): txn._miner_fees.append(Currency.from_json(miner_fee)) if 'arbitrarydata' in txn_data: txn._data = BinaryData.from_json(txn_data.get( 'arbitrarydata', None) or '', strencoding='base64') txn._legacy = True return txn