Introduction
This is Part 3 of my F# blockchain implementation. Previous parts:
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 }