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 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 __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 __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 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 hashed_secret(self): if self._hashed_secret == None: return BinaryData() return self._hashed_secret
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