def sign(self, private, hashcode=SIGHASH.ALL): output_type = self.ref().type() if self.is_signed(): raise SigningError('Input already signed') try: tx = self.parent idx = self.tx_index assert idx is not None except (AttributeError, AssertionError): raise SigningError("Reference to parent transaction missing") sighash = tx.sighash(idx, hashcode=hashcode) sig = private.sign_hash(sighash) # # https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#low_s # if sig.s > CURVE.N//2: # sig.s = CURVE.N - sig.s raw_sig = sig.encode() + hashcode.byte if output_type == TX.P2PKH: pub = private.to_public().encode(compressed=False) self.clear() self.script = push(raw_sig) + push(pub) elif output_type == TX.P2WPKH: pub = private.to_public().encode(compressed=True) self.clear() self.witness = (raw_sig, pub) elif output_type == TX.P2PK: self.clear() self.script = push(raw_sig) else: raise SigningError('Cannot sign P2SH or P2WSH outputs.')
def signature_form_segwit(self, i, hashcode=SIGHASH.ALL): """Segwit version of signature_form""" # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#specification tx = deepcopy(self) ref = tx.inputs[i].ref() nversion = tx._version[::-1] hashprevouts = sha256(sha256(concat((inp.outpoint() for inp in tx.inputs)))) if not hashcode.is_anyonecanpay() else pad(0, 32) hashsequence = sha256(sha256(concat(inp._sequence[::-1] for inp in tx.inputs))) if hashcode == SIGHASH.ALL else pad(0, 32) outpoint = tx.inputs[i].outpoint() scriptcode = serialize(tx.inputs[i].scriptcode()) value = ref._value[::-1] nsequence = tx.inputs[i]._sequence[::-1] if not (hashcode.is_single() or hashcode.is_none()): hashoutputs = sha256(sha256(concat(out._value[::-1] + push(out.script) for out in tx.outputs))) elif hashcode.is_single() and i < len(tx.outputs): hashoutputs = sha256(sha256(tx.outputs[i]._value[::-1] + push(tx.outputs[i].script))) else: hashoutputs = pad(0, 32) nlocktime = tx._lock_time[::-1] sighash = pad(hashcode.value, 4)[::-1] return concat([nversion, hashprevouts, hashsequence, outpoint, scriptcode, value, nsequence, hashoutputs, nlocktime, sighash])
def scriptcode(self): output = self.ref() output_type = self.ref().type() if output_type == TX.P2WPKH: return OP.DUP.byte + OP.HASH160.byte + push(witness_program(output.script)) + OP.EQUALVERIFY.byte + OP.CHECKSIG.byte elif output_type == TX.P2SH: if self.is_nested() == TX.P2WPKH: return OP.DUP.byte + OP.HASH160.byte + push(witness_program(self.script[1:])) + OP.EQUALVERIFY.byte + OP.CHECKSIG.byte elif self.is_nested() == TX.P2WSH: return self.witness[-1] # elif output_type == TX.P2WPKH: # return output.script # elif output_type == TX.P2SH: # return self.script elif output_type == TX.P2WSH: return self.witness[-1] else: raise ScriptValidationError(f"No scriptcode for {output_type}")
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 address_to_script(addr: str) -> bytes: """https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#segwit-address-format""" hrp, _ = bech32.bech32_decode(addr) if hrp not in [net['hrp'] for net in networks.values()]: raise Bech32DecodeError('Invalid human-readable part') witver, witprog = bech32.decode(hrp, addr) if not (0 <= witver <= 16): raise Bech32DecodeError('Invalid witness version') script = witness_byte(witver) + push(bytes(witprog)) return script
def test_p2wsh(self): """https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#examples""" pubkey = PublicKey.from_hex( '0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798' ) script = push(pubkey.encode( compressed=True)) + OP.CHECKSIG.byte # <key> <OP_CHECKSIG> address = script_to_address(script, 'P2WSH') self.assertEqual( address, 'bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3') self.assertEqual(address_type(address), ADDRESS.P2WSH)
def script_to_bech32(script: bytes, witver: int) -> str: """https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program""" witprog = sha256(script) return bech32.encode(network('hrp'), witver, witprog) def pubkey_to_bech32(pub: PublicKey, witver: int) -> str: """https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program""" witprog = hash160(pub.encode(compressed=True)) return bech32.encode(network('hrp'), witver, witprog) key_to_addr_versions = { ADDRESS.P2PKH: lambda pub: legacy_address(pub, version_byte=network('keyhash')), # 'P2WPKH': partial(pubkey_to_p2wpkh, version_byte=0x06, witver=0x00), # WAS REPLACED BY BIP 173 ADDRESS.P2WPKH_P2SH: lambda pub: legacy_address(witness_byte(witver=0) + push(hash160(pub.encode(compressed=True))), version_byte=network('scripthash')), ADDRESS.P2WPKH: partial(pubkey_to_bech32, witver=0x00), } script_to_addr_versions = { ADDRESS.P2SH: lambda script: legacy_address(script, version_byte=network('scripthash')), # 'P2WSH': partial(script_to_p2wsh, version_byte=0x0A, witver=0x00), # WAS REPLACED BY BIP 173 ADDRESS.P2WSH_P2SH: lambda script: legacy_address(witness_byte(witver=0) + push(sha256(script)), version_byte=network('scripthash')), ADDRESS.P2WSH: partial(script_to_bech32, witver=0x00), } def pubkey_to_address(pub: PublicKey, version='P2PKH') -> str: converter = key_to_addr_versions[ADDRESS(version.upper())] return converter(pub)
def test_digest_p2sh_p2wsh(self): # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#p2sh-p2wsh tx = Transaction.from_hex( '010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000' ) ref = Output( int(9.87654321 * 10**8), hex_to_bytes('a9149993a429037b5d912407a71c252019287b8d27a587')) inp = tx.inputs[0] inp._referenced_output = ref inp.script = push( hex_to_bytes( '0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54' )) inp.witness = [ hex_to_bytes( '56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae' ) ] sighash = tx.sighash(0, hashcode=SIGHASH.ALL) self.assertEqual( bytes_to_hex(sighash), '185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c') sighash = tx.sighash(0, hashcode=SIGHASH.NONE) self.assertEqual( bytes_to_hex(sighash), 'e9733bc60ea13c95c6527066bb975a2ff29a925e80aa14c213f686cbae5d2f36') sighash = tx.sighash(0, hashcode=SIGHASH.SINGLE) self.assertEqual( bytes_to_hex(sighash), '1e1f1c303dc025bd664acb72e583e933fae4cff9148bf78c157d1e8f78530aea') sighash = tx.sighash(0, hashcode=SIGHASH.ALL_ANYONECANPAY) self.assertEqual( bytes_to_hex(sighash), '2a67f03e63a6a422125878b40b82da593be8d4efaafe88ee528af6e5a9955c6e') sighash = tx.sighash(0, hashcode=SIGHASH.NONE_ANYONECANPAY) self.assertEqual( bytes_to_hex(sighash), '781ba15f3779d5542ce8ecb5c18716733a5ee42a6f51488ec96154934e2c890a') sighash = tx.sighash(0, hashcode=SIGHASH.SINGLE_ANYONECANPAY) self.assertEqual( bytes_to_hex(sighash), '511e8e52ed574121fc1b654970395502128263f62662e076dc6baf05c2e6a99b') signatures = [ hex_to_bytes( '304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01' ), hex_to_bytes( '3044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502' ), hex_to_bytes( '3044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403' ), hex_to_bytes( '3045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381' ), hex_to_bytes( '3045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a0882' ), hex_to_bytes( '30440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783' ) ] inp.witness = [b''] + signatures + inp.witness self.assertEqual( tx.hex(), '0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000' ) self.assertTrue(tx.verify())