def _internal_transaction_put(self, transaction: payloads.Transaction, batch: WriteBatch = None) -> None: if batch: batch.put(self.TX, transaction.hash(), transaction) else: self.db[self.TX][transaction.hash()] = transaction
def _internal_transaction_put(self, transaction: payloads.Transaction, batch=None): if batch: db = batch else: db = self._real_db with serialization.BinaryWriter() as bw: transaction.serialize_special(bw) serialized_tx = bw.to_array() db.put(DBPrefixes.TRANSACTIONS + transaction.hash().to_array(), serialized_tx)
def sign_tx(self, tx: payloads.Transaction, password: str, magic: Optional[int] = None) -> None: """ Helper function that signs the TX, adds the Witness and Sender Args: tx: transaction to sign password: the password to decrypt the private key for signing magic: the network magic Raises: ValueError: if transaction validation fails """ if magic is None: magic = settings.network.magic self._validate_tx(tx) message = magic.to_bytes(4, byteorder="little", signed=False) + tx.hash().to_array() signature = self.sign(message, password) invocation_script = vm.ScriptBuilder().emit_push(signature).to_array() # mypy can't infer that the is_watchonly check ensures public_key has a value verification_script = contracts.Contract.create_signature_redeemscript( self.public_key) # type: ignore tx.witnesses.insert( 0, payloads.Witness(invocation_script, verification_script))
def put(self, tx: payloads.Transaction) -> None: """ Store a transaction. Args: tx: instance. Raises: ValueError: if a duplicate item is found. """ super(CachedTXAccess, self)._put(tx.hash(), tx)
def calculate_network_fee(tx: payloads.Transaction, snapshot: storage.Snapshot, account: wallet.Account) -> int: if len(tx.signers) == 0: raise ValueError("Cannot calculate the network fee without a sender in the transaction.") hashes = tx.get_script_hashes_for_verifying(snapshot) network_fee_size = (tx.HEADER_SIZE + core_utils.get_var_size(tx.signers) # type: ignore + core_utils.get_var_size(tx.attributes) # type: ignore + core_utils.get_var_size(tx.script) # type: ignore + core_utils.get_var_size(len(hashes)) # type: ignore ) exec_fee_factor = contracts.PolicyContract().get_exec_fee_factor(snapshot) network_fee = 0 for i, hash_ in enumerate(hashes): witness_script = None if hash_ == account.script_hash and account.contract and len(account.contract.script) > 0: witness_script = account.contract.script if witness_script is None and len(tx.witnesses) > 0: for witness in tx.witnesses: if witness.script_hash() == hash_: witness_script = witness.verification_script break if witness_script is None or (witness_script and len(witness_script) == 0): raise ValueError("Using a smart contract as a witness is not yet supported in mamba") elif contracts.Contract.is_signature_contract(witness_script): network_fee_size += 67 + core_utils.get_var_size(witness_script) # type: ignore network_fee = exec_fee_factor * signature_contract_costs() elif contracts.Contract.is_multisig_contract(witness_script): _, threshold, public_keys = contracts.Contract.parse_as_multisig_contract(witness_script) invocation_script_size = 66 * threshold network_fee_size += (core_utils.get_var_size(invocation_script_size) # type: ignore + invocation_script_size + core_utils.get_var_size(witness_script)) # type: ignore network_fee = exec_fee_factor * multisig_contract_costs(threshold, len(public_keys)) network_fee += network_fee_size * contracts.PolicyContract().get_fee_per_byte(snapshot) return network_fee
def sign_multisig_tx(self, tx: payloads.Transaction, password: str, context: wallet.MultiSigContext, magic: Optional[int] = None) -> None: if magic is None: magic = settings.network.magic if not self.contract: raise ValueError("Account is not a valid multi-signature account") # When importing a multi-sig account it searches for an associated regular account to copy key material from. # However, it is possible to add a multi-sig account before having a regular account with one of the required # public keys for the multi-sig account. Therefore, we should check if we actually have key material to continue if self.is_watchonly: _, _, public_keys = contracts.Contract.parse_as_multisig_contract( self.contract.script) raise ValueError( f"Cannot sign with watch only account. Try adding a regular account to your wallet " f"matching one of the following public keys, or update the key material for this account " f"directly." f" {list(map(lambda pk: str(pk), public_keys))}") self._validate_tx(tx) if not self.is_multisig: raise ValueError("Account is not a valid multi-signature account") if not context.initialised: context.process_contract(self.contract.script) if self.public_key not in context.expected_public_keys: raise ValueError( "Account is not in the required key list for this signing context" ) message = magic.to_bytes(4, byteorder="little", signed=False) + tx.hash().to_array() signature = self.sign(message, password) context.signature_pairs.update({self.public_key: signature}) if context.is_complete: # build and insert multisig witness sb = vm.ScriptBuilder() pairs = list(context.signature_pairs.items()) # sort by public key pairs.sort(key=lambda p: p[0]) for i, (key, sig) in enumerate(pairs): if i == context.signing_threshold: break sb.emit_push(sig) invocation_script = sb.to_array() verification_script = contracts.Contract.create_multisig_redeemscript( context.signing_threshold, context.expected_public_keys) tx.witnesses.insert( 0, payloads.Witness(invocation_script, verification_script))
def add_system_fee(tx: payloads.Transaction, snapshot: storage.Snapshot) -> None: tx.system_fee = calculate_system_fee(tx, snapshot)
def add_network_fee(tx: payloads.Transaction, snapshot: storage.Snapshot, account: wallet.Account) -> None: tx.network_fee = calculate_network_fee(tx, snapshot, account)