1
\$\begingroup\$

Introduction

This is Part 3 of my F# blockchain implementation. Previous parts:

  1. Block and Blockchain
  2. Transactions

Code

In this part I am implementing a wallet functionality. The code is based on a JavaScript blockchain implementation called naivecoin. The source code for naivecoin transactions can be found here.

namespace Blockchain.Core

open System.Security.Cryptography

open Helpers

type Wallet() =
    let keyName = "blockchain"

    member private this.Key =
        if CngKey.Exists(keyName) then 
            CngKey.Open(keyName)
        else
            CngKey.Create(CngAlgorithm.ECDsaP521, keyName)

    member this.Address = this.Key.Export(CngKeyBlobFormat.EccPublicBlob).ToHex()

    member private this.GetBalance(address: string, unspentOutputs: UnspentTransactionOutput seq) =
        unspentOutputs
        |> Seq.filter (fun x -> x.address = address)
        |> Seq.map (fun x -> x.amount)
        |> Seq.fold (+) 0.0

    member private this.FindOutputsWithChangeForTransaction(transactionAmount: float, unspentOutputs: UnspentTransactionOutput seq) =
        let rec findOutputsWithChange currentAmount (unspentOutputs : UnspentTransactionOutput seq) (consumedOutputs : UnspentTransactionOutput seq) =
            if currentAmount >= transactionAmount then
                (consumedOutputs, currentAmount - transactionAmount)
            else if Seq.isEmpty unspentOutputs then
                failwith "Not enough coins to send transaction."
            else
                let currentOutput = unspentOutputs |> Seq.head
                findOutputsWithChange (currentAmount + currentOutput.amount) (unspentOutputs |> Seq.skip 1) (consumedOutputs |> Seq.append [| currentOutput |])

        findOutputsWithChange 0.0 unspentOutputs []     

    member private this.CreateOutputs(receiverAddress: string, amount: float, change: float) =
        if change = 0.0 then
            Seq.ofArray [| { address = receiverAddress; amount = amount } |] 
        else
            Seq.ofArray [| { address = receiverAddress; amount = amount}; { address = this.Address; amount = change } |]

    member this.CreateTransaction(receiverAddress: string, amount: float, unspentOutputs: UnspentTransactionOutput seq) =
        let myUnspentOutputs = unspentOutputs |> Seq.filter (fun x -> x.address = this.Address)
        let (consumedOutputs, change) = this.FindOutputsWithChangeForTransaction(amount, myUnspentOutputs)
        let unsignedInputs = consumedOutputs |> Seq.map (fun x -> { sourceOutputTransactionId = x.transactionId; sourceOutputIndex = x.outputIndex; signature = "" })
        let outputs = this.CreateOutputs(receiverAddress, amount, change)
        let processor = new TransactionProcessor()
        let transactionId = processor.GetTransactionId(unsignedInputs, outputs)
        let tempTransaction = { id = transactionId; inputs = unsignedInputs |> Array.ofSeq; outputs = outputs |> Array.ofSeq }
        let inputs = unsignedInputs |> Seq.mapi (fun i x -> { x with signature = processor.SignTransactionInput(tempTransaction, i, this.Key, myUnspentOutputs) })
        { tempTransaction with inputs = inputs |> Array.ofSeq }
\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.