348 lines
15 KiB
Nim
348 lines
15 KiB
Nim
|
import std/math
|
||
|
import std/options
|
||
|
import std/strformat
|
||
|
import std/strutils
|
||
|
import std/unittest
|
||
|
import pkg/chronicles
|
||
|
import pkg/stew/byteutils
|
||
|
import pkg/stint
|
||
|
import pkg/codex/contracts/requests
|
||
|
from pkg/codex/rest/json import RestPurchase
|
||
|
import pkg/codex/utils/json as utilsjson
|
||
|
import pkg/questionable
|
||
|
import pkg/questionable/results
|
||
|
import ../examples
|
||
|
import ../helpers
|
||
|
|
||
|
checksuite "json serialization":
|
||
|
var request: StorageRequest
|
||
|
var requestJson: JsonNode
|
||
|
|
||
|
func flatten(s: string): string =
|
||
|
s.replace(" ")
|
||
|
.replace("\n")
|
||
|
|
||
|
setup:
|
||
|
request = StorageRequest(
|
||
|
client: Address.init("0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174").get(),
|
||
|
ask: StorageAsk(
|
||
|
slots: 4,
|
||
|
slotSize: (1 * 1024 * 1024 * 1024).u256, # 1 Gigabyte
|
||
|
duration: (10 * 60 * 60).u256, # 10 hours
|
||
|
collateral: 200.u256,
|
||
|
proofProbability: 4.u256, # require a proof roughly once every 4 periods
|
||
|
reward: 84.u256,
|
||
|
maxSlotLoss: 2 # 2 slots can be freed without data considered to be lost
|
||
|
),
|
||
|
content: StorageContent(
|
||
|
cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob",
|
||
|
erasure: StorageErasure(
|
||
|
totalChunks: 12,
|
||
|
),
|
||
|
por: StoragePoR(
|
||
|
u: @(array[480, byte].fromHex("0xc066dd7e405de5a795ce1e765209cfaa6de6c74829c607d1a2fe53107107a2981baccff95e2fc2e38d08303e8ab59a1169cfc9bfbfa2294c6df065456056a0d0106f66d6fe48300222758fd6fd286a1ac9060d1a295e19f931a8d3ad2c47eb131bea267fe942d460fda96fd4bf663148cd90fbb1b670dd97aae70394248b75cfbb98f71e8c69f50381e0558884d5d9aa75147923b55386c66f75f63024b698eeb0ff994bfdb610eea1b7c75e87bdb54843071bc64fbaf93e5dc214e875bd95bd30f167b69df1de819a34cc71a3a0465f5c1d1b7e5b98de6017ff3e3c059536f974471fe62e0f224eba8f96352a8ee51befbf4c31c88ad0fc8ff4e9d9da174a455a1c30fd61ac977145d3677a08167d508fae207f9458a9b19d4ceec2be30506e2d70cc0362c2bcdb0f73d63fa5e79f9b2901bc870ac8b2a264d50e1862ea177eb587bcd16ceb7d66f96f198cadec3f644af4d3cbe478bc1665818401f89107053d1750047fb7cfc47938bec2cd006db9c176ce337e41160077e353f87ab319e5b9df92282916ef99334c067f6ca20c3d7cbc12b95180b7bba762993a4dbdf4242032da8865988183738d279918906c3357701d74e5d8f5142315ae8f6d0f93537abc3545118e953f983317657a9d8b86e4305ea49e10f80ea07dc7ea7321b32c")),
|
||
|
publicKey: @(array[96, byte].fromHex("0xb231b19de641f678d250623b2b76099ab4bbd67aac19dcf42ded946831e3366d2a20af0fd9e841197e7e64d7639da4518b76c353db480087e21d55b470f24a180d6d6c8265bf3895e2e4e4e54b8ca9334d62b22feeeed8e77e54bfbc8fae6b62")),
|
||
|
name: @(array[512, byte].fromHex("0x75b2ac401efd21e60e84a69288da6fff28c7badaae885e417f35055a4e10cb514855f68a0ae18bf42861426c9fc34af13df2f2d04dc68933af78bf3fc396953f301b95f6d6af54ec9fc871c292096e45b91e836063f128c2d1469adbee49bc9b7d62985a858801e4df2cb77eb41ee7b50a8a4e5afb5b585f9034a2808f81bd95b9a3fbdd2579331023f1816a1ecbe7a31e386721a72e3d0ff6087326fba8442dfd22d1182c85906d796e697231c2d7d4a888ae256c79a9019974a4c729d981f3e554f48895e27fe8f45da46bc48c35cc74ae5a31dfea8baa1334fa7f106cdc4ec54452f39c823fa0af97769217cc16c78eb7d0c494c26d2f286f09a507bd04cb15963270bffefb28258176d9e10b7aaad76cdd86e0fe49437eb83c1c0650cb5920e32dc54f3a21a70308b7312b47ce57ef72c2c19eba5027612128b747e80b88c912d7fc10177e67beda0ed5bb8fdfc268bfa5a5c700da953c56bcc79b9186da99ee19a6fa954f44bdcbc7c7f4d208fb750bad587d5513fbaccd511b9b6e0cd798120de87b9c0c410b3b85c75a8a0f469d9973a1ec4c86982cf4fe1a2be21a9206aaabb1ad2fafa628d5156d2ec99ee30fc0ddb9dca6a4cd3a7987227315ceeaa832909853cabaf33c976b59cf5ed9643781d92ab769c0d0aa3bcef40b41b4b3e6fc5a00c9dfbf794047d9cfb97d9d669d00520b6492760a08dba65b0fd7e6d0"))
|
||
|
)
|
||
|
),
|
||
|
expiry: 1691545330.u256,
|
||
|
nonce: Nonce array[32, byte].fromHex("0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1")
|
||
|
)
|
||
|
requestJson = """{
|
||
|
"client": "0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174",
|
||
|
"ask": {
|
||
|
"slots": 4,
|
||
|
"slotSize": "1073741824",
|
||
|
"duration": "36000",
|
||
|
"proofProbability": "4",
|
||
|
"reward": "84",
|
||
|
"collateral": "200",
|
||
|
"maxSlotLoss": 2
|
||
|
},
|
||
|
"content": {
|
||
|
"cid": "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob",
|
||
|
"erasure": {
|
||
|
"totalChunks": 12
|
||
|
},
|
||
|
"por": {
|
||
|
"u": "0xc066dd7e405de5a795ce1e765209cfaa6de6c74829c607d1a2fe53107107a2981baccff95e2fc2e38d08303e8ab59a1169cfc9bfbfa2294c6df065456056a0d0106f66d6fe48300222758fd6fd286a1ac9060d1a295e19f931a8d3ad2c47eb131bea267fe942d460fda96fd4bf663148cd90fbb1b670dd97aae70394248b75cfbb98f71e8c69f50381e0558884d5d9aa75147923b55386c66f75f63024b698eeb0ff994bfdb610eea1b7c75e87bdb54843071bc64fbaf93e5dc214e875bd95bd30f167b69df1de819a34cc71a3a0465f5c1d1b7e5b98de6017ff3e3c059536f974471fe62e0f224eba8f96352a8ee51befbf4c31c88ad0fc8ff4e9d9da174a455a1c30fd61ac977145d3677a08167d508fae207f9458a9b19d4ceec2be30506e2d70cc0362c2bcdb0f73d63fa5e79f9b2901bc870ac8b2a264d50e1862ea177eb587bcd16ceb7d66f96f198cadec3f644af4d3cbe478bc1665818401f89107053d1750047fb7cfc47938bec2cd006db9c176ce337e41160077e353f87ab319e5b9df92282916ef99334c067f6ca20c3d7cbc12b95180b7bba762993a4dbdf4242032da8865988183738d279918906c3357701d74e5d8f5142315ae8f6d0f93537abc3545118e953f983317657a9d8b86e4305ea49e10f80ea07dc7ea7321b32c",
|
||
|
"publicKey": "0xb231b19de641f678d250623b2b76099ab4bbd67aac19dcf42ded946831e3366d2a20af0fd9e841197e7e64d7639da4518b76c353db480087e21d55b470f24a180d6d6c8265bf3895e2e4e4e54b8ca9334d62b22feeeed8e77e54bfbc8fae6b62",
|
||
|
"name": "0x75b2ac401efd21e60e84a69288da6fff28c7badaae885e417f35055a4e10cb514855f68a0ae18bf42861426c9fc34af13df2f2d04dc68933af78bf3fc396953f301b95f6d6af54ec9fc871c292096e45b91e836063f128c2d1469adbee49bc9b7d62985a858801e4df2cb77eb41ee7b50a8a4e5afb5b585f9034a2808f81bd95b9a3fbdd2579331023f1816a1ecbe7a31e386721a72e3d0ff6087326fba8442dfd22d1182c85906d796e697231c2d7d4a888ae256c79a9019974a4c729d981f3e554f48895e27fe8f45da46bc48c35cc74ae5a31dfea8baa1334fa7f106cdc4ec54452f39c823fa0af97769217cc16c78eb7d0c494c26d2f286f09a507bd04cb15963270bffefb28258176d9e10b7aaad76cdd86e0fe49437eb83c1c0650cb5920e32dc54f3a21a70308b7312b47ce57ef72c2c19eba5027612128b747e80b88c912d7fc10177e67beda0ed5bb8fdfc268bfa5a5c700da953c56bcc79b9186da99ee19a6fa954f44bdcbc7c7f4d208fb750bad587d5513fbaccd511b9b6e0cd798120de87b9c0c410b3b85c75a8a0f469d9973a1ec4c86982cf4fe1a2be21a9206aaabb1ad2fafa628d5156d2ec99ee30fc0ddb9dca6a4cd3a7987227315ceeaa832909853cabaf33c976b59cf5ed9643781d92ab769c0d0aa3bcef40b41b4b3e6fc5a00c9dfbf794047d9cfb97d9d669d00520b6492760a08dba65b0fd7e6d0"
|
||
|
}
|
||
|
},
|
||
|
"expiry": "1691545330",
|
||
|
"nonce": "0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1"
|
||
|
}""".parseJson
|
||
|
|
||
|
test "serializes UInt256 to non-hex string representation":
|
||
|
check (% 100000.u256) == newJString("100000")
|
||
|
|
||
|
test "serializes sequence to an array":
|
||
|
let json = % @[1, 2, 3]
|
||
|
let expected = "[1,2,3]"
|
||
|
check $json == expected
|
||
|
|
||
|
test "serializes Option[T] when has a value":
|
||
|
let obj = %(some 1)
|
||
|
let expected = "1"
|
||
|
check $obj == expected
|
||
|
|
||
|
test "serializes Option[T] when doesn't have a value":
|
||
|
let obj = %(none int)
|
||
|
let expected = "null"
|
||
|
check $obj == expected
|
||
|
|
||
|
test "serializes uints int.high or smaller":
|
||
|
let largeUInt: uint = uint(int.high)
|
||
|
check %largeUInt == newJInt(BiggestInt(largeUInt))
|
||
|
|
||
|
test "serializes large uints":
|
||
|
let largeUInt: uint = uint(int.high) + 1'u
|
||
|
check %largeUInt == newJString($largeUInt)
|
||
|
|
||
|
|
||
|
test "serializes Inf float":
|
||
|
check %Inf == newJString("inf")
|
||
|
|
||
|
test "serializes -Inf float":
|
||
|
check %(-Inf) == newJString("-inf")
|
||
|
|
||
|
test "deserializes NaN float":
|
||
|
check %NaN == newJString("nan")
|
||
|
|
||
|
test "can construct json objects with %*":
|
||
|
type MyObj = object
|
||
|
mystring {.serialize.}: string
|
||
|
myint {.serialize.}: int
|
||
|
myoption {.serialize.}: ?bool
|
||
|
|
||
|
let myobj = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
||
|
let mystuint = 100000.u256
|
||
|
|
||
|
let json = %*{
|
||
|
"myobj": myobj,
|
||
|
"mystuint": mystuint
|
||
|
}
|
||
|
|
||
|
let expected = """{
|
||
|
"myobj": {
|
||
|
"mystring": "abc",
|
||
|
"myint": 123,
|
||
|
"myoption": true
|
||
|
},
|
||
|
"mystuint": "100000"
|
||
|
}""".flatten
|
||
|
|
||
|
check $json == expected
|
||
|
|
||
|
test "only serializes marked fields":
|
||
|
type MyObj = object
|
||
|
mystring {.serialize.}: string
|
||
|
myint {.serialize.}: int
|
||
|
mybool: bool
|
||
|
|
||
|
let obj = % MyObj(mystring: "abc", myint: 1, mybool: true)
|
||
|
|
||
|
let expected = """{
|
||
|
"mystring": "abc",
|
||
|
"myint": 1
|
||
|
}""".flatten
|
||
|
|
||
|
check $obj == expected
|
||
|
|
||
|
test "serializes ref objects":
|
||
|
type MyRef = ref object
|
||
|
mystring {.serialize.}: string
|
||
|
myint {.serialize.}: int
|
||
|
|
||
|
let obj = % MyRef(mystring: "abc", myint: 1)
|
||
|
|
||
|
let expected = """{
|
||
|
"mystring": "abc",
|
||
|
"myint": 1
|
||
|
}""".flatten
|
||
|
|
||
|
check $obj == expected
|
||
|
|
||
|
test "serializes RestPurchase":
|
||
|
let request = % RestPurchase(
|
||
|
request: some request,
|
||
|
requestId: RequestId.fromHex("0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1"),
|
||
|
error: some "error",
|
||
|
state: "state"
|
||
|
)
|
||
|
let expected = """{
|
||
|
"requestId": "0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1",
|
||
|
"request": {
|
||
|
"client": "0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174",
|
||
|
"ask": {
|
||
|
"slots": 4,
|
||
|
"slotSize": "1073741824",
|
||
|
"duration": "36000",
|
||
|
"proofProbability": "4",
|
||
|
"reward": "84",
|
||
|
"collateral": "200",
|
||
|
"maxSlotLoss": 2
|
||
|
},
|
||
|
"content": {
|
||
|
"cid": "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"
|
||
|
},
|
||
|
"expiry": "1691545330"
|
||
|
},
|
||
|
"state": "state",
|
||
|
"error": "error"
|
||
|
}""".flatten
|
||
|
check $request == expected
|
||
|
|
||
|
test "serializes StorageRequest":
|
||
|
let expected = """{
|
||
|
"client": "0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174",
|
||
|
"ask": {
|
||
|
"slots": 4,
|
||
|
"slotSize": "1073741824",
|
||
|
"duration": "36000",
|
||
|
"proofProbability": "4",
|
||
|
"reward": "84",
|
||
|
"collateral": "200",
|
||
|
"maxSlotLoss": 2
|
||
|
},
|
||
|
"content": {
|
||
|
"cid": "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"
|
||
|
},
|
||
|
"expiry": "1691545330"
|
||
|
}""".flatten
|
||
|
check $(%request) == expected
|
||
|
|
||
|
test "deserializes UInt256 from non-hex string representation":
|
||
|
let json = newJString("100000")
|
||
|
check !UInt256.fromJson(json) == 100000.u256
|
||
|
|
||
|
test "deserializes Option[T] when has a value":
|
||
|
let json = newJInt(1)
|
||
|
check (!fromJson(?int, json) == some 1)
|
||
|
|
||
|
test "deserializes Option[T] when doesn't have a value":
|
||
|
let json = newJNull()
|
||
|
check !fromJson(?int, json) == none int
|
||
|
|
||
|
test "deserializes float":
|
||
|
let json = newJFloat(1.234)
|
||
|
check !float.fromJson(json) == 1.234
|
||
|
|
||
|
test "deserializes Inf float":
|
||
|
let json = newJString("inf")
|
||
|
check !float.fromJson(json) == Inf
|
||
|
|
||
|
test "deserializes -Inf float":
|
||
|
let json = newJString("-inf")
|
||
|
check !float.fromJson(json) == -Inf
|
||
|
|
||
|
test "deserializes NaN float":
|
||
|
let json = newJString("nan")
|
||
|
check float.fromJson(json).get.isNaN
|
||
|
|
||
|
test "deserializes array to sequence":
|
||
|
let expected = @[1, 2, 3]
|
||
|
let json = "[1,2,3]".parseJson
|
||
|
check !seq[int].fromJson(json) == expected
|
||
|
|
||
|
test "deserializes uints int.high or smaller":
|
||
|
let largeUInt: uint = uint(int.high)
|
||
|
let json = newJInt(BiggestInt(largeUInt))
|
||
|
check !uint.fromJson(json) == largeUInt
|
||
|
|
||
|
test "deserializes large uints":
|
||
|
let largeUInt: uint = uint(int.high) + 1'u
|
||
|
let json = newJString($BiggestUInt(largeUInt))
|
||
|
check !uint.fromJson(json) == largeUInt
|
||
|
|
||
|
test "can deserialize json objects":
|
||
|
type MyObj = object
|
||
|
mystring: string
|
||
|
myint: int
|
||
|
myoption: ?bool
|
||
|
|
||
|
let expected = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
||
|
|
||
|
let json = parseJson("""{
|
||
|
"mystring": "abc",
|
||
|
"myint": 123,
|
||
|
"myoption": true
|
||
|
}""")
|
||
|
check !MyObj.fromJson(json) == expected
|
||
|
|
||
|
test "ignores serialize pragma when deserializing":
|
||
|
type MyObj = object
|
||
|
mystring {.serialize.}: string
|
||
|
mybool: bool
|
||
|
|
||
|
let expected = MyObj(mystring: "abc", mybool: true)
|
||
|
|
||
|
let json = parseJson("""{
|
||
|
"mystring": "abc",
|
||
|
"mybool": true
|
||
|
}""")
|
||
|
|
||
|
check !MyObj.fromJson(json) == expected
|
||
|
|
||
|
test "deserializes objects with extra fields":
|
||
|
type MyObj = object
|
||
|
mystring: string
|
||
|
mybool: bool
|
||
|
|
||
|
let expected = MyObj(mystring: "abc", mybool: true)
|
||
|
|
||
|
let json = """{
|
||
|
"mystring": "abc",
|
||
|
"mybool": true,
|
||
|
"extra": "extra"
|
||
|
}""".parseJson
|
||
|
check !MyObj.fromJson(json) == expected
|
||
|
|
||
|
test "deserializes objects with less fields":
|
||
|
type MyObj = object
|
||
|
mystring: string
|
||
|
mybool: bool
|
||
|
|
||
|
let expected = MyObj(mystring: "abc", mybool: false)
|
||
|
|
||
|
let json = """{
|
||
|
"mystring": "abc"
|
||
|
}""".parseJson
|
||
|
check !MyObj.fromJson(json) == expected
|
||
|
|
||
|
test "deserializes ref objects":
|
||
|
type MyRef = ref object
|
||
|
mystring: string
|
||
|
myint: int
|
||
|
|
||
|
let expected = MyRef(mystring: "abc", myint: 1)
|
||
|
|
||
|
let json = """{
|
||
|
"mystring": "abc",
|
||
|
"myint": 1
|
||
|
}""".parseJson
|
||
|
|
||
|
let deserialized = !MyRef.fromJson(json)
|
||
|
check deserialized.mystring == expected.mystring
|
||
|
check deserialized.myint == expected.myint
|
||
|
|
||
|
test "deserializes StorageRequest":
|
||
|
check !StorageRequest.fromJson(requestJson) == request
|
||
|
|
||
|
test "deserializes RestPurchase":
|
||
|
let json = """{
|
||
|
"requestId": "0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1",
|
||
|
"state": "state",
|
||
|
"error": "error"
|
||
|
}""".parseJson
|
||
|
json["request"] = requestJson
|
||
|
|
||
|
let expected = RestPurchase(
|
||
|
requestId: RequestId.fromHex("0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1"),
|
||
|
state: "state",
|
||
|
error: some "error",
|
||
|
request: some request
|
||
|
)
|
||
|
check !RestPurchase.fromJson(json) == expected
|