Add validator to transactions

This commit is contained in:
Mark Spanbroek 2021-06-30 13:49:19 +02:00
parent 96908b01b5
commit c054636381
5 changed files with 52 additions and 32 deletions

View File

@ -13,6 +13,7 @@ type
Transaction* = object Transaction* = object
inputs: seq[TxInput] inputs: seq[TxInput]
outputs: seq[TxOutput] outputs: seq[TxOutput]
validator: PublicKey
signature: Signature signature: Signature
TxInput* = tuple TxInput* = tuple
txHash: TxHash txHash: TxHash
@ -26,18 +27,20 @@ func `==`*(a, b: TxHash): bool {.borrow.}
func init*(_: type Transaction, func init*(_: type Transaction,
inputs: openArray[TxInput], inputs: openArray[TxInput],
outputs: openArray[TxOutput]): ?Transaction = outputs: openArray[TxOutput],
validator: PublicKey): ?Transaction =
if outputs.len == 0: if outputs.len == 0:
return none Transaction return none Transaction
if outputs.map(output => output.owner).hasDuplicates: if outputs.map(output => output.owner).hasDuplicates:
return none Transaction return none Transaction
some Transaction(inputs: @inputs, outputs: @outputs) some Transaction(inputs: @inputs, outputs: @outputs, validator: validator)
func init*(_: type Transaction, func init*(_: type Transaction,
outputs: openArray[TxOutput]): ?Transaction = outputs: openArray[TxOutput],
Transaction.init([], outputs) validator: PublicKey): ?Transaction =
Transaction.init([], outputs, validator)
func inputs*(transaction: Transaction): seq[TxInput] = func inputs*(transaction: Transaction): seq[TxInput] =
transaction.inputs transaction.inputs
@ -48,6 +51,9 @@ func outputs*(transaction: Transaction): seq[TxOutput] =
func signature*(transaction: Transaction): Signature = func signature*(transaction: Transaction): Signature =
transaction.signature transaction.signature
func validator*(transaction: Transaction): PublicKey =
transaction.validator
func add*(transaction: var Transaction, signature: Signature) = func add*(transaction: var Transaction, signature: Signature) =
transaction.signature = aggregate(transaction.signature, signature) transaction.signature = aggregate(transaction.signature, signature)

View File

@ -1,7 +1,7 @@
import pkg/questionable import pkg/questionable
import abc import abc
let a, b = PrivateKey.random let a, b, v = PrivateKey.random
proc alice*(_: type PrivateKey): PrivateKey = proc alice*(_: type PrivateKey): PrivateKey =
a a
@ -9,13 +9,20 @@ proc alice*(_: type PrivateKey): PrivateKey =
proc bob*(_: type PrivateKey): PrivateKey = proc bob*(_: type PrivateKey): PrivateKey =
b b
proc victor*(_: type PrivateKey): PrivateKey =
v
proc alice*(_: type PublicKey): PublicKey = proc alice*(_: type PublicKey): PublicKey =
a.toPublicKey a.toPublicKey
proc bob*(_: type PublicKey): PublicKey = proc bob*(_: type PublicKey): PublicKey =
b.toPublicKey b.toPublicKey
proc victor*(_: type PublicKey): PublicKey =
v.toPublicKey
proc genesis*(_: type Transaction): Transaction = proc genesis*(_: type Transaction): Transaction =
let alice = PublicKey.alice let alice = PublicKey.alice
let bob = PublicKey.bob 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)

View File

@ -16,10 +16,12 @@ proc example*(_: type Wallet): Wallet =
proc example*(_: type Transaction): Transaction = proc example*(_: type Transaction): Transaction =
let alice = PublicKey.alice let alice = PublicKey.alice
let bob = PublicKey.bob let bob = PublicKey.bob
let victor = PublicKey.victor
let genesis = Transaction.genesis let genesis = Transaction.genesis
let amount = rand(100).u256 let amount = rand(100).u256
var transaction = !Transaction.init( var transaction = !Transaction.init(
{genesis.hash: alice}, {genesis.hash: alice},
{bob: amount, alice: 100.u256 - amount} {bob: amount, alice: 100.u256 - amount},
victor
) )
transaction transaction

View File

@ -5,9 +5,10 @@ suite "Transactions":
let alice = PublicKey.alice let alice = PublicKey.alice
let bob = PublicKey.bob let bob = PublicKey.bob
let victor = PublicKey.victor
test "a genesis transaction can be made": 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 check genesis.isSome
test "a transaction has a hash": test "a transaction has a hash":
@ -17,19 +18,21 @@ suite "Transactions":
check transaction1.hash != transaction2.hash check transaction1.hash != transaction2.hash
test "a transaction references outputs from other transactions": 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( let transaction = !Transaction.init(
{genesis.hash: alice}, {genesis.hash: alice},
{alice: 2.u256, bob: 30.u256} {alice: 2.u256, bob: 30.u256},
victor
) )
check transaction.inputs.len == 1 check transaction.inputs.len == 1
check transaction.outputs.len == 2 check transaction.outputs.len == 2
test "a transaction can be converted to bytes": 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( let transaction = !Transaction.init(
{genesis.hash: alice}, {genesis.hash: alice},
{alice: 2.u256, bob: 30.u256} {alice: 2.u256, bob: 30.u256},
victor
) )
var expected: seq[byte] var expected: seq[byte]
expected.add(1) # amount of inputs expected.add(1) # amount of inputs
@ -59,11 +62,12 @@ suite "Transactions":
check transaction.signature == key.sign(transaction.hash.toBytes) check transaction.signature == key.sign(transaction.hash.toBytes)
test "transaction signature can be checked for validity": 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() check not genesis.hasValidSignature()
var transaction = !Transaction.init( var transaction = !Transaction.init(
{genesis.hash: alice}, {genesis.hash: alice},
{alice: 2.u256, bob: 30.u256} {alice: 2.u256, bob: 30.u256},
victor
) )
let hash = transaction.hash.toBytes let hash = transaction.hash.toBytes
check not transaction.hasValidSignature check not transaction.hasValidSignature
@ -73,7 +77,7 @@ suite "Transactions":
check not transaction.hasValidSignature check not transaction.hasValidSignature
test "transaction must have at least one output": 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": 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

View File

@ -7,12 +7,13 @@ suite "Transaction validation":
let alice = PublicKey.alice let alice = PublicKey.alice
let bob = PublicKey.bob let bob = PublicKey.bob
let victor = PublicKey.victor
let genesis = Transaction.genesis let genesis = Transaction.genesis
var tx1, tx2: Transaction var tx1, tx2: Transaction
setup: setup:
tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}) tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}) tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}, victor)
PrivateKey.alice.sign(tx1) PrivateKey.alice.sign(tx1)
PrivateKey.bob.sign(tx2) PrivateKey.bob.sign(tx2)
@ -30,20 +31,20 @@ suite "Transaction validation":
test "checks that inputs and outputs match": test "checks that inputs and outputs match":
var store = TxStore.init(genesis) var store = TxStore.init(genesis)
var invalid1 = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}) var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}, victor)
var invalid2 = !Transaction.init({invalid1.hash: bob}, {alice: 999.u256}) var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 999.u256}, victor)
PrivateKey.alice.sign(invalid1) PrivateKey.alice.sign(bad1)
PrivateKey.bob.sign(invalid2) PrivateKey.bob.sign(bad2)
store.add(invalid1, invalid2) store.add(bad1, bad2)
check not store.hasValidTx(invalid1.hash) check not store.hasValidTx(bad1.hash)
check not store.hasValidTx(invalid2.hash) check not store.hasValidTx(bad2.hash)
test "checks that signatures match": test "checks that signatures match":
var store = TxStore.init(genesis) var store = TxStore.init(genesis)
var invalid1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}) var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor)
var invalid2 = !Transaction.init({invalid1.hash: bob}, {alice: 100.u256}) var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 100.u256}, victor)
PrivateKey.bob.sign(invalid1) # invalid signature, should be signed by alice PrivateKey.bob.sign(bad1) # invalid signature, should be signed by alice
PrivateKey.bob.sign(invalid2) PrivateKey.bob.sign(bad2)
store.add(invalid1, invalid2) store.add(bad1, bad2)
check not store.hasValidTx(invalid1.hash) check not store.hasValidTx(bad1.hash)
check not store.hasValidTx(invalid2.hash) check not store.hasValidTx(bad2.hash)