From a82e62522fb46dff4e7acd59385d50bb6ef76796 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Wed, 18 Dec 2024 10:28:10 +0100 Subject: [PATCH] deserialize block --- codexvalidator/blocks.nim | 2 + codexvalidator/blocks/deserialization.nim | 46 ++++++++++++++++ codexvalidator/hashing.nim | 12 ++++- .../transaction/deserialization.nim | 2 +- .../blocks/testSerialization.nim | 52 +++++++++++++++++++ tests/codexvalidator/testHashing.nim | 24 +++++++++ tests/tests.nim | 1 + 7 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 codexvalidator/blocks/deserialization.nim create mode 100644 tests/codexvalidator/testHashing.nim diff --git a/codexvalidator/blocks.nim b/codexvalidator/blocks.nim index 32b7c05..5f8ac3f 100644 --- a/codexvalidator/blocks.nim +++ b/codexvalidator/blocks.nim @@ -5,8 +5,10 @@ export blockid export blck except hash import ./blocks/serialization +import ./blocks/deserialization export serialization.toBytes +export deserialization.fromBytes import ./blocks/hashing diff --git a/codexvalidator/blocks/deserialization.nim b/codexvalidator/blocks/deserialization.nim new file mode 100644 index 0000000..75073b4 --- /dev/null +++ b/codexvalidator/blocks/deserialization.nim @@ -0,0 +1,46 @@ +import ../basics +import ../signatures +import ../hashing +import ../transaction +import ../transaction/deserialization +import ./blck +import ./blockid +import ./serialization + +func init*( + _: type BlockId, + message: BlockIdMessage +): ?!BlockId = + success BlockId.init( + CommitteeMember(message.author), + message.round, + ? Hash.fromBytes(message.hash) + ) + +func init*( + _: type Block, + message: BlockMessage +): ?!Block = + success Block( + author: CommitteeMember(message.author), + round: message.round, + parents: message.parents.mapIt(? BlockId.init(it)), + transactions: message.transactions.mapIt(? Transaction.init(it)) + ) + +func init*( + _: type Signed[Block], + message: SignedBlockMessage +): ?!Signed[Block] = + success Signed[Block].init( + ? Block.init(message.blck), + ? Identifier.fromBytes(message.signer), + ? Signature.fromBytes(message.signature) + ) + +func fromBytes*( + _: type Signed[Block], + bytes: openArray[byte] +): ?!Signed[Block] = + let message = ? Protobuf.decode(bytes, SignedBlockMessage).catch() + Signed[Block].init(message) diff --git a/codexvalidator/hashing.nim b/codexvalidator/hashing.nim index 007080a..ba50a96 100644 --- a/codexvalidator/hashing.nim +++ b/codexvalidator/hashing.nim @@ -1,5 +1,6 @@ import pkg/nimcrypto/sha2 import pkg/nimcrypto/hash +import ./basics export hash.`$` @@ -12,5 +13,12 @@ func hash*(_: type Hash, bytes: openArray[byte]): Hash = result = context.finish() context.clear() -func toBytes*(hash: Hash): auto = - hash.data +func toBytes*(hash: Hash): seq[byte] = + @(hash.data) + +func fromBytes*(_: type Hash, bytes: openArray[byte]): ?!Hash = + if bytes.len != 32: + return failure "expected hash of 32 bytes, but got: " & $bytes.len + var data: array[32, byte] + data[0..<32] = bytes[0..<32] + success Hash(data: data) diff --git a/codexvalidator/transaction/deserialization.nim b/codexvalidator/transaction/deserialization.nim index 469c0e2..7c35696 100644 --- a/codexvalidator/transaction/deserialization.nim +++ b/codexvalidator/transaction/deserialization.nim @@ -55,7 +55,7 @@ func init(_: type Groth16Proof, message: Groth16ProofMessage): Groth16Proof = ) ) -func init(_: type Transaction, message: TransactionMessage): ?!Transaction = +func init*(_: type Transaction, message: TransactionMessage): ?!Transaction = if message.version != TransactionVersion.version0.uint32: return failure "unsupported transaction version: " & $message.version let proofInput = ? StorageProofInput.init(message.proofInput) diff --git a/tests/codexvalidator/blocks/testSerialization.nim b/tests/codexvalidator/blocks/testSerialization.nim index cbc1d07..698df0a 100644 --- a/tests/codexvalidator/blocks/testSerialization.nim +++ b/tests/codexvalidator/blocks/testSerialization.nim @@ -1,6 +1,7 @@ import ../basics import codexvalidator/blocks import codexvalidator/blocks/serialization +import codexvalidator/transaction import codexvalidator/transaction/serialization import codexvalidator/hashing import codexvalidator/signatures @@ -35,3 +36,54 @@ suite "Block serialization": check protobuf.blck == BlockMessage.init(blck) check protobuf.signer == signed.signer.toBytes() check protobuf.signature == signed.signature.toBytes() + + test "deserializes a signed block": + let signed = Signed[Block].example + let serialized = signed.toBytes() + let deserialized = Signed[Block].fromBytes(serialized) + check deserialized == success signed + + test "deserialization fails when protobuf encoding is invalid": + let invalid = seq[byte].example + let deserialized = Signed[Block].fromBytes(invalid) + check deserialized.isFailure + check deserialized.errorOption.?msg == some "Invalid wire type" + + test "deserialization fails when signer is invalid": + let signed = Signed[Block].example + var message = SignedBlockMessage.init(signed) + message.signer &= 42'u8 + let invalid = Protobuf.encode(message) + let deserialized = Signed[Block].fromBytes(invalid) + check deserialized.isFailure + check deserialized.errorOption.?msg == some "invalid identifier" + + test "deserialization fails when signature is invalid": + let signed = Signed[Block].example + var message = SignedBlockMessage.init(signed) + message.signature &= 42'u8 + let invalid = Protobuf.encode(message) + let deserialized = Signed[Block].fromBytes(invalid) + check deserialized.isFailure + check deserialized.errorOption.?msg == some "invalid signature" + + test "deserialization fails when parent block id is invalid": + let signed = Signed[Block].example + var message = SignedBlockMessage.init(signed) + let hash = array[32, byte].example + message.blck.parents &= BlockIdMessage(hash: @hash & 42'u8) + let invalid = Protobuf.encode(message) + let deserialized = Signed[Block].fromBytes(invalid) + check deserialized.isFailure + check deserialized.errorOption.?msg == some "expected hash of 32 bytes, but got: 33" + + test "deserialization fails when transaction is invalid": + let signed = Signed[Block].example + var message = SignedBlockMessage.init(signed) + var transaction = TransactionMessage.init(Transaction.example) + transaction.version = 42'u8 + message.blck.transactions &= transaction + let invalid = Protobuf.encode(message) + let deserialized = Signed[Block].fromBytes(invalid) + check deserialized.isFailure + check deserialized.errorOption.?msg == some "unsupported transaction version: 42" diff --git a/tests/codexvalidator/testHashing.nim b/tests/codexvalidator/testHashing.nim new file mode 100644 index 0000000..1db7afc --- /dev/null +++ b/tests/codexvalidator/testHashing.nim @@ -0,0 +1,24 @@ +import ./basics +import codexvalidator/hashing + +suite "Hashing": + + test "serializes a hash to bytes": + let hash = Hash.example + let serialized = hash.toBytes() + check serialized == hash.data + + test "deserializes a hash from bytes": + let hash = Hash.example + let serialized = hash.toBytes() + let deserialized = Hash.fromBytes(serialized) + check deserialized == success hash + + test "deserialization fails when number of bytes is not 32": + let hash = Hash.example + let serialized = hash.toBytes() + let invalid = serialized & 42'u8 + let deserialized = Hash.fromBytes(invalid) + check deserialized.isFailure + check deserialized.errorOption.?msg == some "expected hash of 32 bytes, but got: 33" + diff --git a/tests/tests.nim b/tests/tests.nim index fc46414..7225b5f 100644 --- a/tests/tests.nim +++ b/tests/tests.nim @@ -1,4 +1,5 @@ import ./codexvalidator/testSignatures +import ./codexvalidator/testHashing import ./codexvalidator/transaction/testTransaction import ./codexvalidator/transaction/testSerialization import ./codexvalidator/transaction/testHashing