From 5661469aee5d980b95de7e29f74e21f6bfa7558d Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Mon, 28 Jun 2021 15:04:50 +0200 Subject: [PATCH] Transactions --- abc.nimble | 1 + abc/transactions.nim | 52 ++++++++++++++++++++++++++++++++++++++ tests/examples.nim | 6 +++++ tests/testTransactions.nim | 42 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 abc/transactions.nim create mode 100644 tests/testTransactions.nim diff --git a/abc.nimble b/abc.nimble index 376c973..1d917bb 100644 --- a/abc.nimble +++ b/abc.nimble @@ -6,4 +6,5 @@ license = "MIT" requires "questionable >= 0.10.0 & < 0.11.0" requires "https://github.com/markspanbroek/nim-blscurve#fix-nimble" requires "nimcrypto" +requires "stint" requires "stew" diff --git a/abc/transactions.nim b/abc/transactions.nim new file mode 100644 index 0000000..df2cba3 --- /dev/null +++ b/abc/transactions.nim @@ -0,0 +1,52 @@ +import pkg/nimcrypto +import pkg/stint +import ./keys + +export stint +export keys + +type + Transaction* = object + inputs: seq[TxInput] + outputs: seq[TxOutput] + signature: Signature + TxInput* = tuple + txHash: TxHash + owner: PublicKey + TxOutput* = tuple + owner: PublicKey + amount: UInt256 + TxHash* = distinct MDigest[256] + +func `==`*(a, b: TxHash): bool {.borrow.} + +func init*(_: type Transaction, + inputs: openArray[TxInput], + outputs: openArray[TxOutput]): Transaction = + Transaction(inputs: @inputs, outputs: @outputs) + +func init*(_: type Transaction, + outputs: openArray[TxOutput]): Transaction = + Transaction.init([], outputs) + +func inputs*(transaction: Transaction): seq[TxInput] = + transaction.inputs + +func outputs*(transaction: Transaction): seq[TxOutput] = + transaction.outputs + +func toBytes*(hash: TxHash): array[32, byte] = + MDigest[256](hash).data + +func toBytes*(transaction: Transaction): seq[byte] = + result.add(transaction.inputs.len.uint8) + for (txHash, owner) in transaction.inputs: + result.add(txHash.toBytes) + result.add(owner.toBytes) + result.add(transaction.outputs.len.uint8) + for (owner, amount) in transaction.outputs: + result.add(owner.toBytes) + result.add(amount.toBytes) + +func hash*(transaction: Transaction): TxHash = + TxHash(sha256.digest(transaction.toBytes)) diff --git a/tests/examples.nim b/tests/examples.nim index f8baff3..ebe3854 100644 --- a/tests/examples.nim +++ b/tests/examples.nim @@ -1,7 +1,13 @@ import abc/keys +import abc/transactions proc example*(_: type PrivateKey): PrivateKey = PrivateKey.random proc example*(_: type PublicKey): PublicKey = PrivateKey.example.toPublicKey + +proc example*(_: type Transaction): Transaction = + let alice, bob = PublicKey.example + let genesis = Transaction.init({alice: 32.u256, bob: 10.u256}) + Transaction.init({genesis.hash: alice}, {alice: 2.u256, bob: 30.u256}) diff --git a/tests/testTransactions.nim b/tests/testTransactions.nim new file mode 100644 index 0000000..ffd83aa --- /dev/null +++ b/tests/testTransactions.nim @@ -0,0 +1,42 @@ +import std/unittest +import abc/transactions +import ./examples + +suite "Transactions": + + let alice, bob = PublicKey.example + + test "a genesis transaction can be made": + let genesis = Transaction.init({alice: 32.u256, bob: 10.u256}) + + test "a transaction has a hash": + let transaction1, transaction2 = Transaction.example + check transaction1.hash == transaction1.hash + check transaction2.hash == transaction2.hash + check transaction1.hash != transaction2.hash + + test "a transaction references outputs from other transactions": + let genesis = Transaction.init({alice: 32.u256, bob: 10.u256}) + let transaction = Transaction.init( + {genesis.hash: alice}, + {alice: 2.u256, bob: 30.u256} + ) + check transaction.inputs.len == 1 + check transaction.outputs.len == 2 + + test "a transaction can be converted to bytes": + let genesis = Transaction.init({alice: 32.u256, bob: 10.u256}) + let transaction = Transaction.init( + {genesis.hash: alice}, + {alice: 2.u256, bob: 30.u256} + ) + var expected: seq[byte] + expected.add(1) # amount of inputs + expected.add(genesis.hash.toBytes) + expected.add(alice.toBytes) + expected.add(2) # amount of outputs + expected.add(alice.toBytes) + expected.add(2.u256.toBytes) + expected.add(bob.toBytes) + expected.add(30.u256.toBytes) + check transaction.toBytes == expected