From 469a8d4154aeb21c92df8ef371e8bc352bdd91c9 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Tue, 6 Apr 2021 12:25:47 +0200 Subject: [PATCH] JSON serialization of signed states --- nitro.nim | 2 + nitro/json.nim | 96 ++++++++++++++++++++++++++++++++++++++++ tests/nitro/examples.nim | 6 +++ tests/nitro/testJson.nim | 19 ++++++++ tests/testAll.nim | 1 + 5 files changed, 124 insertions(+) create mode 100644 nitro/json.nim create mode 100644 tests/nitro/testJson.nim diff --git a/nitro.nim b/nitro.nim index 7c1afe6..1dd1e4d 100644 --- a/nitro.nim +++ b/nitro.nim @@ -1,5 +1,7 @@ import ./nitro/protocol import ./nitro/wallet +import ./nitro/json export protocol export wallet +export json diff --git a/nitro/json.nim b/nitro/json.nim new file mode 100644 index 0000000..739313a --- /dev/null +++ b/nitro/json.nim @@ -0,0 +1,96 @@ +import std/json +import std/typetraits +import pkg/stew/byteutils +import ./basics +import ./protocol +import ./wallet/signedstate + +export signedstate + +push: {.upraises:[].} + +func `%`(value: Outcome | Allocation): JsonNode = + type Base = distinctBase(typeof value) + %(Base(value)) + +func `%`(value: seq[byte]): JsonNode = + %value.toHex + +func `%`(value: EthAddress | Destination): JsonNode = + %($value) + +func `%`(value: UInt256): JsonNode = + %(value.toHex) + +func `%`(value: Signature): JsonNode = + %($value) + +func `%`(value: AllocationItem): JsonNode = + %*{ + "destination": value.destination, + "amount": value.amount + } + +func toJson*(payment: SignedState): string = + $(%*payment) + +{.pop.} + +push: {.upraises: [ValueError].} + +func expectKind(node: JsonNode, kind: JsonNodeKind) = + if node.kind != kind: + let message = "expected " & $kind & ", got: " & $node.kind + raise newException(JsonKindError, message) + +func initFromJson*(bytes: var seq[byte], node: JsonNode, _: var string) = + node.expectKind(JString) + let parsed = seq[byte].fromHex(node.getStr) + if parsed.isOk: + bytes = parsed.get + else: + raise newException(ValueError, "invalid hex string") + +func initFromJson*(address: var EthAddress, node: JsonNode, _: var string) = + node.expectKind(JString) + let parsed = EthAddress.parse(node.getStr) + if parsed.isSome: + address = parsed.get + else: + raise newException(ValueError, "invalid ethereum address") + +func initFromJson*(dest: var Destination, node: JsonNode, _: var string) = + node.expectKind(JString) + let parsed = Destination.parse(node.getStr) + if parsed.isSome: + dest = parsed.get + else: + raise newException(ValueError, "invalid nitro destination") + +func initFromJson*(number: var UInt256, node: JsonNode, _: var string) = + node.expectKind(JString) + number = UInt256.fromHex(node.getStr) + +func initFromJson*(signature: var Signature, node: JsonNode, _: var string) = + node.expectKind(JString) + let parsed = Signature.parse(node.getStr) + if parsed.isSome: + signature = parsed.get + else: + raise newException(ValueError, "invalid signature") + +{.pop.} + +push: {.upraises: [].} + +proc fromJson*(_: type SignedState, json: string): ?SignedState = + try: + {.warning[UnsafeSetLen]: off.} + return parseJson(json).to(SignedState).some + {.warning[UnsafeSetLen]: on.} + except ValueError: + return SignedState.none + except Exception as error: + raise (ref Defect)(msg: error.msg, parent: error) + +{.pop.} diff --git a/tests/nitro/examples.nim b/tests/nitro/examples.nim index 1b0ca68..ae49195 100644 --- a/tests/nitro/examples.nim +++ b/tests/nitro/examples.nim @@ -83,3 +83,9 @@ proc example*(_: type Signature): Signature = let key = PrivateKey.random let state = State.example key.sign(state) + +proc example*(_: type SignedState): SignedState = + let state = State.example + let key = PrivateKey.random + let signature = key.sign(state) + SignedState(state: state, signatures: @[signature]) diff --git a/tests/nitro/testJson.nim b/tests/nitro/testJson.nim new file mode 100644 index 0000000..8d3b49f --- /dev/null +++ b/tests/nitro/testJson.nim @@ -0,0 +1,19 @@ +import ./basics + +suite "json serialization": + + let payment = SignedState.example() + + test "serializes signed states to json": + check payment.toJson.len > 0 + + test "deserializes signed state": + check SignedState.fromJson(payment.toJson) == payment.some + + test "returns none when deserializing invalid json": + let invalid = "{" + check SignedState.fromJson(invalid).isNone + + test "returns none when json cannot be converted to signed state": + let invalid = "{}" + check SignedState.fromJson(invalid).isNone diff --git a/tests/testAll.nim b/tests/testAll.nim index 3ed9f24..c747a70 100644 --- a/tests/testAll.nim +++ b/tests/testAll.nim @@ -4,5 +4,6 @@ import ./nitro/protocol/testOutcome import ./nitro/protocol/testState import ./nitro/protocol/testSignature import ./nitro/testWallet +import ./nitro/testJson {.warning[UnusedImport]: off.}