def send(self, to: dict, fee: float, private: PrivateKey) -> Transaction: balance = btc_to_satoshi(self.balance()) fee = btc_to_satoshi(fee) to = {key: btc_to_satoshi(val) for key, val in to.items()} sum_send = sum(to.values()) if balance < sum_send + fee: raise ValidationError("Insufficient balance") elif balance > sum_send + fee: raise ValidationError(f"You are trying to send {sum_send/10**8} BTC which is " f"less than this address' current balance of {balance/10**8}." f" You must provide a change address or explicitly add the difference as a fee") inputs = [out.spend() for out in self.utxos] outputs = [Address(addr)._receive(val) for addr, val in to.items()] tx = Transaction(inputs=inputs, outputs=outputs) for idx in range(len(tx.inputs)): tx.inputs[idx].tx_index = idx tx.inputs[idx]._parent = tx for inp in tx.inputs: inp.sign(private) return tx
def verify(self, i=None, debug=False): """Run the script for the i-th input or all the inputs""" sum_inputs = sum(inp.ref().value for inp in self.inputs) sum_outputs = sum(out.value for out in self.outputs) if sum_outputs > sum_inputs: raise ValidationError("Value of outputs is greater than value of inputs") if i is not None: vm = VM(self, i) return vm.verify(debug=debug) else: results = [] for idx in range(len(self.inputs)): vm = VM(self, idx) results.append(vm.verify()) return all(results)
def _receive(self, value: int): """Creates an output that sends to this address""" addr_type = self.type() output = Output(value=value, script=b'') if addr_type == ADDRESS.P2PKH: address = base58.decode(self.address).rjust(25, b'\x00') keyhash = address[1:-4] output.script = OP.DUP.byte + OP.HASH160.byte + push(keyhash) + OP.EQUALVERIFY.byte + OP.CHECKSIG.byte elif addr_type == ADDRESS.P2SH: address = base58.decode(self.address).rjust(25, b'\x00') scripthash = address[1:-4] output.script = OP.HASH160.byte + push(scripthash) + OP.EQUAL.byte elif addr_type in (ADDRESS.P2WPKH, ADDRESS.P2WSH): witness_version, witness_program = bech32.decode(network('hrp'), self.address) output.script = OP(bytes_to_int(witness_byte(witness_version))).byte + push(bytes(witness_program)) else: raise ValidationError(f"Cannot create output of type {addr_type}") return output
def get_address(script): """Extracts the address from a scriptPubkey""" script = hex_to_bytes(script) if isinstance(script, str) else script stype = get_type(script) if stype == TX.P2SH: data = script[2:22] version = network('scripthash') return hashed_payload_to_address(version + data) elif stype == TX.P2PKH: data = script[3:23] version = network('keyhash') return hashed_payload_to_address(version + data) elif stype in (TX.P2WSH, TX.P2WPKH): witver = version_byte(script) witprog = witness_program(script) return bech32.encode(network('hrp'), witver, witprog) elif stype == TX.P2PK: return "N/A" raise ValidationError(f"Unknown script type: {bytes_to_hex(script)}")