def sign(self, default_wallet: BitcoinWallet = None): """Signing transaction using the wallet object.""" for tx_index, tx_in in enumerate(self.tx.vin): utxo = self.solvable_utxo[tx_index] wallet = utxo.wallet or default_wallet if wallet is None: raise RuntimeError('Cannot sign transaction without a wallet.') tx_script = utxo.parsed_script if utxo.contract: sig_hash = script.SignatureHash( script.CScript.fromhex(utxo.contract), self.tx, tx_index, script.SIGHASH_ALL) else: sig_hash = script.SignatureHash(tx_script, self.tx, tx_index, script.SIGHASH_ALL) sig = wallet.private_key.sign(sig_hash) + struct.pack( '<B', script.SIGHASH_ALL) script_sig = [sig, wallet.private_key.pub ] + utxo.unsigned_script_sig tx_in.scriptSig = script.CScript(script_sig) VerifyScript(tx_in.scriptSig, tx_script, self.tx, tx_index, (SCRIPT_VERIFY_P2SH, )) self.signed = True
def close_tx(self, fee: int, privkey_dest: str) -> str: """Create a (mutual) close tx""" txin = CTxIn(COutPoint(bytes.fromhex(self.txid), self.output_index)) out_privkey = privkey_expand(privkey_dest) txout = CTxOut(self.amount - fee, CScript([script.OP_0, Hash160(coincurve.PublicKey.from_secret(out_privkey.secret).format())])) tx = CMutableTransaction(vin=[txin], vout=[txout]) sighash = script.SignatureHash(self.redeemscript(), tx, inIdx=0, hashtype=script.SIGHASH_ALL, amount=self.amount, sigversion=script.SIGVERSION_WITNESS_V0) sigs = [key.sign(sighash, hasher=None) for key in self.funding_privkeys_for_tx()] # BOLT #3: # ## Closing Transaction # ... # * `txin[0]` witness: `0 <signature_for_pubkey1> <signature_for_pubkey2>` witness = CScriptWitness([bytes(), sigs[0] + bytes([script.SIGHASH_ALL]), sigs[1] + bytes([script.SIGHASH_ALL]), self.redeemscript()]) tx.wit = CTxWitness([CTxInWitness(witness)]) return tx.serialize().hex()
def sign_our_inputs(self) -> None: assert self.tx is not None for idx, _in in enumerate(self.inputs): privkey = _in['privkey'] if privkey and 'sig' not in _in: print('signing our input for tx', self.tx.serialize().hex()) inkey = privkey_expand(privkey) inkey_pub = coincurve.PublicKey.from_secret(inkey.secret) # Really horrid hack to produce a signature for the # multisig utxo in tests/helpers.py if privkey == '38204720bc4f9647fd58c6d0a4bd3a6dd2be16d8e4273c4d1bdd5774e8c51eaf': redeemscript = bytes.fromhex('51210253cdf835e328346a4f19de099cf3d42d4a7041e073cd4057a1c4fd7cdbb1228f2103ae903722f21f85e651b8f9b18fc854084fb90eeb76452bdcfd0cb43a16a382a221036c264d68a9727afdc75949f7d7fa71910ae9ae8001a1fbffa6f7ce000976597c21036429fa8a4ef0b2b1d5cb553e34eeb90a32ab19fae1f0024f332ab4f74283a7282103d4232f19ea85051e7b76bf5f01d03e17eea8751463dee36d71413a739de1a92755ae') else: address = P2WPKHBitcoinAddress.from_scriptPubKey(CScript([script.OP_0, Hash160(inkey_pub.format())])) redeemscript = address.to_redeemScript() sighash = script.SignatureHash(redeemscript, self.tx, idx, script.SIGHASH_ALL, amount=_in['sats'], sigversion=script.SIGVERSION_WITNESS_V0) sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL]) if privkey == '38204720bc4f9647fd58c6d0a4bd3a6dd2be16d8e4273c4d1bdd5774e8c51eaf': _in['sig'] = CTxInWitness(CScriptWitness([bytes([]), sig, redeemscript])) else: _in['sig'] = CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))
def add_witnesses(self, witness_stack) -> str: wits = [] for idx, _in in enumerate(self.inputs): privkey = _in['privkey'] serial_id = _in['serial_id'] if privkey: inkey = privkey_expand(privkey) inkey_pub = coincurve.PublicKey.from_secret(inkey.secret) address = P2WPKHBitcoinAddress.from_scriptPubKey(CScript([script.OP_0, Hash160(inkey_pub.format())])) sighash = script.SignatureHash(address.to_redeemScript(), self.tx, idx, script.SIGHASH_ALL, amount=_in['sats'], sigversion=script.SIGVERSION_WITNESS_V0) sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL]) wits.append(CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))) continue # Every input from the witness stack will be the accepter's # which is always an odd serial assert(serial_id % 2 == 1) elems = witness_stack.pop(0)['witness_element'] stack = [] for elem in elems: stack.append(bytes.fromhex(elem['witness'])) wits.append(CTxInWitness(CScriptWitness(stack))) self.tx.wit = CTxWitness(wits) return self.tx.serialize().hex()
def _sig(self, privkey: coincurve.PrivateKey, tx: CMutableTransaction) -> Sig: sighash = script.SignatureHash(self.funding.redeemscript(), tx, inIdx=0, hashtype=script.SIGHASH_ALL, amount=self.funding.amount, sigversion=script.SIGVERSION_WITNESS_V0) return Sig(privkey.secret.hex(), sighash.hex())
def from_utxo(txid_in: str, tx_index_in: int, sats: int, privkey: str, fee: int, local_node_privkey: str, local_funding_privkey: str, remote_node_privkey: str, remote_funding_privkey: str, chain_hash: str = regtest_hash) -> Tuple['Funding', str]: """Make a funding transaction by spending this utxo using privkey: return Funding, tx.""" # Create dummy one to start: we will fill in txid at the end. funding = Funding('', 0, sats - fee, local_node_privkey, local_funding_privkey, remote_node_privkey, remote_funding_privkey, chain_hash) # input private key. inkey = privkey_expand(privkey) inkey_pub = coincurve.PublicKey.from_secret(inkey.secret) # use RBF'able input (requirement for dual-funded things) txin = CTxIn(COutPoint(bytes.fromhex(txid_in), tx_index_in), nSequence=0xFFFFFFFD) txout = CTxOut( sats - fee, CScript([script.OP_0, sha256(funding.redeemscript()).digest()])) tx = CMutableTransaction([txin], [txout], nVersion=2, nLockTime=funding.locktime) # now fill in funding txid. funding.txid = tx.GetTxid().hex() funding.tx = tx # while we're here, sign the transaction. address = P2WPKHBitcoinAddress.from_scriptPubKey( CScript([script.OP_0, Hash160(inkey_pub.format())])) sighash = script.SignatureHash(address.to_redeemScript(), tx, 0, script.SIGHASH_ALL, amount=sats, sigversion=script.SIGVERSION_WITNESS_V0) sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL]) tx.wit = CTxWitness( [CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))]) return funding, tx.serialize().hex()
def create_tx(n, addressFrom, addressTo, mixer_list, fee=None): pre_tx = transaction(n, addressFrom, addressTo, mixer_list, fee=None) own_n = len(pre_tx.own_inputs) # Get inputs signed by each of the miners for x in pre_tx.mixer_inputs: sig_mixer, pubKey_mixer = send_tx(pre_tx.tx, x, pre_tx.pruned_mixer_inputs) x.scriptSig = bcs.CScript([sig_mixer, pubKey_mixer]) # Sign inputs owned by sender's address txin_scriptPubKey = bcs.CScript([bcs.OP_DUP, bcs.OP_HASH160, bc.Hash160(addressFrom.pubKey), bcs.OP_EQUALVERIFY, bcs.OP_CHECKSIG]) for i in range(own_n): sighash = bcs.SignatureHash(txin_scriptPubKey, pre_tx.tx, i, bcs.SIGHASH_ALL) sig = addressFrom.priv.sign(sighash) + bytes([bcs.SIGHASH_ALL]) pre_tx.inputs[i].scriptSig = bcs.CScript([sig, addressFrom.pubKey]) bcseval.VerifyScript(pre_tx.inputs[i].scriptSig, txin_scriptPubKey, pre_tx.tx, i, (bcseval.SCRIPT_VERIFY_P2SH,)) return pre_tx
def htlc_sigs(self, signer: Side, side: Side) -> List[Sig]: """Produce the signer's signatures for the dest's HTLC transactions""" # BOLT #2: # - MUST include one `htlc_signature` for every HTLC transaction # corresponding to the ordering of the commitment transaction (see # [BOLT # #3](03-transactions.md#transaction-input-and-output-ordering)). # So we need the HTLCs in output order, which is why we had _unsigned_tx # return them. commit_tx, htlcs = self._unsigned_tx(side) sigs: List[Sig] = [] for outnum, htlc in enumerate(htlcs): # to_local or to_remote output? if htlc is None: continue if htlc.owner == side: redeemscript, sats = self._offered_htlc_output(htlc, side) fee = htlc.htlc_timeout_fee(self.feerate) # BOLT #3: # * locktime: `0` for HTLC-success, `cltv_expiry` for HTLC-timeout locktime = htlc.cltv_expiry else: redeemscript, sats = self._received_htlc_output(htlc, side) fee = htlc.htlc_success_fee(self.feerate) locktime = 0 htlc_tx = self.htlc_tx(commit_tx, outnum, side, (htlc.amount_msat - msat(fee)) // 1000, locktime) print("htlc_tx = {}".format(htlc_tx.serialize().hex())) sighash = script.SignatureHash( redeemscript, htlc_tx, inIdx=0, hashtype=script.SIGHASH_ALL, amount=htlc.amount_msat // 1000, sigversion=script.SIGVERSION_WITNESS_V0) privkey = self._basepoint_tweak( self.keyset[signer].htlc_base_secret, side) sigs.append(Sig(privkey.secret.hex(), sighash.hex())) return sigs
def signed_tx(self, unsigned_tx: CMutableTransaction) -> CMutableTransaction: # BOLT #3: # * `txin[0]` witness: `0 <signature_for_pubkey1> <signature_for_pubkey2>` tx = unsigned_tx.copy() sighash = script.SignatureHash(self.funding.redeemscript(), tx, inIdx=0, hashtype=script.SIGHASH_ALL, amount=self.funding.amount, sigversion=script.SIGVERSION_WITNESS_V0) sigs = [ key.sign(sighash, hasher=None) for key in self.funding.funding_privkeys_for_tx() ] tx.wit = CTxWitness([ CScriptWitness([ bytes(), sigs[0] + bytes([script.SIGHASH_ALL]), sigs[1] + bytes([script.SIGHASH_ALL]), self.funding.redeemscript() ]) ]) return tx