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
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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)