diff --git a/abc/acks.nim b/abc/acks.nim index 90beb9d..255e8e7 100644 --- a/abc/acks.nim +++ b/abc/acks.nim @@ -13,13 +13,15 @@ type hash: Hash signature: ?Signature -func toBytes*(ack: Ack): seq[byte] = +func calculateHash(ack: Ack) = + var hashing = Hashing.init(HashKind.Ack) let previous = ack.previous |? Hash.default - result.add(previous.toBytes) - result.add(ack.transactions.len.uint8) + hashing.update(previous.toBytes) + hashing.update([ack.transactions.len.uint8]) for transaction in ack.transactions: - result.add(transaction.toBytes) - result.add(ack.validator.toBytes) + hashing.update(transaction.toBytes) + hashing.update(ack.validator.toBytes) + ack.hash = hashing.finish() func new(_: type Ack, previous: ?Hash, @@ -40,7 +42,7 @@ func new(_: type Ack, transactions: @transactions, validator: validator ) - ack.hash = hash(ack.toBytes, HashKind.Ack) + ack.calculateHash() some ack func new*(_: type Ack, diff --git a/abc/hash.nim b/abc/hash.nim index 217d125..1c9d2d4 100644 --- a/abc/hash.nim +++ b/abc/hash.nim @@ -6,6 +6,9 @@ type Hash* = object kind*: HashKind hash: MDigest[256] + Hashing* = object + kind: HashKind + context: sha256 func hash*(bytes: openArray[byte], kind: HashKind): Hash = Hash(kind: kind, hash: sha256.digest(bytes)) @@ -22,3 +25,15 @@ func `$`*(hash: Hash): string = "Tx(" & $hash.hash & ")" of Ack: "Ack(" & $hash.hash & ")" + +func init*(_: type Hashing, kind: HashKind): Hashing = + result.kind = kind + result.context.init() + +func update*(hashing: var Hashing, bytes: openArray[byte]) = + hashing.context.update(bytes) + +func finish*(hashing: var Hashing): Hash = + let hash = hashing.context.finish() + hashing.context.clear() + Hash(kind: hashing.kind, hash: hash) diff --git a/abc/transactions.nim b/abc/transactions.nim index c1a5302..0c544aa 100644 --- a/abc/transactions.nim +++ b/abc/transactions.nim @@ -25,16 +25,18 @@ type owner: PublicKey value: UInt256 -func toBytes*(transaction: Transaction): seq[byte] = - result.add(transaction.inputs.len.uint8) +func calculateHash(transaction: Transaction) = + var hashing = Hashing.init(HashKind.Tx) + hashing.update([transaction.inputs.len.uint8]) for (txHash, owner) in transaction.inputs: - result.add(txHash.toBytes) - result.add(owner.toBytes) - result.add(transaction.outputs.len.uint8) + hashing.update(txHash.toBytes) + hashing.update(owner.toBytes) + hashing.update([transaction.outputs.len.uint8]) for (owner, value) in transaction.outputs: - result.add(owner.toBytes) - result.add(value.toBytes) - result.add(transaction.validator.toBytes) + hashing.update(owner.toBytes) + hashing.update(value.toBytes) + hashing.update(transaction.validator.toBytes) + transaction.hash = hashing.finish() func new*(_: type Transaction, inputs: openArray[TxInput], @@ -55,7 +57,7 @@ func new*(_: type Transaction, outputs: @outputs, validator: validator ) - transaction.hash = hash(transaction.toBytes, HashKind.Tx) + transaction.calculateHash() some transaction func new*(_: type Transaction, diff --git a/tests/abc/testAcks.nim b/tests/abc/testAcks.nim index c71ae5c..b76f54b 100644 --- a/tests/abc/testAcks.nim +++ b/tests/abc/testAcks.nim @@ -28,7 +28,7 @@ suite "Acknowledgements": check ack.?previous == previous.hash.some check ack.?validator == victor.some - test "an acknowledgement can be converted to bytes": + test "an acknowledgement hash is derived from its fields": let previous = Ack.example let ack = !Ack.new(previous.hash, [tx1.hash, tx2.hash], victor) var expected: seq[byte] @@ -37,7 +37,7 @@ suite "Acknowledgements": expected.add(tx1.hash.toBytes) expected.add(tx2.hash.toBytes) expected.add(victor.toBytes) - check ack.toBytes == expected + check ack.hash == hash(expected, HashKind.Ack) test "a signature can be added to an acknowledgment": let key = PrivateKey.example diff --git a/tests/abc/testTransactions.nim b/tests/abc/testTransactions.nim index e6f8df6..77a5c7c 100644 --- a/tests/abc/testTransactions.nim +++ b/tests/abc/testTransactions.nim @@ -37,7 +37,7 @@ suite "Transactions": check genesis.outputValue(bob) == 10.u256 check genesis.outputValue(victor) == 0.u256 - test "a transaction can be converted to bytes": + test "a transaction hash is derived from its fields": let genesis = !Transaction.new({alice: 32.u256, bob: 10.u256}, victor) let transaction = !Transaction.new( {genesis.hash: alice}, @@ -54,7 +54,7 @@ suite "Transactions": expected.add(bob.toBytes) expected.add(30.u256.toBytes) expected.add(victor.toBytes) - check transaction.toBytes == expected + check transaction.hash == hash(expected, HashKind.Tx) test "signatures can be added to a transaction": let key1, key2 = PrivateKey.example