diff --git a/abc/transactions.nim b/abc/transactions.nim index bc8f8e9..6f26c68 100644 --- a/abc/transactions.nim +++ b/abc/transactions.nim @@ -13,6 +13,7 @@ type Transaction* = object inputs: seq[TxInput] outputs: seq[TxOutput] + validator: PublicKey signature: Signature TxInput* = tuple txHash: TxHash @@ -26,18 +27,20 @@ func `==`*(a, b: TxHash): bool {.borrow.} func init*(_: type Transaction, inputs: openArray[TxInput], - outputs: openArray[TxOutput]): ?Transaction = + outputs: openArray[TxOutput], + validator: PublicKey): ?Transaction = if outputs.len == 0: return none Transaction if outputs.map(output => output.owner).hasDuplicates: return none Transaction - some Transaction(inputs: @inputs, outputs: @outputs) + some Transaction(inputs: @inputs, outputs: @outputs, validator: validator) func init*(_: type Transaction, - outputs: openArray[TxOutput]): ?Transaction = - Transaction.init([], outputs) + outputs: openArray[TxOutput], + validator: PublicKey): ?Transaction = + Transaction.init([], outputs, validator) func inputs*(transaction: Transaction): seq[TxInput] = transaction.inputs @@ -48,6 +51,9 @@ func outputs*(transaction: Transaction): seq[TxOutput] = func signature*(transaction: Transaction): Signature = transaction.signature +func validator*(transaction: Transaction): PublicKey = + transaction.validator + func add*(transaction: var Transaction, signature: Signature) = transaction.signature = aggregate(transaction.signature, signature) diff --git a/tests/abc/alicebob.nim b/tests/abc/alicebob.nim index bc2f18e..1801aeb 100644 --- a/tests/abc/alicebob.nim +++ b/tests/abc/alicebob.nim @@ -1,7 +1,7 @@ import pkg/questionable import abc -let a, b = PrivateKey.random +let a, b, v = PrivateKey.random proc alice*(_: type PrivateKey): PrivateKey = a @@ -9,13 +9,20 @@ proc alice*(_: type PrivateKey): PrivateKey = proc bob*(_: type PrivateKey): PrivateKey = b +proc victor*(_: type PrivateKey): PrivateKey = + v + proc alice*(_: type PublicKey): PublicKey = a.toPublicKey proc bob*(_: type PublicKey): PublicKey = b.toPublicKey +proc victor*(_: type PublicKey): PublicKey = + v.toPublicKey + proc genesis*(_: type Transaction): Transaction = let alice = PublicKey.alice let bob = PublicKey.bob - !Transaction.init({alice: 100.u256, bob: 100.u256}) + let victor = PublicKey.victor + !Transaction.init({alice: 100.u256, bob: 100.u256}, victor) diff --git a/tests/abc/examples.nim b/tests/abc/examples.nim index 36d7ae0..fcc9137 100644 --- a/tests/abc/examples.nim +++ b/tests/abc/examples.nim @@ -16,10 +16,12 @@ proc example*(_: type Wallet): Wallet = proc example*(_: type Transaction): Transaction = let alice = PublicKey.alice let bob = PublicKey.bob + let victor = PublicKey.victor let genesis = Transaction.genesis let amount = rand(100).u256 var transaction = !Transaction.init( {genesis.hash: alice}, - {bob: amount, alice: 100.u256 - amount} + {bob: amount, alice: 100.u256 - amount}, + victor ) transaction diff --git a/tests/abc/testTransactions.nim b/tests/abc/testTransactions.nim index cdb9212..f7d2cf4 100644 --- a/tests/abc/testTransactions.nim +++ b/tests/abc/testTransactions.nim @@ -5,9 +5,10 @@ suite "Transactions": let alice = PublicKey.alice let bob = PublicKey.bob + let victor = PublicKey.victor test "a genesis transaction can be made": - let genesis = Transaction.init({alice: 32.u256, bob: 10.u256}) + let genesis = Transaction.init({alice: 32.u256, bob: 10.u256}, victor) check genesis.isSome test "a transaction has a hash": @@ -17,19 +18,21 @@ suite "Transactions": check transaction1.hash != transaction2.hash test "a transaction references outputs from other transactions": - let genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}) + let genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}, victor) let transaction = !Transaction.init( {genesis.hash: alice}, - {alice: 2.u256, bob: 30.u256} + {alice: 2.u256, bob: 30.u256}, + victor ) 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 genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}, victor) let transaction = !Transaction.init( {genesis.hash: alice}, - {alice: 2.u256, bob: 30.u256} + {alice: 2.u256, bob: 30.u256}, + victor ) var expected: seq[byte] expected.add(1) # amount of inputs @@ -59,11 +62,12 @@ suite "Transactions": check transaction.signature == key.sign(transaction.hash.toBytes) test "transaction signature can be checked for validity": - let genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}) + let genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}, victor) check not genesis.hasValidSignature() var transaction = !Transaction.init( {genesis.hash: alice}, - {alice: 2.u256, bob: 30.u256} + {alice: 2.u256, bob: 30.u256}, + victor ) let hash = transaction.hash.toBytes check not transaction.hasValidSignature @@ -73,7 +77,7 @@ suite "Transactions": check not transaction.hasValidSignature test "transaction must have at least one output": - check Transaction.init([]).isNone + check Transaction.init([], victor).isNone test "multiple outputs to the same owner are not allowed": - check Transaction.init({alice: 40.u256, alice: 2.u256}).isNone + check Transaction.init({alice: 40.u256, alice: 2.u256}, victor).isNone diff --git a/tests/abc/testTxValidation.nim b/tests/abc/testTxValidation.nim index 3c9ff14..0ef6770 100644 --- a/tests/abc/testTxValidation.nim +++ b/tests/abc/testTxValidation.nim @@ -7,12 +7,13 @@ suite "Transaction validation": let alice = PublicKey.alice let bob = PublicKey.bob + let victor = PublicKey.victor let genesis = Transaction.genesis var tx1, tx2: Transaction setup: - tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}) - tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}) + tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor) + tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}, victor) PrivateKey.alice.sign(tx1) PrivateKey.bob.sign(tx2) @@ -30,20 +31,20 @@ suite "Transaction validation": test "checks that inputs and outputs match": var store = TxStore.init(genesis) - var invalid1 = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}) - var invalid2 = !Transaction.init({invalid1.hash: bob}, {alice: 999.u256}) - PrivateKey.alice.sign(invalid1) - PrivateKey.bob.sign(invalid2) - store.add(invalid1, invalid2) - check not store.hasValidTx(invalid1.hash) - check not store.hasValidTx(invalid2.hash) + var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}, victor) + var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 999.u256}, victor) + PrivateKey.alice.sign(bad1) + PrivateKey.bob.sign(bad2) + store.add(bad1, bad2) + check not store.hasValidTx(bad1.hash) + check not store.hasValidTx(bad2.hash) test "checks that signatures match": var store = TxStore.init(genesis) - var invalid1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}) - var invalid2 = !Transaction.init({invalid1.hash: bob}, {alice: 100.u256}) - PrivateKey.bob.sign(invalid1) # invalid signature, should be signed by alice - PrivateKey.bob.sign(invalid2) - store.add(invalid1, invalid2) - check not store.hasValidTx(invalid1.hash) - check not store.hasValidTx(invalid2.hash) + var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor) + var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 100.u256}, victor) + PrivateKey.bob.sign(bad1) # invalid signature, should be signed by alice + PrivateKey.bob.sign(bad2) + store.add(bad1, bad2) + check not store.hasValidTx(bad1.hash) + check not store.hasValidTx(bad2.hash)