deserialize transaction

This commit is contained in:
Mark Spanbroek 2024-12-17 12:24:16 +01:00
parent 4e86fcb581
commit 54d5c89122
5 changed files with 173 additions and 4 deletions

View File

@ -1,8 +1,10 @@
import ./transaction/transaction
import ./transaction/serialization
import ./transaction/deserialization
import ./transaction/hashing
export transaction except hash
export serialization.toBytes
export deserialization.fromBytes
export hashing.hash
export hashing.toBytes

View File

@ -0,0 +1,90 @@
import ../basics
import ../signatures
import ./transaction
import ./groth16
import ./serialization
func fromBytes(
_: type array[32, byte],
bytes: openArray[byte]
): ?!array[32, byte] =
if bytes.len != 32:
failure "expected 32 bytes but got: " & $bytes.len
else:
var bytes32: array[32, byte]
bytes32[0..<32] = bytes[0..<32]
success bytes32
func fromBytes(
_: type StorageRequestId,
bytes: openArray[byte]
): ?!StorageRequestId =
success StorageRequestId(? array[32, byte].fromBytes(bytes))
func init(_: type Groth16Proof, message: Groth16ProofMessage): Groth16Proof =
Groth16Proof.init(
G1Point.init(
UInt256.fromBytesBE(message.a.x),
UInt256.fromBytesBE(message.a.y)
),
G2Point.init(
Fp2Element.init(
UInt256.fromBytesBE(message.b.x.real),
UInt256.fromBytesBE(message.b.x.imag)
),
Fp2Element.init(
UInt256.fromBytesBE(message.b.y.real),
UInt256.fromBytesBE(message.b.y.imag)
)
),
G1Point.init(
UInt256.fromBytesBE(message.c.x),
UInt256.fromBytesBE(message.c.y)
)
)
func init(_: type Transaction, message: TransactionMessage): ?!Transaction =
if message.version != TransactionVersion.version0.uint32:
return failure "unsupported transaction version: " & $message.version
let requestId = ? StorageRequestId.fromBytes(message.requestId)
let slotIndex = message.slotIndex
let period = Period(message.period)
let merkleRoot = ? array[32, byte].fromBytes(message.merkleRoot)
let challenge = ? array[32, byte].fromBytes(message.challenge)
case message.kind
of TransactionKind.storageProof.uint32:
success Transaction.storageProof(
requestId,
slotIndex,
period,
merkleRoot,
challenge,
Groth16Proof.init(message.proof)
)
of TransactionKind.missingProof.uint32:
success Transaction.missingProof(
requestId,
message.slotIndex,
period,
merkleRoot,
challenge,
)
else:
failure "invalid transaction kind: " & $message.kind
func init(
_: type Signed[Transaction],
message: SignedTransactionMessage
): ?!Signed[Transaction] =
success Signed[Transaction].init(
? Transaction.init(message.transaction),
? Identifier.fromBytes(message.signer),
? Signature.fromBytes(message.signature)
)
func fromBytes*(
_: type Signed[Transaction],
bytes: openArray[byte]
): ?!Signed[Transaction] =
let message = ? Protobuf.decode(bytes, SignedTransactionMessage).catch()
Signed[Transaction].init(message)

View File

@ -80,6 +80,9 @@ proc example*(_: type Identifier): Identifier =
proc example*(_: type Signature): Signature =
Identity.example.sign(seq[byte].example)
proc example*[T](_: type Signed[T]): Signed[T] =
Signed.sign(Identity.example, T.example)
proc example*(_: type CommitteeMember): CommitteeMember =
CommitteeMember(uint32.example.int)

View File

@ -41,11 +41,84 @@ suite "Transaction serialization":
check protobuf.proof.c.y == transaction.proof.c.y.toBytesBE()
test "serializes a signed transaction with protobuf":
let transaction = Transaction.example
let identity = Identity.example
let signed = Signed.sign(identity, transaction)
let signed = Signed[Transaction].example
let serialized = signed.toBytes()
let protobuf = Protobuf.decode(serialized, SignedTransactionMessage)
check protobuf.transaction == TransactionMessage.init(transaction)
check protobuf.transaction == TransactionMessage.init(signed.value)
check protobuf.signer == signed.signer.toBytes()
check protobuf.signature == signed.signature.toBytes()
test "deserializes a signed transaction":
let signed = Signed[Transaction].example
let serialized = signed.toBytes()
let deserialized = Signed[Transaction].fromBytes(serialized)
check deserialized == success signed
test "deserialization fails when protobuf encoding is invalid":
let invalid = seq[byte].example
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "Invalid wire type"
test "deserialization fails when signer is invalid":
let signed = Signed[Transaction].example
var message = SignedTransactionMessage.init(signed)
message.signer &= 42'u8
let invalid = Protobuf.encode(message)
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "invalid identifier"
test "deserialization fails when signature is invalid":
let signed = Signed[Transaction].example
var message = SignedTransactionMessage.init(signed)
message.signature &= 42'u8
let invalid = Protobuf.encode(message)
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "invalid signature"
test "deserialization fails when transaction version is unsupported":
let signed = Signed[Transaction].example
var message = SignedTransactionMessage.init(signed)
message.transaction.version = 42
let invalid = Protobuf.encode(message)
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "unsupported transaction version: 42"
test "deserialization fails when transaction kind is invalid":
let signed = Signed[Transaction].example
var message = SignedTransactionMessage.init(signed)
message.transaction.kind = 42
let invalid = Protobuf.encode(message)
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "invalid transaction kind: 42"
test "deserialization fails when storage request id is invalid":
let signed = Signed[Transaction].example
var message = SignedTransactionMessage.init(signed)
message.transaction.requestid &= 42'u8
let invalid = Protobuf.encode(message)
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "expected 32 bytes but got: 33"
test "deserialization fails when merkle root is invalid":
let signed = Signed[Transaction].example
var message = SignedTransactionMessage.init(signed)
message.transaction.merkleRoot &= 42'u8
let invalid = Protobuf.encode(message)
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "expected 32 bytes but got: 33"
test "deserialization fails when challenge is invalid":
let signed = Signed[Transaction].example
var message = SignedTransactionMessage.init(signed)
message.transaction.challenge &= 42'u8
let invalid = Protobuf.encode(message)
let deserialized = Signed[Transaction].fromBytes(invalid)
check deserialized.isFailure
check deserialized.errorOption.?msg == some "expected 32 bytes but got: 33"

View File

@ -1 +1,2 @@
--path:".."
--warning[DotLikeOps]:off