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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.