Example #1
0
 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
Example #2
0
    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)
Example #3
0
    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))
Example #4
0
    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)
Example #5
0
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
Example #6
0
    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))
Example #7
0
def add_system_fee(tx: payloads.Transaction, snapshot: storage.Snapshot) -> None:
    tx.system_fee = calculate_system_fee(tx, snapshot)
Example #8
0
def add_network_fee(tx: payloads.Transaction, snapshot: storage.Snapshot, account: wallet.Account) -> None:
    tx.network_fee = calculate_network_fee(tx, snapshot, account)