catcrafts.net/tests/ShouldParseEurcChains/main.cpp

142 lines
6.8 KiB
C++
Raw Normal View History

2026-08-15 00:54:05 +02:00
/*
catcrafts.net
Copyright (C) 2026 Catcrafts
The source code of this website is made available for viewing purposes only.
No permission is granted to copy, modify, distribute, or create derivative works.
*/
// The EURC rail's two parsers.
//
// The balance decoder is the line between "the buyer paid" and "the node
// errored", and the chains parser is the line between "watching a chain"
// and "silently not watching it". Both refuse rather than guess.
import std;
import Catcrafts.Shared;
import Catcrafts.Server;
using namespace Catcrafts;
namespace {
int failures = 0;
void Check(bool ok, std::string_view what, std::string_view got = {}) {
if (ok) return;
++failures;
std::println(std::cerr, "FAIL: {}{}{}", what,
got.empty() ? "" : " got: ", got);
}
} // namespace
int main() {
using Server::ParseEthCallUint;
Check(ParseEthCallUint(R"({"jsonrpc":"2.0","id":1,"result":"0x0"})") == 0,
"eurc: zero balance");
Check(ParseEthCallUint(
R"({"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000022001230"})")
== 570430000,
"eurc: €570.43 as 6-decimal base units, full 32-byte word");
Check(ParseEthCallUint(R"({"result":"0xFF"})") == 255,
"eurc: uppercase hex accepted");
2026-08-19 23:55:01 +02:00
// 2^63 does not fit. It must not wrap to negative, and it must not
// saturate to INT64_MAX either: a saturated maximum satisfies the covering
// comparison in CheckPaid, so a node answering 0xffff…ff would mark any
// order paid. int64 base units is already past EURC's entire supply, so an
// unrepresentable balance is a broken or lying node — "unknown", which
// neither settles nor lapses.
Check(!ParseEthCallUint(R"({"result":"0x8000000000000000"})").has_value(),
"eurc: an unrepresentable balance is unknown, not a covering maximum");
Check(!ParseEthCallUint(
R"({"result":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"})")
.has_value(),
"eurc: a lying node's 2^256-1 does not read as paid");
2026-08-15 00:54:05 +02:00
Check(!ParseEthCallUint(
R"({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"x"}})")
.has_value(),
"eurc: an RPC error is not a zero balance");
Check(!ParseEthCallUint(R"({"result":"21ff361c"})").has_value(),
"eurc: missing 0x rejected");
Check(!ParseEthCallUint(R"({"result":"0x"})").has_value(),
"eurc: empty word rejected");
Check(!ParseEthCallUint(R"({"result":42})").has_value(),
"eurc: numeric result rejected — the wire type is a hex string");
Check(!ParseEthCallUint("garbage").has_value(), "eurc: malformed payload");
const auto chains = Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://mainnet.base.org",
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
"chain_id":8453,"note":"lowest fees"},
{"name":"ethereum","rpc":"https://ethereum-rpc.publicnode.com",
"contract":"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c","chain_id":1}]})");
Check(chains.has_value() && chains->size() == 2, "eurc: chains file parses");
if (chains && chains->size() == 2) {
Check((*chains)[0].name == "base" && (*chains)[0].chainId == 8453,
"eurc: file order preserved — first entry is the recommendation");
Check((*chains)[0].contract == "0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42",
"eurc: contract lowercased for comparison");
Check((*chains)[0].note == "lowest fees", "eurc: note carried");
Check((*chains)[1].blockTag == "finalized" && (*chains)[1].decimals == 6,
"eurc: defaults");
}
Check(!Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://x.org","contract":"0xdeadbeef"}]})").has_value(),
"eurc: short contract rejects the whole file");
Check(!Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"ftp://x.org",
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"}]})").has_value(),
"eurc: non-http rpc rejects the whole file");
Check(!Server::ParseEurcChains(R"({"chains":[]})").has_value(),
"eurc: empty chain list rejected");
Check(!Server::ParseEurcChains("garbage").has_value(),
"eurc: malformed chains file rejected");
2026-08-19 23:55:01 +02:00
// Two chains with one name would share a connection-map slot, so the
// second's requests would go to the first's host and one chain would never
// be watched at all.
Check(!Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://a.example",
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"},
{"name":"base","rpc":"https://b.example",
"contract":"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c"}]})").has_value(),
"eurc: duplicate chain names reject the whole file");
// 18 decimals is legal ERC-20 but not representable here: cents × 10^16
// overflows int64 above €9.22, and the overflow answers "unknown", which
// neither settles nor lapses an order.
Check(!Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://a.example","decimals":18,
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"}]})").has_value(),
"eurc: decimals beyond what the arithmetic carries is refused");
// block_tag reaches the eth_call params array. A quote in it closed the
// JSON string and appended another param; a typo silenced the chain.
Check(!Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://a.example","block_tag":"latest\",\"0xdead",
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"}]})").has_value(),
"eurc: a block_tag that injects JSON is refused");
Check(!Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://a.example","block_tag":"finalised",
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"}]})").has_value(),
"eurc: a misspelled block_tag is refused, not silently never-settling");
Check(!Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://a.example","block_tag":"",
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"}]})").has_value(),
"eurc: an empty block_tag is refused");
{
// A specific block number stays legitimate.
const auto ok = Server::ParseEurcChains(R"({"chains":[
{"name":"base","rpc":"https://a.example","block_tag":"0x1b4",
"contract":"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"}]})");
Check(ok.has_value() && (*ok)[0].blockTag == "0x1b4",
"eurc: a hex block number is still accepted");
}
2026-08-15 00:54:05 +02:00
if (failures != 0) {
std::println(std::cerr, "{} check(s) failed", failures);
return 1;
}
return 0;
}