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

View File

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

View File

@ -31,4 +31,4 @@ proc genesis*(_: type Transaction): Transaction =
let alice = PublicKey.alice
let bob = PublicKey.bob
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 genesis = Transaction.genesis
let amount = rand(100).u256
var transaction = !Transaction.init(
var transaction = !Transaction.new(
{genesis.hash: alice},
{carol: amount, alice: 100.u256 - amount},
victor
@ -30,4 +30,4 @@ proc example*(_: type Transaction): Transaction =
proc example*(_: type Ack): Ack =
let tx1, tx2 = Transaction.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
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.?transactions == @[tx1.hash, tx2.hash].some
check ack.?previous == Hash.none
@ -22,7 +22,7 @@ suite "Acknowledgements":
test "an acknowledgement references a previous acknowledgement":
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.?transactions == @[tx1.hash, tx2.hash].some
check ack.?previous == previous.hash.some
@ -30,7 +30,7 @@ suite "Acknowledgements":
test "an acknowledgement can be converted to bytes":
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]
expected.add(previous.hash.toBytes)
expected.add(2) # amount of transactions
@ -53,7 +53,7 @@ suite "Acknowledgements":
check ack.signature == key.sign(ack.hash.toBytes).some
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)
check not ack.hasValidSignature
PrivateKey.victor.sign(ack)
@ -61,13 +61,13 @@ suite "Acknowledgements":
test "an acknowledgement must contain at least one transaction":
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":
let transaction = 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":
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:
store = TxStore.new(genesis)
tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.init({genesis.hash: bob}, {alice: 100.u256}, victor)
tx3 = !Transaction.init(
tx1 = !Transaction.new({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.new({genesis.hash: bob}, {alice: 100.u256}, victor)
tx3 = !Transaction.new(
{tx1.hash: bob, tx2.hash: alice},
{alice: 200.u256},
victor
)
ack1 = !Ack.init([tx1.hash], victor)
ack2 = !Ack.init([tx2.hash], victor)
ack3 = !Ack.init(ack1.hash, [tx2.hash, tx3.hash], victor)
ack1 = !Ack.new([tx1.hash], victor)
ack2 = !Ack.new([tx2.hash], victor)
ack3 = !Ack.new(ack1.hash, [tx2.hash, tx3.hash], victor)
PrivateKey.alice.sign(tx1)
PrivateKey.bob.sign(tx2)
PrivateKey.alice.sign(tx3)
@ -78,8 +78,8 @@ suite "Transaction validation":
setup:
store = TxStore.new(genesis)
tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}, victor)
tx1 = !Transaction.new({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.new({tx1.hash: bob}, {alice: 100.u256}, victor)
PrivateKey.alice.sign(tx1)
PrivateKey.bob.sign(tx2)
@ -96,8 +96,8 @@ suite "Transaction validation":
check past.missing == set(tx1.hash)
test "checks that inputs and outputs match":
var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}, victor)
var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 999.u256}, victor)
var bad1 = !Transaction.new({genesis.hash: alice}, {bob: 999.u256}, victor)
var bad2 = !Transaction.new({bad1.hash: bob}, {alice: 999.u256}, victor)
PrivateKey.alice.sign(bad1)
PrivateKey.bob.sign(bad2)
store.add(bad1, bad2)
@ -107,8 +107,8 @@ suite "Transaction validation":
check past.invalid == set(bad1.hash)
test "checks that signatures match":
var bad1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor)
var bad2 = !Transaction.init({bad1.hash: bob}, {alice: 100.u256}, victor)
var bad1 = !Transaction.new({genesis.hash: alice}, {bob: 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(bad2)
store.add(bad1, bad2)
@ -129,10 +129,10 @@ suite "Acknowledgement validation":
setup:
store = TxStore.new(genesis)
tx1 = !Transaction.init({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.init({tx1.hash: bob}, {alice: 100.u256}, victor)
ack1 = !Ack.init([tx1.hash], victor)
ack2 = !Ack.init(ack1.hash, [tx2.hash], victor)
tx1 = !Transaction.new({genesis.hash: alice}, {bob: 100.u256}, victor)
tx2 = !Transaction.new({tx1.hash: bob}, {alice: 100.u256}, victor)
ack1 = !Ack.new([tx1.hash], victor)
ack2 = !Ack.new(ack1.hash, [tx2.hash], victor)
PrivateKey.alice.sign(tx1)
PrivateKey.bob.sign(tx2)
PrivateKey.victor.sign(ack1)
@ -159,8 +159,8 @@ suite "Acknowledgement validation":
check past.missing == set(tx1.hash)
test "checks that no transaction is invalid":
var bad = !Transaction.init({genesis.hash: alice}, {bob: 999.u256}, victor)
var ack = !Ack.init([bad.hash], victor)
var bad = !Transaction.new({genesis.hash: alice}, {bob: 999.u256}, victor)
var ack = !Ack.new([bad.hash], victor)
PrivateKey.alice.sign(bad)
PrivateKey.victor.sign(ack)
store.add(bad)
@ -170,8 +170,8 @@ suite "Acknowledgement validation":
check past.invalid == set(bad.hash)
test "checks that signatures match":
var bad1 = !Ack.init([tx1.hash], victor)
var bad2 = !Ack.init(bad1.hash, [tx2.hash], victor)
var bad1 = !Ack.new([tx1.hash], victor)
var bad2 = !Ack.new(bad1.hash, [tx2.hash], victor)
PrivateKey.bob.sign(bad1) # invalid signature, should be signed by victor
PrivateKey.victor.sign(bad2)
store.add(tx1, tx2)

View File

@ -8,7 +8,7 @@ suite "Transactions":
let victor = PublicKey.victor
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
test "a transaction has a hash":
@ -18,8 +18,8 @@ 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}, victor)
let transaction = !Transaction.init(
let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
let transaction = !Transaction.new(
{genesis.hash: alice},
{alice: 2.u256, bob: 30.u256},
victor
@ -28,18 +28,18 @@ suite "Transactions":
check transaction.outputs.len == 2
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
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(bob) == 10.u256
check genesis.outputValue(victor) == 0.u256
test "a transaction can be converted to bytes":
let genesis = !Transaction.init({alice: 32.u256, bob: 10.u256}, victor)
let transaction = !Transaction.init(
let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
let transaction = !Transaction.new(
{genesis.hash: alice},
{alice: 2.u256, bob: 30.u256},
victor
@ -73,9 +73,9 @@ 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}, victor)
let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor)
check not genesis.hasValidSignature()
var transaction = !Transaction.init(
var transaction = !Transaction.new(
{genesis.hash: alice},
{alice: 2.u256, bob: 30.u256},
victor
@ -88,11 +88,11 @@ suite "Transactions":
check not transaction.hasValidSignature
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":
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":
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)
test "transaction with acknowledgement by all stake is confirmed":
var tx = !Transaction.init({genesis.hash: bob}, {bob: 100.u256}, victor)
var ack = !Ack.init([tx.hash], victor)
var tx = !Transaction.new({genesis.hash: bob}, {bob: 100.u256}, victor)
var ack = !Ack.new([tx.hash], victor)
PrivateKey.bob.sign(tx)
PrivateKey.victor.sign(ack)
let store = TxStore.new(genesis)
@ -49,11 +49,11 @@ suite "Transaction Store":
check store.isConfirmed(tx.hash)
test "transaction with acknowledgements > 2/3 stake is confirmed":
let genesis = !Transaction.init({alice: 67.u256, bob: 33.u256}, victor)
var tx1 = !Transaction.init({genesis.hash: alice}, {alice: 67.u256}, vanna)
var tx2 = !Transaction.init({tx1.hash: alice}, {alice: 67.u256}, victor)
var ack1 = !Ack.init([tx1.hash], victor)
var ack2 = !Ack.init([tx2.hash], vanna)
let genesis = !Transaction.new({alice: 67.u256, bob: 33.u256}, victor)
var tx1 = !Transaction.new({genesis.hash: alice}, {alice: 67.u256}, vanna)
var tx2 = !Transaction.new({tx1.hash: alice}, {alice: 67.u256}, victor)
var ack1 = !Ack.new([tx1.hash], victor)
var ack2 = !Ack.new([tx2.hash], vanna)
PrivateKey.alice.sign(tx1)
PrivateKey.alice.sign(tx2)
PrivateKey.victor.sign(ack1)
@ -65,11 +65,11 @@ suite "Transaction Store":
check store.isConfirmed(tx2.hash)
test "transaction with acknowledgements < 2/3 stake is not confirmed":
let genesis = !Transaction.init({alice: 66.u256, bob: 34.u256}, victor)
var tx1 = !Transaction.init({genesis.hash: alice}, {alice: 66.u256}, vanna)
var tx2 = !Transaction.init({tx1.hash: alice}, {alice: 66.u256}, victor)
var ack1 = !Ack.init([tx1.hash], victor)
var ack2 = !Ack.init([tx2.hash], vanna)
let genesis = !Transaction.new({alice: 66.u256, bob: 34.u256}, victor)
var tx1 = !Transaction.new({genesis.hash: alice}, {alice: 66.u256}, vanna)
var tx2 = !Transaction.new({tx1.hash: alice}, {alice: 66.u256}, victor)
var ack1 = !Ack.new([tx1.hash], victor)
var ack2 = !Ack.new([tx2.hash], vanna)
PrivateKey.alice.sign(tx1)
PrivateKey.alice.sign(tx2)
PrivateKey.victor.sign(ack1)