예제 #1
0
def make_miner_keys():
    """
    makes a public-private key pair that the miner will use to receive
    the mining reward and the transaction fee for each transaction.
    This function writes the  keys to a file and returns hash: 
    RIPEMD160(SHA256(public key))
    """

    try:
        keys = rcrypt.make_ecc_keys()
        privkey = keys[0]
        pubkey = keys[1]

        pkhash = rcrypt.make_SHA256_hash(pubkey)
        mdhash = rcrypt.make_RIPEMD160_hash(pkhash)

        # write the keys to file with the private key as a hexadecimal string
        f = open('coinbase_keys.txt', 'a')
        f.write(privkey)
        f.write('\n')  # newline
        f.write(pubkey)
        f.write('\n')
        f.close()

    except Exception as err:
        logging.debug('make_miner_keys: exception: ' + str(err))

    return mdhash
예제 #2
0
def test_make_coinbase_transaction():
    """
    tests making a coinbase transaction
    """
    ctx = hmining.make_coinbase_transaction(10, "synthetic pubkey")
    assert len(ctx["vin"]) == 0
    assert len(ctx["vout"]) == 1
    hash = rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash("synthetic pubkey"))
    assert ctx["vout"][0]["ScriptPubKey"][1] == hash
def unlock_transaction_fragment(vinel: "dictionary",
                                fragment: "dictionary") -> "boolean":
    """
    unlocks a previous transaction fragment using the p2pkhash script.
    Executes the script and returns False if the transaction is not unlocked
    Receives: the consuming vin and the previous transaction fragment consumed
    by the vin element.
    """
    try:
        execution_stack = []
        result_stack = []

        # make the execution stack for a p2pkhash script
        # since we only have one type of script in Helium
        # we can use hard-coded values
        execution_stack.append('SIG')
        execution_stack.append('PUBKEY')
        execution_stack.append('<DUP>')
        execution_stack.append('<HASH_160>')
        execution_stack.append('HASH-160')
        execution_stack.append('<EQ-VERIFY>')
        execution_stack.append('<CHECK_SIG>')

        # Run the p2pkhash execution stack
        result_stack.insert(0, vinel['ScriptSig'][0])
        result_stack.insert(0, vinel['ScriptSig'][1])
        result_stack.insert(0, vinel['ScriptSig'][1])

        hash_160 = rcrypt.make_SHA256_hash(vinel['ScriptSig'][1])
        hash_160 = rcrypt.make_RIPEMD160_hash(hash_160)

        result_stack.insert(0, hash_160)
        result_stack.insert(0, fragment["pkhash"])

        # do the EQ_VERIFY operation
        tmp1 = result_stack.pop(0)
        tmp2 = result_stack.pop(0)

        # test for RIPEMD-160 hash match
        if tmp1 != tmp2:
            raise (ValueError("public key match failure"))

        # test for a signature match
        ret = rcrypt.verify_signature(vinel['ScriptSig'][1],
                                      vinel['ScriptSig'][1],
                                      vinel['ScriptSig'][0])
        if ret == False:
            raise (ValueError("signature match failure"))

    except Exception as err:
        logging.debug('unlock_transaction_fragment: exception: ' + str(err))
        return False

    return True
def make_synthetic_vout(block, trx, vout_index, pubkey):
    """
    make a randomized vout element
    receives a public key
    returns the vout dict element
    """
    vout = {}

    # genesis transaction
    # spendable values
    if block["height"] == 0:
        vout['value'] = secrets.randbelow(
            10_000_000_000_000) + 10  # helium cents
    else:
        value_received = 0
        ctr = 0
        for vin in trx["vin"]:
            fragmentid = vin["txid"] + "_" + str(ctr)
            fragment = hchaindb.get_transaction(fragmentid)
            if fragment == False: break
            value_received += fragment["value"]
            ctr += 1
            if value_received < 0:
                raise (ValueError("negative value receive by vin"))

        if value_received == 0: return False
        value_spent = 0
        for vout in trx["vout"]:
            value_spent += vout["value"]

        value_available = value_received - value_spent
        if value_available < 0:
            raise (ValueError("funds available are negative"))
        if value_available == 0: return False
        vout['value'] = secrets.randbelow(value_available) + 1000

    ripemd_hash = rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(pubkey))
    tmp = []
    tmp.append('<DUP>')
    tmp.append('<HASH-160>')
    tmp.append(ripemd_hash)
    tmp.append('<EQ-VERIFY>')
    tmp.append('<CHECK-SIG>')
    vout["ScriptPubKey"] = []
    vout['ScriptPubKey'].append(tmp)

    #add fragment to wallet
    fragment["transactionid"] = trx["transactionid"]
    fragment["vout_index"] = vout_index
    wallet.append(fragment)

    return vout
def test_pubkeyhash():
    """
    test a public key hash in scriptpubkey for a 
    valid RIPEMD-160 format
    """
    txn = make_synthetic_transaction(5)

    indices = randomize_list(txn['vout'])

    for ctr in list(range(len(indices))):
        val = txn['vout'][indices[ctr]]['ScriptPubKey'][2]
        val = rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(val)) 
        assert rcrypt.validate_RIPEMD160_hash(val) == True
예제 #6
0
def value_received(blockno:"integer"= 0) -> "list" :
    """
    obtains all of the helium values received by the wallet-holder by examining
    transactions in the blockchain from and including blockno onwards.
    Updates the wallet state. 
    """
    hreceived = []
    tmp = {}

    try:
    # get values received from the blockchain
        for block in hblockchain.blockchain:
            if block["height"] < blockno: continue

            for transaction in block["tx"]:
                ctr = -1
                for vout in transaction["vout"]:
                    ctr += 1
                    for key_pair in wallet_state["keys"]:
                        if rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(key_pair[1])) \
                             == vout["ScriptPubKey"][2]: 
                            tmp["value"] = vout["value"]
                            tmp["blockno"] = block["height"]             
                            tmp["fragmentid"] = transaction["transactionid"] + "_" + str(ctr)
                            tmp["public_key"] = key_pair[1]         
                            hreceived.append(tmp)
                            break

        # update the wallet state
        last_block = hblockchain.blockchain[-1]                  
        if last_block["height"] > wallet_state["received_last_block_scanned"]:
            wallet_state["received_last_block_scanned"] = last_block["height"]

        for received in hreceived:
            wallet_state["received"].append(received)

    
    except Exception as err:
        print(str(err))
        logging.debug('value_received exception: ' + str(err))
        return False
        

    return
예제 #7
0
def value_spent(blockno:"integer"= 0):
    """
    obtains all of the helium values transferred by the wallet-holder by
    examining transactions in the blockchain from and including blockno.
    onwards. Updates the wallet state. 
    """
    hspent = []
    tvalue = {}

    try:
        # get values spent from  blockchain transactions
        for block in hblockchain.blockchain:
            if block["height"] < blockno: continue

            for transaction in block["tx"]:
                for vin in transaction["vin"]:
                    for key_pair in wallet_state["keys"]:
                        prevtxid =  vin["transactionid"] + "_" + str(vin["vout_index"])
                        fragment = hchaindb.get_transaction(prevtxid)

                        if rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(key_pair[1])) \
                             == fragment["pkhash"] : 
                            tvalue["value"] = vin["value"]
                            tvalue["blockno"] = block["height"]             
                            tvalue["fragmentid"] = vin["transactionid"] + "_" \
                                + str(vin["vout_index"])
                            tvalue["public_key"] = key_pair[1]         
                            hspent.append(tvalue)
                            break

        last_block = hblockchain.blockchain[-1]                  
        if last_block["height"] > wallet_state["spent_last_block_scanned"]:
            wallet_state["spent_last_block_scanned"] = last_block["height"]

        for spent in hspent:
            wallet_state["spent"].append(spent)

    except Exception as err:
        print(str(err))
        logging.debug('value_sent: exception: ' + str(err))
        return False

    return        
def test_unlock_bad_signature(monkeypatch):
    """ 
    test unlocking a transaction with a bad signature
    """
    global prev_tx_keys

    prev_tx_keys.clear()
    txn1 = make_synthetic_previous_transaction(4)
    txn2 = make_synthetic_transaction(2)

    # make a transaction fragment where the first vin element
    # of txn2 consumes the value of the first vout element of txn1

    # synthetic consuming vin element in tx2
    vin = {}
    vin['txid'] = txn1["transactionid"]
    vin['vout_index'] = 0
    vin['ScriptSig'] = {}

    # use wrong private key to sign 
    key_pair = rcrypt.make_ecc_keys()

    signature = rcrypt.sign_message(key_pair[0], prev_tx_keys[1][1])
    pubkey = prev_tx_keys[1][1]  
    sig = []
    sig.append(signature)
    sig.append(pubkey + "corrupted")
    vin['ScriptSig'] = sig
                                        
    # public key hash in txn2
    ripemd_hash = rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(prev_tx_keys[1][1])) 

    fragment = {
        "value": 210,
        "pkhash": ripemd_hash,
        "spent": False,
        "tx_chain": txn2["transactionid"] + "_" + "0",
        "checksum":   rcrypt.make_SHA256_hash(txn1["transactionid"])
    }
    
    assert tx.unlock_transaction_fragment(vin, fragment) == False
예제 #9
0
def make_coinbase_transaction(block_height: "integer",
                              pubkey: "string") -> 'dict':
    """
    makes a coinbase transaction, this is the miner's reward for mining 
    a block. Receives a public key to denote ownership of the reward. 
    Since this is a fresh issuance of heliums there are no vin elements.
    locks the transaction for hconfig["COINBASE_INTERVAL"] blocks.
    Returns the coinbase transaction.
    """
    try:
        # calculate the mining reward
        reward = mining_reward(block_height)

        # create a coinbase transaction
        trx = {}
        trx['transactionid'] = rcrypt.make_uuid()
        trx['version'] = hconfig.conf["VERSION_NO"]
        # the mining reward cannot be claimed until approximately 100 blocks are mined
        # convert into a time interval
        trx['locktime'] = hconfig.conf["COINBASE_INTERVAL"] * 600
        trx['vin'] = []
        trx['vout'] = []

        ScriptPubKey = []
        ScriptPubKey.append('SIG')
        ScriptPubKey.append(
            rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(pubkey)))
        ScriptPubKey.append('<DUP>')
        ScriptPubKey.append('<HASH_160>')
        ScriptPubKey.append('HASH-160')
        ScriptPubKey.append('<EQ-VERIFY>')
        ScriptPubKey.append('<CHECK_SIG>')

        # create the vout element with the reward
        trx['vout'].append({'value': reward, 'ScriptPubKey': ScriptPubKey})

    except Exception as err:
        logging.debug('make_coinbase_transaction: exception: ' + str(err))

    return trx
예제 #10
0
def add_transaction_fee(trx: 'dictionary', pubkey: 'string') -> 'dictionary':
    """
    add_transaction_fee directs the transaction fee of a transaction to 
    the miner.
    receives a transaction and a miner's public key.
    amends and returns the transaction so that it consumes the transaction fee.
    """

    try:
        # get the previous transaction fragments
        prev_fragments = []

        for vin in trx["vin"]:
            fragment_id = vin["txid"] + "_" + str(vin["vout_index"])
            prev_fragments.append(hchaindb.get_transaction(fragment_id))

        #  Calculate the transaction fee
        fee = tx.transaction_fee(trx, prev_fragments)

        if fee > 0:
            vout = {}
            vout["value"] = fee
            ScriptPubKey = []
            ScriptPubKey.append('SIG')
            ScriptPubKey.append(
                rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(pubkey)))
            ScriptPubKey.append('<DUP>')
            ScriptPubKey.append('<HASH_160>')
            ScriptPubKey.append('HASH-160')
            ScriptPubKey.append('<EQ-VERIFY>')
            ScriptPubKey.append('<CHECK_SIG>')
            vout["ScriptPubKey"] = ScriptPubKey

            trx["vout"].append(vout)

    except Exception as err:
        logging.debug('add_transaction_fee: exception: ' + str(err))
        return False

    return trx
def make_random_transaction(blockno, is_coinbase):

    txn = {}
    txn["version"] = "1"
    txn["transactionid"] = rcrypt.make_uuid()
    if is_coinbase == True: txn["locktime"] = hconfig.conf["COINBASE_LOCKTIME"]
    else: txn["locktime"] = 0

    # the public-private key pair for this transaction
    transaction_keys = rcrypt.make_ecc_keys()

    # previous transaction fragments spent by this transaction
    total_spendable = 0

    #######################
    # Build the vin array
    #######################
    txn["vin"] = []

    # genesis block transactions have no prior inputs.
    # coinbase transactions do not have any inputs
    if (blockno > 0) and (is_coinbase != True):
        max_inputs = secrets.randbelow(hconfig.conf["MAX_INPUTS"])
        if max_inputs == 0: max_inputs = hconfig.conf["MAX_INPUTS"] - 1

        # get some random previous unspent transaction
        # fragments to spend
        ind = 0
        ctr = 0

        while ind < max_inputs:
            # get a random unspent fragment from a previous block
            index = secrets.randbelow(len(unspent_fragments))
            frag_dict = unspent_fragments[index]
            key = [*frag_dict.keys()][0]
            val = [*frag_dict.values()][0]
            if val["blockno"] == blockno:
                ctr += 1
                if ctr == 10000:
                    print("failed to get random unspent fragment")
                    return False
                continue

            unspent_fragments.pop(index)

            total_spendable += val["value"]
            tmp = hchaindb.get_transaction(key)
            if tmp == False:
                print("cannot get fragment from chainstate: " + key)

            assert tmp != False
            assert tmp["spent"] == False
            assert tmp["value"] > 0

            # create a random vin element
            key_array = key.split("_")

            signed = rcrypt.sign_message(val["privkey"], val["pubkey"])
            ScriptSig = []
            ScriptSig.append(signed)
            ScriptSig.append(val["pubkey"])

            txn["vin"].append({
                "txid": key_array[0],
                "vout_index": int(key_array[1]),
                "ScriptSig": ScriptSig
            })

            ctr = 0
            ind += 1

    #####################
    # Build Vout list
    #####################
    txn["vout"] = []

    # genesis block
    if blockno == 0:
        total_spendable = secrets.randbelow(10_000_000) + 50_000

    # we need at least one transaction output for non-coinbase
    # transactions
    if is_coinbase == True: max_outputs = hconfig.conf["MAX_OUTPUTS"]
    else:
        max_outputs = secrets.randbelow(hconfig.conf["MAX_OUTPUTS"])
        if max_outputs <= 1: max_outputs = 2

    ind = 0

    while ind < max_outputs:
        tmp = rcrypt.make_SHA256_hash(transaction_keys[1])
        tmp = rcrypt.make_RIPEMD160_hash(tmp)

        ScriptPubKey = []
        ScriptPubKey.append("<DUP>")
        ScriptPubKey.append("<HASH-160>")
        ScriptPubKey.append(tmp)
        ScriptPubKey.append("<EQ-VERIFY>")
        ScriptPubKey.append("<CHECK-SIG>")

        if is_coinbase == True:
            value = hmining.mining_reward(blockno)
        else:
            amt = int(total_spendable / max_outputs)
            value = secrets.randbelow(amt)  # helium cents
            if value == 0:
                value = int(amt / 10)
            total_spendable -= value
            assert value > 0
            assert total_spendable >= 0

        txn["vout"].append({"value": value, "ScriptPubKey": ScriptPubKey})

        # save the transaction fragment
        fragid = txn["transactionid"] + "_" + str(ind)
        fragment = {}
        fragment[fragid] = {
            "value": value,
            "privkey": transaction_keys[0],
            "pubkey": transaction_keys[1],
            "blockno": blockno
        }
        unspent_fragments.append(fragment)
        #print("added to unspent fragments: " + fragid)

        if total_spendable <= 0: break
        ind += 1

    return txn
def test_make_RIPEMD160_hash(string_input, value):
    """
    validate that a valid RIPEMD-160 message digest format is generated
    """
    ret = rcrypt.make_RIPEMD160_hash(string_input)
    assert rcrypt.validate_RIPEMD160_hash(ret) == value
예제 #13
0
def make_pkhash():
    """
    creates a random pkhash value
    """
    id = rcrypt.make_uuid()
    return rcrypt.make_RIPEMD160_hash(rcrypt.make_SHA256_hash(id))