Esempio n. 1
0
def createTxState(tx_in: TxInput, pubkey_left: PublicKey, pubkey_right: PublicKey,
                  pubkey_pay_right: PublicKey,
                  pubkey_mulsig_left: PublicKey, pubkey_mulsig_right: PublicKey,
                  lock_val: float, left_val: float, right_val: float, T: int, delta: int) -> Transaction:

    """
    Move coins from left user balance to a new "lock" output.
    Before: 'a' coins to L, 'b' coins to R.
    After: 'a - c' coins to L, 'b' coins to R, 'c' coins locked.

    :param tx_in: reference to channel open transaction
    :param pubkey_left: public key owned by left user to receive his coins from channel
    :param pubkey_right: public key owned by right user to receive his coins from channel
    :param pubkey_pay_right: public key owned by right user for payment after time T
    :param pubkey_mulsig_left: public key owned by left user for refund if enable-refund tx is published
    :param pubkey_mulsig_right: public key owned by right user for refund if enable-refund tx is published
    :param lock_val: amount of coins to lock: 'c'
    :param left_val: coins or left user: '******'
    :param right_val: coins or right user: '******'
    :param T: locked funds can pe paid after this time wherever right user wants
    :param delta: upper bound on time for transaction to be confirmed by the network
    :return: tx_state
    """

    out_lock_script = getTxStateLockScript(T, delta, pubkey_pay_right, pubkey_mulsig_left, pubkey_mulsig_right)

    tx_out_lock = TxOutput(lock_val, out_lock_script)
    tx_out_left = TxOutput(left_val, P2pkhAddress(pubkey_left.get_address().to_string()).to_script_pub_key())
    tx_out_right = TxOutput(right_val, P2pkhAddress(pubkey_right.get_address().to_string()).to_script_pub_key())

    tx = Transaction([tx_in], [tx_out_lock, tx_out_left, tx_out_right])

    return tx
Esempio n. 2
0
def getTxStateLockScript(T: int, delta: int, pubkey_pay_right: PublicKey,
                         pubkey_mulsig_left: PublicKey, pubkey_mulsig_right: PublicKey) -> Script:
    """
    tx_state lock script encodes possibilities to either refund locked coins to left or pay to right

    :param T: locked funds can pe paid after this time wherever right user wants
    :param delta: upper bound on time for transaction to be confirmed by the network
    :param pubkey_pay_right: public key owned by right user for payment after time T
    :param pubkey_mulsig_left: public key owned by left user for refund if enable-refund tx is published
    :param pubkey_mulsig_right: public key owned by right user for refund if enable-refund tx is published
    :return: tx_state lock script
    """

    # signature script:
    # - for refund (with enable-refund tx + ∆): "OP_0 <left_signature> <right_signature>"
    # - for payment (time() >= T): "<signature_right> <pubkey_right> OP_0 OP_0 OP_0 OP_0 OP_0 OP_0"
    lock_script = Script([
        'OP_2', pubkey_mulsig_left.to_hex(), pubkey_mulsig_right.to_hex(), 'OP_2', 'OP_CHECKMULTISIG',
        'OP_IF',
            delta, 'OP_CHECKSEQUENCEVERIFY', 'OP_DROP', 'OP_TRUE',  # check if refund and lock for ∆
        'OP_ELSE',
            T, 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', pubkey_pay_right.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG',  # check if payment
        'OP_ENDIF'
    ])

    return lock_script
Esempio n. 3
0
def main():

    #Connect to the regtest
    setup('regtest')
    proxy = NodeProxy('user', 'bitcoin').get_proxy()

    #Accept a Public Key from User and derive a P2PKH address from it
    pk = PublicKey.from_hex(input('Please input your Public Key\n'))
    pk_p2pkh_addr = pk.get_address()

    #Accept future absolute block amount from User
    current_blocks = proxy.getblockcount()
    absolute_blocks = int(
        input(
            f'\nPlease input the future block height. The current block height is: {current_blocks}\n'
        ))

    #Set the timelock sequence and create a redeemScript accordingly

    redeem_script = Script([
        absolute_blocks, 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP',
        'OP_HASH160',
        pk_p2pkh_addr.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'
    ])

    #Generate a P2SH Address from the redeemScript and show it
    p2sh_addr = P2shAddress.from_script(redeem_script)
    print(
        f'\nThe P2SH Address is:\n{p2sh_addr.to_string()} Now, go on and send some funds to it.\n'
    )
Esempio n. 4
0
    def tx_decf(self, tx_hash):
        raw_tx = self.proxy.getrawtransaction(
            tx_hash)  # get the raw transaction from the hash of the tx
        decoded_tx = self.proxy.decoderawtransaction(
            raw_tx)  # decode the raw transaction and get the json format

        # check to see if the tx has 1 input and two outputs and is of type pay2pubkeyhash and pay2scripthash
        if len(decoded_tx['vin']) != 1 or len(decoded_tx['vout']) != 2:
            return -1, -1
        if decoded_tx['vout'][0]['scriptPubKey'][
                'hex'][:6] != '76a914' or decoded_tx['vout'][0][
                    'scriptPubKey']['hex'][-4:] != '88ac':  #pay2pubkeyhash
            return -1, -1
        if decoded_tx['vout'][1]['scriptPubKey'][
                'hex'][:4] != 'a914' or decoded_tx['vout'][1]['scriptPubKey'][
                    'hex'][-2:] != '87':  #pay2scripthash
            return -1, -1

        try:
            vk_u_compressed = decoded_tx['vin'][0]['scriptSig']['hex'][-66:]
            vk_u = PublicKey(vk_u_compressed).to_hex(compressed=False)[2:]
            ct_c = decoded_tx['vout'][1]['scriptPubKey']['hex'][4:-2]
            return vk_u, ct_c
        except:
            return -1, -1
Esempio n. 5
0
    def tx_decb(self, block_id, vk_p):
        txs_hashes = self.get_block_txs(
            block_id)  # Get the hash of transactions inside this block
        for tx_hash in txs_hashes:
            raw_tx = self.proxy.getrawtransaction(
                tx_hash)  # get the raw transaction from the hash of the tx
            decoded_tx = self.proxy.decoderawtransaction(
                raw_tx)  # decode the raw transaction and get the json format

            # check to see if the tx has 1 input and two outputs and is of type pay2pubkeyhash
            if len(decoded_tx['vin']) != 1 or len(decoded_tx['vout']) != 2:
                continue
            if decoded_tx['vout'][0]['scriptPubKey'][
                    'hex'][:6] != '76a914' or decoded_tx['vout'][0][
                        'scriptPubKey']['hex'][-4:] != '88ac':  # pay2pubkeyhash
                continue
            if decoded_tx['vout'][1]['scriptPubKey'][
                    'hex'][:4] != 'a914' or decoded_tx['vout'][1][
                        'scriptPubKey']['hex'][-2:] != '87':  # pay2scripthash
                continue

            try:
                vk_p_compressed = decoded_tx['vin'][0]['scriptSig']['hex'][
                    -66:]
                ct_r1 = decoded_tx['vout'][0]['scriptPubKey']['hex'][6:-4]
                ct_r2 = decoded_tx['vout'][1]['scriptPubKey']['hex'][4:-2]
                if vk_p_compressed == PublicKey('04' +
                                                vk_p).to_hex(compressed=True):
                    print("Found the transaction with response message")
                    return ct_r1 + ct_r2
            except:
                continue

        return None
Esempio n. 6
0
def get_redeem_pub_key_obj():
    #
    # Get the private key for the p2pkh of the redeem script
    #
    # Using a public key has not been tested.
    # Support for public from user submitted text looks like it's limited in bitcoin-utils lib.
    #
    function_section = 'Function 1'

    private_key_to = Common.get_config_value(function_section,
                                             'private_key_to')
    public_key_to = Common.get_config_value(function_section, 'public_key_to')

    if len(private_key_to) > 0:
        redeem_pub_key_obj = Common.get_pub_key_from_priv_key_obj(
            private_key_to)
        return redeem_pub_key_obj

    # Not tested and copied the line below from the lib.
    elif len(public_key_to) > 0:
        return PublicKey.from_hex(
            hexlify(public_key_to.to_string()).decode('utf-8'))

    else:
        sys.exit(
            'No private or public key provided in configuration of function 1!'
        )
Esempio n. 7
0
def create_HODL_address(key, lock, is_priv=False):
    setup(os.getenv("BUY_LAMBO_BTC_NET", "regtest"))

    # Get public key (given or found from private key)
    public_key = PrivateKey(key).get_public_key() if is_priv else PublicKey(key)

    # Get address from public key
    address_from_key = public_key.get_address()

    # Set lock sequence prefix
    seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, lock)

    # create the redeem script - needed to sign the transaction
    redeem_script = Script(
        [
            seq.for_script(),
            "OP_CHECKLOCKTIMEVERIFY",
            "OP_DROP",
            "OP_DUP",
            "OP_HASH160",
            address_from_key.to_hash160(),
            "OP_EQUALVERIFY",
            "OP_CHECKSIG",
        ]
    )

    # create a P2SH address from a redeem script
    addr = P2shAddress.from_script(redeem_script)
    print("Time-locked address: {}".format(addr.to_string()))
Esempio n. 8
0
    def _tx_encf(self, vk_p, ct_c, sk_u, txin_id, txin_index):
        # create transaction input from tx id of UTXO
        txin = TxInput(txin_id, txin_index)

        # having the vk_p from the shared key generate the corresponding address for decoder to use to respond
        vk_p = PublicKey(vk_p)
        addr_p = vk_p.get_address().to_string()

        # create transaction output using P2PKH scriptPubKey for paying address
        paying_addr = P2pkhAddress(addr_p)
        paying_txout = TxOutput(0.00002, paying_addr.to_script_pub_key())

        # create an output where the address is the ciphertext
        cipher_txout = TxOutput(0.00001,
                                Script(['OP_HASH160', ct_c, 'OP_EQUAL']))

        # create transaction from inputs/outputs -- default locktime is used
        tx = Transaction([txin], [paying_txout, cipher_txout])
        #print("\nRaw unsigned transaction:\n" + tx.serialize())

        # use private key corresponding to the address that contains the UTXO we are trying to spend to sign the input
        vk_u = sk_u.get_public_key()
        addr_u = vk_u.get_address().to_string()

        # note that we pass the scriptPubkey as one of the inputs of sign_input because it is used to replace
        # the scriptSig of the UTXO we are trying to spend when creating the transaction digest
        from_addr = P2pkhAddress(addr_u)
        sig = sk_u.sign_input(
            tx, 0,
            from_addr.to_script_pub_key())  # 0 is for the index of the input

        # set the scriptSig (unlocking script)
        vk_u = vk_u.to_hex()
        txin.script_sig = Script([sig, vk_u])
        signed_tx = tx.serialize()

        print("Users input address: ", addr_u)
        print("Decoder paying address: ", addr_p)
        print("\nRaw signed transaction:\n" + signed_tx)

        return signed_tx
def main():
    # always remember to setup the network
    setup('regtest')

    #
    # This script creates a P2SH address containing a CHECKLOCKTIMEVERIFY plus a P2PKH locking funds with a key as 
    # well as for an absolute amount of blocks or an absolute amount of seconds since the transaction. 
    #

    parser = argparse.ArgumentParser(
        description='Give the public key, a future time expressed either in block height or in UNIX Epoch time and '
                    'the P2SH address will be displayed') 
    parser.add_argument('pubkey', help="Add the public key.")
    parser.add_argument('-param', type=int, help="Add the number of blocks or the time expressed in seconds.")
    args = parser.parse_args()

    # set values
    key = args.pubkey
    absolute_param = args.param

    p2pkh_pk = PublicKey(key)

    # set Locktime
    seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, absolute_param)
    locktime = Locktime(absolute_param)

    # get the address (from the public key)
    p2pkh_addr = p2pkh_pk.get_address()

    # print("Public key: " + p2pkh_pk.to_hex(compressed=True))
    # print("P2PKH Address: " + p2pkh_addr.to_string())

    # create the redeem script
    redeem_script = Script(
        [seq.for_script(), 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', p2pkh_addr.to_hash160(),
         'OP_EQUALVERIFY', 'OP_CHECKSIG'])

    # create a P2SH address from a redeem script
    addr = P2shAddress.from_script(redeem_script)
    print("The P2SH address is : " + addr.to_string())
Esempio n. 10
0
def create_p2sh(proxy, block_height=10, address_pubk=None):
    """
    Creates a P2SH address with an absolute block locktime.

        :param proxy: JSON RPC proxy for connecting to the network.
        :param block_height: Block height the lock is valid for. Default value is 10.
        :param address_pubk: Public key of the address locking the funds. If None, a new address will be created.
    """

    # if a public key is not specified, create a new address and display its keys for future use
    if not address_pubk:
        print('Public key not provided. Created a new address.')
        address = proxy.getnewaddress()
        print('Address:', address)
        address_privk = proxy.dumpprivkey(address)
        print('Private key:', address_privk)
        address_pubk = proxy.getaddressinfo(address)['pubkey']
        print('Public key:', address_pubk)
    # create the public key object
    p2pkh_pk = PublicKey(address_pubk)

    # create sequence for the redeem script
    seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, block_height)
    # create the redeem script
    redeem_script = Script([
        seq.for_script(), 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP',
        'OP_HASH160',
        p2pkh_pk.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'
    ])

    # create the P2SH address from the redeem script
    p2sh_addr = P2shAddress.from_script(redeem_script)
    # insert the P2SH address into the wallet
    proxy.importaddress(p2sh_addr.to_string())

    # display the P2SH address
    print('Created P2SH address:', p2sh_addr.to_string())
Esempio n. 11
0
def getEnableTxOutputLockScript(pubkey: PublicKey,
                                rel_timelock: int) -> Script:
    """
    Create lock script for output of enable-(payment/refund) transaction

    :param pubkey: public key owned by corresponding payment participant
    :param rel_timelock: relative lock on outputs. Should be same for all outputs of transaction
    :return: lock script
    """

    seq = Sequence(TYPE_RELATIVE_TIMELOCK, rel_timelock)
    return Script([
        seq.for_script(), 'OP_CHECKSEQUENCEVERIFY', 'OP_DROP', 'OP_DUP',
        'OP_HASH160',
        pubkey.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'
    ])
 def tx_decf(self, tx_hash):
     decoded_tx = self.get_tx(tx_hash)
     #--------------------get the public key of the user------------------------------
     if len(decoded_tx['vin']) != 1 or len(
             decoded_tx['vShieldedOutput']) != 2:
         return -1, -1
     try:
         vk_u_compressed = decoded_tx['vin'][0]['scriptSig']['hex'][-66:]
         vk_u = PublicKey(vk_u_compressed).to_hex(compressed=False)[2:]
     except:
         return -1, -1
     #--------------------get the ciphertext------------------------------------------
     cmd = os.popen(zcash_cli + " " + conf_file +
                    ' z_listreceivedbyaddress ' +
                    public_address_decoder_shielded)
     tx_json = json.loads(cmd.read())[-1]
     ct_c = tx_json['memo'][:self.challenge_len * 2]
     self.tx_addr_dict[tx_hash] = bytes.fromhex(
         tx_json['memo'][self.challenge_len * 2:self.challenge_len * 2 +
                         self.address_len]).decode()
     return vk_u, ct_c
def main():
    # always remember to setup the network
    setup('mainnet')

    # create a private key (deterministically)
    priv = PrivateKey(secret_exponent=1)

    # compressed is the default
    print("\nPrivate key WIF:", priv.to_wif(compressed=True))

    # could also instantiate from existing WIF key
    #priv = PrivateKey.from_wif('KwDiBf89qGgbjEhKnhxjUh7LrciVRzI3qYjgd9m7Rfu73SvHnOwn')

    # get the public key
    pub = priv.get_public_key()

    # compressed is the default
    print("Public key:", pub.to_hex(compressed=True))

    # get address from public key
    address = pub.get_address()

    # print the address and hash160 - default is compressed address
    print("Address:", address.to_string())
    print("Hash160:", address.to_hash160())

    print("\n--------------------------------------\n")

    # sign a message with the private key and verify it
    message = "The test!"
    signature = priv.sign_message(message)
    print("The message to sign:", message)
    print("The signature is:", signature)

    if PublicKey.verify_message(address.to_string(), signature, message):
        print("The signature is valid!")
    else:
        print("The signature is NOT valid!")
def validate_certificate(cert, issuer_identifier, testnet,
                         blockchain_services):
    filename = os.path.basename(cert)
    tmp_filename = '__' + filename
    shutil.copy(cert, tmp_filename)

    issuer_address = get_issuer_address(tmp_filename)

    proof = get_and_remove_chainpoint_proof(tmp_filename)
    if proof == None:
        os.remove(tmp_filename)
        return False, "no chainpoint_proof in metadata"

    # get the hash after removing the metadata
    filehash = ''
    with open(tmp_filename, 'rb') as pdf_file:
        filehash = hashlib.sha256(pdf_file.read()).hexdigest()

    # instantiate chainpoint object
    cp = ChainPointV2()

    txid = cp.get_txid_from_receipt(proof)

    # make request to get txs regarding this address
    # issuance is the first element of data_before_issuance
    data_before_issuance, data_after_issuance = \
        network_utils.get_all_op_return_hexes(issuer_address, txid,
                                              blockchain_services, testnet)

    # validate receipt
    valid, reason = cp.validate_receipt(proof, data_before_issuance[0],
                                        filehash, issuer_identifier, testnet)

    # display error except when the certificate expired; this is because we want
    # revoked certificate error to be displayed before cert expired error
    # TODO clean hard-coded reason
    if not valid and not reason.startswith("certificate expired"):
        return False, reason

    # set bitcoin network (required for addr->pkh in revoke address)
    if testnet:
        setup('testnet')
    else:
        setup('mainnet')

    # check if cert's issuance is after a revoke address cmd on that address
    # and if yes then the issuance is invalid (address was revoked)
    # we check before checking for cert revocations since if the issuance was
    # after an address revocation it should show that as an invalid reason
    # 0 index is the actual issuance -- ignore it
    for i in range(len(data_before_issuance))[1:]:
        cred_dict = cred_protocol.parse_op_return_hex(data_before_issuance[i])
        if cred_dict:
            if cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_address'):
                issuer_pkh = P2pkhAddress(issuer_address).to_hash160()
                if issuer_pkh == cred_dict['data']['pkh']:
                    return False, "address was revoked"

    # check if cert or batch was revoked from oldest to newest
    for op_return in reversed(data_after_issuance):
        cred_dict = cred_protocol.parse_op_return_hex(op_return)
        if cred_dict:
            if cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_batch'):
                if txid == cred_dict['data']['txid']:
                    return False, "batch was revoked"
            elif cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_creds'):
                if txid == cred_dict['data']['txid']:
                    # compare the certificate hash bytes
                    filehash_bytes = utils.hex_to_bytes(filehash)
                    ripemd_filehash = utils.ripemd160(filehash_bytes)
                    ripemd_hex = utils.bytes_to_hex(ripemd_filehash)
                    if ripemd_hex == cred_dict['data']['hashes'][0]:
                        return False, "cert hash was revoked"

                    if len(cred_dict['data']['hashes']) > 1:
                        if ripemd_hex == cred_dict['data']['hashes'][1]:
                            return False, "cert hash was revoked"
            elif cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_address'):
                # if address revocation is found stop looking since all other
                # revocations will be invalid
                issuer_pkh = P2pkhAddress(issuer_address).to_hash160()
                if issuer_pkh == cred_dict['data']['pkh']:
                    break

    # if not revoked but not valid this means that it was expired; now that we
    # checked for revocations we can show the expiry error
    if not valid:
        return False, reason

    # now that the issuer (anchoring) was validated validate the certificate
    # with the owner's public key (vpdf v2)

    # get owner and owner_proof removing the latter
    owner, owner_proof = get_owner_and_remove_owner_proof(tmp_filename)
    if owner:
        # get public key
        pk = PublicKey.from_hex(owner['pk'])
        # get file hash
        sha256_hash = None
        with open(tmp_filename, 'rb') as pdf:
            sha256_hash = hashlib.sha256(pdf.read()).hexdigest()

        # cleanup now that we got original filehash
        os.remove(tmp_filename)

        # finally check if owner signature is valid
        #print(pk.get_address().to_string(), pk.to_hex(), sha256_hash, owner_proof)
        try:
            if (pk.verify(owner_proof, sha256_hash)):
                pass
        except Exception:  #BadSignatureError:
            return False, 'owner signature could not be validated'

    # in a valid credential the reason could contain an expiry date
    return True, reason
def _fill_pdf_metadata(out_file,
                       issuer,
                       issuer_address,
                       column_fields,
                       data,
                       global_columns,
                       verify_issuer,
                       conf,
                       interactive=False):

    # create version
    version = 2

    # create issuer object (json)
    issuer = {
        "name": issuer,
        "identity": {
            "address": issuer_address,
            "verification": json.loads(verify_issuer)['methods']
        }
    }

    # create metadata object (json) and add metadata
    metadata = {}

    # add custom metadata
    if column_fields:
        metadata_fields = json.loads(column_fields)['columns']
        for f in metadata_fields:
            key = list(f)[0]
            if key in data:
                field_properties = f[key]
                field_properties['value'] = data[key]
                metadata[key] = field_properties

    # add global field metadata
    if global_columns:
        global_fields = json.loads(global_columns)['fields']
        for g in global_fields:
            key = list(g)[0]
            # note that global fields override column data
            metadata[key] = g[key]

    # now look at special owner name/pubkey columns explicitly in code
    # TODO we should probably check if the public key is valid
    owner = None
    owner_address = None
    owner_pk = None
    if '__OWNER_PK__' in data and data[
            '__OWNER_PK__'] and '__OWNER_ADDRESS__' in data and data[
                '__OWNER_ADDRESS__']:
        # TODO maybe just calculate address from public key?
        owner_address = data['__OWNER_ADDRESS__']
        owner_pk = data['__OWNER_PK__']

        owner = {
            "name": data['__OWNER_NAME__'],
            "owner_address":
            data['__OWNER_ADDRESS__'],  # TODO needed? - can be derived
            "pk": owner_pk
        }

        # add the metadata
        pdf_metadata = PdfDict(version=version,
                               issuer=json.dumps(issuer),
                               metadata=json.dumps(metadata),
                               owner=json.dumps(owner),
                               owner_proof='',
                               chainpoint_proof='')
    else:
        # add the metadata (without dumps(owner) to keep owner empty)
        pdf_metadata = PdfDict(version=version,
                               issuer=json.dumps(issuer),
                               metadata=json.dumps(metadata),
                               owner='',
                               owner_proof='',
                               chainpoint_proof='')

    pdf = PdfReader(out_file)
    if pdf.Info:
        pdf.Info.update(pdf_metadata)
    else:
        pdf.Info = pdf_metadata
    PdfWriter().write(out_file, pdf)

    # if owner exists then need to add owner_proof
    # hash pdf, sign hash message using node and add in owner_proof
    if owner:
        ##import time
        ##start = time.time()
        sha256_hash = None
        with open(out_file, 'rb') as pdf:
            sha256_hash = hashlib.sha256(pdf.read()).hexdigest()
        if (conf.testnet):
            setup('testnet')
        else:
            setup('mainnet')

        host, port = conf.full_node_url.split(
            ':')  #TODO: update when NodeProxy accepts full url!
        proxy = NodeProxy(conf.full_node_rpc_user, conf.full_node_rpc_password,
                          host, port).get_proxy()

        # Due to an old unresolved issue still pending in Bitcoin v0.20.0
        # signmessage does not support signing with bech32 key.
        # To resolve we use the public key to get the base58check encoding that
        # signmessage is happy with so that we can sign!
        if (owner_address.startswith('bc') or owner_address.startswith('tb')):
            owner_address = PublicKey(owner_pk).get_address().to_string()

        # NOTE that address (the encoding) might have changed here from bech32
        # to legacy... take care if you use it again in this function!

        sig = proxy.signmessage(owner_address, sha256_hash)

        # add owner_proof to metadata
        pdf_metadata = PdfDict(owner_proof=sig)
        pdf = PdfReader(out_file)
        pdf.Info.update(pdf_metadata)
        PdfWriter().write(out_file, pdf)
        ##end = time.time()
        ##print(end-start, " seconds")
        ##exit()

    if interactive:
        # print progress
        print('.', end="", flush=True)
Esempio n. 16
0
def spend_p2sh_cltv_p2pkh(proxy, block_height, sender_priv_key, p2sh_addr,
                          rec_addr):
    """
    Spends funds from a P2SH address with an absolute locktime to a P2PKH receiver address.
    :param proxy: JSON RPC proxy for connecting to the network.
    :param block_height: Block height the lock is valid for.
    :param sender_priv_key: Private key of the address locking the funds.
    :param p2sh_addr: P2SH address containing the funds.
    :param rec_addr: P2PKH address receiving the funds.
    """

    # mine 100 blocks and send bitcoin to the P2SH address
    proxy.generatetoaddress(100, p2sh_addr)
    # mine 100 blocks to make mined bitcoin spendable, emulates 'bitcoin-cli -generate 100'
    for _ in range(100):
        proxy.generatetoaddress(1, proxy.getnewaddress())

    # retrieve unspent UTXOs for the P2SH address
    p2sh_addr_unspent = proxy.listunspent(0, 99999999, [p2sh_addr])

    # create absolute-block locking sequence for the transaction inputs
    seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, block_height)

    # create transaction inputs for unspent UTXOs
    # and calculate the total unspent bitcoins they contain
    tx_inputs = []
    total_unspent = 0
    for utxo in p2sh_addr_unspent:
        tx_inputs.append(
            TxInput(utxo['txid'],
                    utxo['vout'],
                    sequence=seq.for_input_sequence()))
        total_unspent += utxo['amount']
    print("Unspent bitcoin in address {} : {}".format(p2sh_addr,
                                                      total_unspent))

    # get the public key of the receiving address for the funds to be sent to
    rec_pub_key = proxy.getaddressinfo(rec_addr)['pubkey']
    rec_pk = PublicKey(rec_pub_key)

    # calculate fee
    satoshis_per_kb = requests \
        .get('https://api.blockcypher.com/v1/btc/test3') \
        .json()['medium_fee_per_kb']
    # formula: |inputs| * 180 + 34 * |outputs| +- |inputs|
    tx_size = len(tx_inputs) * 180 + 34 * 1 + 10 + len(tx_inputs)
    # we calculate fees in terms of bitcoin
    fee = (tx_size / 1024) * (satoshis_per_kb / 10e8)

    # create the transaction output
    tx_output = TxOutput(to_satoshis(Decimal(total_unspent) - Decimal(fee)),
                         rec_pk.get_address().to_script_pub_key())
    # set a lock time in blocks for the transaction
    lock = Locktime(block_height)
    # create the transaction
    tx = Transaction(tx_inputs, [tx_output], lock.for_transaction())
    unsigned_tx = tx.serialize()
    print('Raw unsigned transaction:', unsigned_tx)

    # we need to rebuild the redeem script from the P2SH private key
    # make the locking P2PKH address private key
    sender_sk = PrivateKey(sender_priv_key)
    # retrieve the public key of the locking address
    sender_pk = sender_sk.get_public_key()
    # rebuild the redeem script
    redeem_script = Script([
        seq.for_script(), 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP',
        'OP_HASH160',
        sender_pk.get_address().to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'
    ])

    # for every input of the transaction
    for i, txin in enumerate(tx.inputs):
        # create the signature for redeeming the funds
        sig = sender_sk.sign_input(tx, i, redeem_script)
        # and sign the input
        txin.script_sig = Script(
            [sig, sender_pk.to_hex(),
             redeem_script.to_hex()])

    signed_tx = tx.serialize()
    print('Raw signed transaction:', signed_tx)
    print('Transaction ID:', tx.get_txid())

    # verify that the transaction is valid
    ver = proxy.testmempoolaccept([signed_tx])
    if ver[0]['allowed']:
        # if the transaction is valid send the transaction to the blockchain
        print('Transaction is valid.')
        proxy.sendrawtransaction(signed_tx)
        print('{} Bitcoin sent to address {}'.format(
            Decimal(total_unspent) - Decimal(fee), rec_addr))
    else:
        # otherwise, display the reason the transaction failed
        print('Transaction rejected. Reason:', ver[0]['reject-reason'])
Esempio n. 17
0
 def test_pubkey_to_hash160(self):
     pub = PublicKey(self.public_key_hex)
     self.assertEqual(pub.get_address().to_hash160(), pub.to_hash160())
def load_data_json(priv=False,
                   pub=False,
                   pub_hash=False,
                   timelock_csv=False,
                   timelock_tx=False,
                   timelock_log=False,
                   p2pk=False):
    """loads parameters used to create P2SH address and spending transactions
    from the data.json file - initiates/converts applicable objects/data

    returns : dict of data for the Attributes specified
        - e.g {'priv_key': object, 'pub': object, 'pub_hash': 'string'}
    """

    # reads user data from data.json
    with open('data.json', 'r') as json_file:
        data = json.load(json_file)
        usr_inputs = data['csv_timelock']['inputs']

    # initiates key objects
    if priv or pub or pub_hash:
        usr_priv = usr_inputs['priv_key']
        usr_pub = usr_inputs['pub_key']
        if priv and not usr_priv:
            errors.missing_data('private key')
        elif usr_priv:
            priv_key = PrivateKey.from_wif(usr_priv)
            pub_key = priv_key.get_public_key()
        elif usr_pub:
            pub_key = PublicKey.from_hex(usr_pub)
        else:
            # prints error msg & raises systemExit
            errors.missing_data('public key')

    # loads timelock condition and converts format
    if timelock_csv or timelock_tx or timelock_log:
        lock_height = usr_inputs['block_lock']
        if lock_height:
            seq = Sequence(TYPE_RELATIVE_TIMELOCK, lock_height)
            timelock_script = seq.for_script()
            timelock_txin = seq.for_input_sequence()
        else:
            # prints error msg & raises systemExit
            errors.missing_data('timelock period')

    # initaites P2pkhAddress object
    if p2pk:
        address = usr_inputs['p2pkh_address']
        if address:
            p2pkh_address = P2pkhAddress.from_address(address)
        else:
            # prints error msg & raises systemExit
            errors.missing_data('P2PKH address')

    # fills return outputs with specified data from func arguments
    outputs = {}
    if priv:
        outputs['priv_key'] = priv_key
    if pub:
        outputs['pub_key'] = pub_key
    if pub_hash:
        outputs['pub_key_hash'] = pub_key.to_hash160()
    if timelock_csv:
        outputs['timelock_csv'] = timelock_script
    if timelock_tx:
        outputs['timelock_tx'] = timelock_txin
    if timelock_log:
        outputs['timelock_log'] = lock_height
    if p2pk:
        outputs['p2pk_addr'] = p2pkh_address

    return outputs
Esempio n. 19
0
 def test_get_uncompressed_address(self):
     pub = PublicKey(self.public_key_hex)
     self.assertEqual(
         pub.get_address(compressed=False).to_string(), self.address)
Esempio n. 20
0
 def test_pubkey_uncompressed_from_hex(self):
     pub = PublicKey.from_hex(self.public_key_hex)
     self.assertEqual(pub.to_hex(compressed=False), self.public_key_hex)
Esempio n. 21
0
 def test_pubkey_creation(self):
     pub1 = PublicKey(self.public_key_hex)
     self.assertEqual(pub1.to_bytes(), self.public_key_bytes)
     pub2 = PublicKey(self.public_key_hexc)
     self.assertEqual(pub2.to_bytes(), self.public_key_bytes)
Esempio n. 22
0
 def test_verify_external(self):
     self.assertTrue(
         PublicKey.verify_message(self.external_address,
                                  self.external_signature, self.message))
Esempio n. 23
0
 def test_sign_and_verify(self):
     signature = self.priv.sign_message(self.message)
     self.assertEqual(signature, self.deterministic_signature)
     self.assertTrue(
         PublicKey.verify_message(self.address, signature, self.message))