Convert Transaction and Ack to ref types

Reason: performance, copying transactions and acks around leads
to significant slowdowns
This commit is contained in:
Mark Spanbroek 2021-08-05 10:16:38 +02:00
parent db1e8b805b
commit 67176b3687
8 changed files with 77 additions and 77 deletions

View File

@ -6,7 +6,7 @@ export hash
export keys export keys
type type
Ack* = object Ack* = ref object
previous: ?Hash previous: ?Hash
transactions: seq[Hash] transactions: seq[Hash]
validator: PublicKey validator: PublicKey
@ -21,7 +21,7 @@ func toBytes*(ack: Ack): seq[byte] =
result.add(transaction.toBytes) result.add(transaction.toBytes)
result.add(ack.validator.toBytes) result.add(ack.validator.toBytes)
func init(_: type Ack, func new(_: type Ack,
previous: ?Hash, previous: ?Hash,
transactions: openArray[Hash], transactions: openArray[Hash],
validator: PublicKey): ?Ack = validator: PublicKey): ?Ack =
@ -43,16 +43,16 @@ func init(_: type Ack,
ack.hash = hash(ack.toBytes, HashKind.Ack) ack.hash = hash(ack.toBytes, HashKind.Ack)
some ack some ack
func init*(_: type Ack, func new*(_: type Ack,
transactions: openArray[Hash], transactions: openArray[Hash],
validator: PublicKey): ?Ack = validator: PublicKey): ?Ack =
Ack.init(Hash.none, transactions, validator) Ack.new(Hash.none, transactions, validator)
func init*(_: type Ack, func new*(_: type Ack,
previous: Hash, previous: Hash,
transactions: openArray[Hash], transactions: openArray[Hash],
validator: PublicKey): ?Ack = validator: PublicKey): ?Ack =
Ack.init(previous.some, transactions, validator) Ack.new(previous.some, transactions, validator)
func previous*(ack: Ack): ?Hash = func previous*(ack: Ack): ?Hash =
ack.previous ack.previous

View File

@ -12,7 +12,7 @@ export keys
export hash export hash
type type
Transaction* = object Transaction* = ref object
inputs: seq[TxInput] inputs: seq[TxInput]
outputs: seq[TxOutput] outputs: seq[TxOutput]
validator: PublicKey validator: PublicKey
@ -36,7 +36,7 @@ func toBytes*(transaction: Transaction): seq[byte] =
result.add(value.toBytes) result.add(value.toBytes)
result.add(transaction.validator.toBytes) result.add(transaction.validator.toBytes)
func init*(_: type Transaction, func new*(_: type Transaction,
inputs: openArray[TxInput], inputs: openArray[TxInput],
outputs: openArray[TxOutput], outputs: openArray[TxOutput],
validator: PublicKey): ?Transaction = validator: PublicKey): ?Transaction =
@ -58,10 +58,10 @@ func init*(_: type Transaction,
transaction.hash = hash(transaction.toBytes, HashKind.Tx) transaction.hash = hash(transaction.toBytes, HashKind.Tx)
some transaction some transaction
func init*(_: type Transaction, func new*(_: type Transaction,
outputs: openArray[TxOutput], outputs: openArray[TxOutput],
validator: PublicKey): ?Transaction = validator: PublicKey): ?Transaction =
Transaction.init([], outputs, validator) Transaction.new([], outputs, validator)
func inputs*(transaction: Transaction): seq[TxInput] = func inputs*(transaction: Transaction): seq[TxInput] =
transaction.inputs transaction.inputs

View File

@ -31,4 +31,4 @@ proc genesis*(_: type Transaction): Transaction =
let alice = PublicKey.alice let alice = PublicKey.alice
let bob = PublicKey.bob let bob = PublicKey.bob
let victor = PublicKey.victor let victor = PublicKey.victor
!Transaction.init({alice: 100.u256, bob: 100.u256}, victor) !Transaction.new({alice: 100.u256, bob: 100.u256}, victor)

View File

@ -20,7 +20,7 @@ proc example*(_: type Transaction): Transaction =
let victor = PublicKey.victor 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.new(
{genesis.hash: alice}, {genesis.hash: alice},
{carol: amount, alice: 100.u256 - amount}, {carol: amount, alice: 100.u256 - amount},
victor victor
@ -30,4 +30,4 @@ proc example*(_: type Transaction): Transaction =
proc example*(_: type Ack): Ack = proc example*(_: type Ack): Ack =
let tx1, tx2 = Transaction.example let tx1, tx2 = Transaction.example
let validator = PublicKey.example let validator = PublicKey.example
!Ack.init([tx1.hash, tx2.hash], validator) !Ack.new([tx1.hash, tx2.hash], validator)

View File

@ -8,7 +8,7 @@ suite "Acknowledgements":
let victor = PublicKey.victor let victor = PublicKey.victor
test "a first acknowledgement can be made": test "a first acknowledgement can be made":
let ack = Ack.init([tx1.hash, tx2.hash], victor) let ack = Ack.new([tx1.hash, tx2.hash], victor)
check ack.isSome check ack.isSome
check ack.?transactions == @[tx1.hash, tx2.hash].some check ack.?transactions == @[tx1.hash, tx2.hash].some
check ack.?previous == Hash.none check ack.?previous == Hash.none
@ -22,7 +22,7 @@ suite "Acknowledgements":
test "an acknowledgement references a previous acknowledgement": test "an acknowledgement references a previous acknowledgement":
let previous = Ack.example let previous = Ack.example
let ack = Ack.init(previous.hash, [tx1.hash, tx2.hash], victor) let ack = Ack.new(previous.hash, [tx1.hash, tx2.hash], victor)
check ack.isSome check ack.isSome
check ack.?transactions == @[tx1.hash, tx2.hash].some check ack.?transactions == @[tx1.hash, tx2.hash].some
check ack.?previous == previous.hash.some check ack.?previous == previous.hash.some
@ -30,7 +30,7 @@ suite "Acknowledgements":
test "an acknowledgement can be converted to bytes": test "an acknowledgement can be converted to bytes":
let previous = Ack.example let previous = Ack.example
let ack = !Ack.init(previous.hash, [tx1.hash, tx2.hash], victor) let ack = !Ack.new(previous.hash, [tx1.hash, tx2.hash], victor)
var expected: seq[byte] var expected: seq[byte]
expected.add(previous.hash.toBytes) expected.add(previous.hash.toBytes)
expected.add(2) # amount of transactions expected.add(2) # amount of transactions
@ -53,7 +53,7 @@ suite "Acknowledgements":
check ack.signature == key.sign(ack.hash.toBytes).some check ack.signature == key.sign(ack.hash.toBytes).some
test "acknowledgement signature can be checked for validity": test "acknowledgement signature can be checked for validity":
var ack = !Ack.init([tx1.hash, tx2.hash], victor) var ack = !Ack.new([tx1.hash, tx2.hash], victor)
PrivateKey.bob.sign(ack) PrivateKey.bob.sign(ack)
check not ack.hasValidSignature check not ack.hasValidSignature
PrivateKey.victor.sign(ack) PrivateKey.victor.sign(ack)
@ -61,13 +61,13 @@ suite "Acknowledgements":
test "an acknowledgement must contain at least one transaction": test "an acknowledgement must contain at least one transaction":
let previous = Ack.example let previous = Ack.example
check Ack.init(previous.hash, [], victor).isNone check Ack.new(previous.hash, [], victor).isNone
test "previous acknowledgement must have correct hash type": test "previous acknowledgement must have correct hash type":
let transaction = Transaction.example let transaction = Transaction.example
let invalidPrevious = Transaction.example let invalidPrevious = Transaction.example
check Ack.init(invalidPrevious.hash, [transaction.hash], victor).isNone check Ack.new(invalidPrevious.hash, [transaction.hash], victor).isNone
test "acknowledged transactions must have correct hash type": test "acknowledged transactions must have correct hash type":
let invalidTransaction = Ack.example let invalidTransaction = Ack.example
check Ack.init([invalidTransaction.hash], victor).isNone check Ack.new([invalidTransaction.hash], victor).isNone

View File

@ -18,16 +18,16 @@ suite "Past transactions and acknowledgements":
setup: setup:
store = TxStore.new(genesis) store = TxStore.new(genesis)
tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor) tx1 = !Transaction.new({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.init({genesis.hash: bob}, {alice: 100.u256}, victor) tx2 = !Transaction.new({genesis.hash: bob}, {alice: 100.u256}, victor)
tx3 = !Transaction.init( tx3 = !Transaction.new(
{tx1.hash: bob, tx2.hash: alice}, {tx1.hash: bob, tx2.hash: alice},
{alice: 200.u256}, {alice: 200.u256},
victor victor
) )
ack1 = !Ack.init([tx1.hash], victor) ack1 = !Ack.new([tx1.hash], victor)
ack2 = !Ack.init([tx2.hash], victor) ack2 = !Ack.new([tx2.hash], victor)
ack3 = !Ack.init(ack1.hash, [tx2.hash, tx3.hash], victor) ack3 = !Ack.new(ack1.hash, [tx2.hash, tx3.hash], victor)
PrivateKey.alice.sign(tx1) PrivateKey.alice.sign(tx1)
PrivateKey.bob.sign(tx2) PrivateKey.bob.sign(tx2)
PrivateKey.alice.sign(tx3) PrivateKey.alice.sign(tx3)
@ -78,8 +78,8 @@ suite "Transaction validation":
setup: setup:
store = TxStore.new(genesis) store = TxStore.new(genesis)
tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor) tx1 = !Transaction.new({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}, victor) tx2 = !Transaction.new({tx1.hash: bob}, {alice: 100.u256}, victor)
PrivateKey.alice.sign(tx1) PrivateKey.alice.sign(tx1)
PrivateKey.bob.sign(tx2) PrivateKey.bob.sign(tx2)
@ -96,8 +96,8 @@ suite "Transaction validation":
check past.missing == set(tx1.hash) check past.missing == set(tx1.hash)
test "checks that inputs and outputs match": test "checks that inputs and outputs match":
var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}, victor) var bad1 = !Transaction.new({genesis.hash: alice}, {bob: 999.u256}, victor)
var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 999.u256}, victor) var bad2 = !Transaction.new({bad1.hash: bob}, {alice: 999.u256}, victor)
PrivateKey.alice.sign(bad1) PrivateKey.alice.sign(bad1)
PrivateKey.bob.sign(bad2) PrivateKey.bob.sign(bad2)
store.add(bad1, bad2) store.add(bad1, bad2)
@ -107,8 +107,8 @@ suite "Transaction validation":
check past.invalid == set(bad1.hash) check past.invalid == set(bad1.hash)
test "checks that signatures match": test "checks that signatures match":
var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor) var bad1 = !Transaction.new({genesis.hash: alice}, {bob: 100.u256}, victor)
var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 100.u256}, victor) var bad2 = !Transaction.new({bad1.hash: bob}, {alice: 100.u256}, victor)
PrivateKey.bob.sign(bad1) # invalid signature, should be signed by alice PrivateKey.bob.sign(bad1) # invalid signature, should be signed by alice
PrivateKey.bob.sign(bad2) PrivateKey.bob.sign(bad2)
store.add(bad1, bad2) store.add(bad1, bad2)
@ -129,10 +129,10 @@ suite "Acknowledgement validation":
setup: setup:
store = TxStore.new(genesis) store = TxStore.new(genesis)
tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor) tx1 = !Transaction.new({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}, victor) tx2 = !Transaction.new({tx1.hash: bob}, {alice: 100.u256}, victor)
ack1 = !Ack.init([tx1.hash], victor) ack1 = !Ack.new([tx1.hash], victor)
ack2 = !Ack.init(ack1.hash, [tx2.hash], victor) ack2 = !Ack.new(ack1.hash, [tx2.hash], victor)
PrivateKey.alice.sign(tx1) PrivateKey.alice.sign(tx1)
PrivateKey.bob.sign(tx2) PrivateKey.bob.sign(tx2)
PrivateKey.victor.sign(ack1) PrivateKey.victor.sign(ack1)
@ -159,8 +159,8 @@ suite "Acknowledgement validation":
check past.missing == set(tx1.hash) check past.missing == set(tx1.hash)
test "checks that no transaction is invalid": test "checks that no transaction is invalid":
var bad = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}, victor) var bad = !Transaction.new({genesis.hash: alice}, {bob: 999.u256}, victor)
var ack = !Ack.init([bad.hash], victor) var ack = !Ack.new([bad.hash], victor)
PrivateKey.alice.sign(bad) PrivateKey.alice.sign(bad)
PrivateKey.victor.sign(ack) PrivateKey.victor.sign(ack)
store.add(bad) store.add(bad)
@ -170,8 +170,8 @@ suite "Acknowledgement validation":
check past.invalid == set(bad.hash) check past.invalid == set(bad.hash)
test "checks that signatures match": test "checks that signatures match":
var bad1 = !Ack.init([tx1.hash], victor) var bad1 = !Ack.new([tx1.hash], victor)
var bad2 = !Ack.init(bad1.hash, [tx2.hash], victor) var bad2 = !Ack.new(bad1.hash, [tx2.hash], victor)
PrivateKey.bob.sign(bad1) # invalid signature, should be signed by victor PrivateKey.bob.sign(bad1) # invalid signature, should be signed by victor
PrivateKey.victor.sign(bad2) PrivateKey.victor.sign(bad2)
store.add(tx1, tx2) store.add(tx1, tx2)

View File

@ -8,7 +8,7 @@ suite "Transactions":
let victor = PublicKey.victor 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}, victor) let genesis = Transaction.new({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":
@ -18,8 +18,8 @@ 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}, victor) let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
let transaction = !Transaction.init( let transaction = !Transaction.new(
{genesis.hash: alice}, {genesis.hash: alice},
{alice: 2.u256, bob: 30.u256}, {alice: 2.u256, bob: 30.u256},
victor victor
@ -28,18 +28,18 @@ suite "Transactions":
check transaction.outputs.len == 2 check transaction.outputs.len == 2
test "transaction value is the sum of its output values": test "transaction value is the sum of its output values":
let genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}, victor) let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
check genesis.value == 42.u256 check genesis.value == 42.u256
test "output value is the value of the output for given owner": test "output value is the value of the output for given owner":
let genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}, victor) let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
check genesis.outputValue(alice) == 32.u256 check genesis.outputValue(alice) == 32.u256
check genesis.outputValue(bob) == 10.u256 check genesis.outputValue(bob) == 10.u256
check genesis.outputValue(victor) == 0.u256 check genesis.outputValue(victor) == 0.u256
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}, victor) let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
let transaction = !Transaction.init( let transaction = !Transaction.new(
{genesis.hash: alice}, {genesis.hash: alice},
{alice: 2.u256, bob: 30.u256}, {alice: 2.u256, bob: 30.u256},
victor victor
@ -73,9 +73,9 @@ 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}, victor) let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
check not genesis.hasValidSignature() check not genesis.hasValidSignature()
var transaction = !Transaction.init( var transaction = !Transaction.new(
{genesis.hash: alice}, {genesis.hash: alice},
{alice: 2.u256, bob: 30.u256}, {alice: 2.u256, bob: 30.u256},
victor victor
@ -88,11 +88,11 @@ 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([], victor).isNone check Transaction.new([], 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}, victor).isNone check Transaction.new({alice: 40.u256, alice: 2.u256}, victor).isNone
test "inputs must have correct hash kind": test "inputs must have correct hash kind":
let invalid = Ack.example let invalid = Ack.example
check Transaction.init({invalid.hash: alice}, {bob: 1.u256}, victor).isNone check Transaction.new({invalid.hash: alice}, {bob: 1.u256}, victor).isNone

View File

@ -39,8 +39,8 @@ suite "Transaction Store":
check not store.isConfirmed(transaction.hash) check not store.isConfirmed(transaction.hash)
test "transaction with acknowledgement by all stake is confirmed": test "transaction with acknowledgement by all stake is confirmed":
var tx = !Transaction.init({genesis.hash: bob}, {bob: 100.u256}, victor) var tx = !Transaction.new({genesis.hash: bob}, {bob: 100.u256}, victor)
var ack = !Ack.init([tx.hash], victor) var ack = !Ack.new([tx.hash], victor)
PrivateKey.bob.sign(tx) PrivateKey.bob.sign(tx)
PrivateKey.victor.sign(ack) PrivateKey.victor.sign(ack)
let store = TxStore.new(genesis) let store = TxStore.new(genesis)
@ -49,11 +49,11 @@ suite "Transaction Store":
check store.isConfirmed(tx.hash) check store.isConfirmed(tx.hash)
test "transaction with acknowledgements > 2/3 stake is confirmed": test "transaction with acknowledgements > 2/3 stake is confirmed":
let genesis = !Transaction.init({alice: 67.u256, bob: 33.u256}, victor) let genesis = !Transaction.new({alice: 67.u256, bob: 33.u256}, victor)
var tx1 = !Transaction.init({genesis.hash: alice}, {alice: 67.u256}, vanna) var tx1 = !Transaction.new({genesis.hash: alice}, {alice: 67.u256}, vanna)
var tx2 = !Transaction.init({tx1.hash: alice}, {alice: 67.u256}, victor) var tx2 = !Transaction.new({tx1.hash: alice}, {alice: 67.u256}, victor)
var ack1 = !Ack.init([tx1.hash], victor) var ack1 = !Ack.new([tx1.hash], victor)
var ack2 = !Ack.init([tx2.hash], vanna) var ack2 = !Ack.new([tx2.hash], vanna)
PrivateKey.alice.sign(tx1) PrivateKey.alice.sign(tx1)
PrivateKey.alice.sign(tx2) PrivateKey.alice.sign(tx2)
PrivateKey.victor.sign(ack1) PrivateKey.victor.sign(ack1)
@ -65,11 +65,11 @@ suite "Transaction Store":
check store.isConfirmed(tx2.hash) check store.isConfirmed(tx2.hash)
test "transaction with acknowledgements < 2/3 stake is not confirmed": test "transaction with acknowledgements < 2/3 stake is not confirmed":
let genesis = !Transaction.init({alice: 66.u256, bob: 34.u256}, victor) let genesis = !Transaction.new({alice: 66.u256, bob: 34.u256}, victor)
var tx1 = !Transaction.init({genesis.hash: alice}, {alice: 66.u256}, vanna) var tx1 = !Transaction.new({genesis.hash: alice}, {alice: 66.u256}, vanna)
var tx2 = !Transaction.init({tx1.hash: alice}, {alice: 66.u256}, victor) var tx2 = !Transaction.new({tx1.hash: alice}, {alice: 66.u256}, victor)
var ack1 = !Ack.init([tx1.hash], victor) var ack1 = !Ack.new([tx1.hash], victor)
var ack2 = !Ack.init([tx2.hash], vanna) var ack2 = !Ack.new([tx2.hash], vanna)
PrivateKey.alice.sign(tx1) PrivateKey.alice.sign(tx1)
PrivateKey.alice.sign(tx2) PrivateKey.alice.sign(tx2)
PrivateKey.victor.sign(ack1) PrivateKey.victor.sign(ack1)