mirror of
https://github.com/lux-cpp/node.git
synced 2026-10-30 14:48:15 +00:00
The C-Chain was linked in: evm.cpp executed blocks in memory, import.cpp read exports into it and eth.cpp served its RPC, while cevm's EvmBackend, a second C++ EVM, already had persistence, receipts and eth_call. The owner chose EvmBackend as the one C++ EVM. The node now starts cevm's plugin the way Go starts a VM. Initialize carries the genesis, and --import-chain-data in the config as Go's config bridge does. Votes bind the block with Binding::Transport, as luxd's do, and /v1/chain/<alias> is relayed to the address the plugin's CreateHandlers names, with its status. evm.cpp, eth.cpp and import.cpp are gone, with the tests that drove them; hostile_decode keeps the RLP bound peer.cpp still depends on. A network is a Spec: name, network id, chain id, genesis and VM. A brand's daemon is run(specs), and luxd is the local Lux spec. run() refuses a spec whose genesis names another chain. plugin_test drives this build's cevm through build, verify and accept; 16 node tests and 17 consensus tests pass. Co-authored-by: Hanzo Dev <[email protected]>
89 lines
3.4 KiB
C++
89 lines
3.4 KiB
C++
// Copyright (C) 2026, Lux Industries, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: BSD-3-Clause-Eco
|
|
//
|
|
// hostile_decode_test.cpp — the RLP decoder, fed bytes a stranger would send.
|
|
//
|
|
// rlp::item reads what a luxd peer puts on the p2p link (peer.cpp), so it is
|
|
// reachable by anyone who can open a connection. It may not abort, allocate on
|
|
// a number it was handed, or read past its buffer — a decoder that dies on bad
|
|
// input is a halt available to anyone who can reach one node.
|
|
//
|
|
// The EVM's own decoders — a transaction, a block — are the EVM plugin's, and
|
|
// are held to the same cases where the plugin is built.
|
|
|
|
#include "../src/rlp.hpp" // the decoder itself, where the bound actually lives
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace lux::node;
|
|
|
|
namespace {
|
|
|
|
int g_fail = 0;
|
|
void check(bool ok, const std::string& what) {
|
|
std::printf(" %s %s\n", ok ? "ok " : "FAIL", what.c_str());
|
|
if (!ok) ++g_fail;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
std::printf("node — what a stranger can send the RLP decoder\n\n");
|
|
|
|
// THE OVERFLOW, asserted where it lives: the bound is in rlp::item.
|
|
//
|
|
// 0xbf is a long string whose length is the next EIGHT bytes; all-0xff
|
|
// declares 2^64-1. The header is 9 bytes, so a bound written as
|
|
// `size < head + len` computes 9 + (2^64-1), which WRAPS to 8 — less than
|
|
// the buffer — and the length is ACCEPTED, after which subspan is asked for
|
|
// 18 exabytes. Written as `len > size - head` it cannot wrap.
|
|
{
|
|
std::vector<std::uint8_t> v{0xbf};
|
|
for (int i = 0; i < 8; ++i) v.push_back(0xff);
|
|
v.resize(64, 0xaa);
|
|
check(!rlp::item(v).has_value(),
|
|
"rlp::item refuses a declared length of 2^64-1 (the add-and-compare overflow)");
|
|
}
|
|
{
|
|
std::vector<std::uint8_t> v{0xff}; // the same, as a LIST
|
|
for (int i = 0; i < 8; ++i) v.push_back(0xff);
|
|
v.resize(64, 0xaa);
|
|
check(!rlp::item(v).has_value(), "rlp::item refuses the same overflow declared as a list");
|
|
}
|
|
// Every wrapping length: head is 9, so len in [2^64-8, 2^64-1] lands
|
|
// head+len in [1, 8] and slips under a 64-byte buffer.
|
|
{
|
|
bool all_refused = true;
|
|
for (int k = 1; k <= 8; ++k) {
|
|
std::vector<std::uint8_t> v{0xbf};
|
|
const std::uint64_t len = ~0ull - static_cast<std::uint64_t>(k) + 1;
|
|
for (int i = 7; i >= 0; --i) v.push_back(std::uint8_t((len >> (i * 8)) & 0xff));
|
|
v.resize(64, 0xaa);
|
|
if (rlp::item(v).has_value()) all_refused = false;
|
|
}
|
|
check(all_refused, "rlp::item refuses every length that wraps the bound");
|
|
}
|
|
{
|
|
std::vector<std::uint8_t> v{0xbf, 0x7f};
|
|
for (int i = 0; i < 7; ++i) v.push_back(0xff);
|
|
v.resize(64, 0xaa);
|
|
check(!rlp::item(v).has_value(), "rlp::item refuses a huge-but-unwrapped declared length");
|
|
}
|
|
// A well-formed long string is still ACCEPTED — the bound rejects lies, not
|
|
// length. Without this the two checks above would pass on a decoder that
|
|
// refused everything.
|
|
{
|
|
std::vector<std::uint8_t> v{0xb8, 60};
|
|
v.resize(62, 0xaa);
|
|
const auto it = rlp::item(v);
|
|
check(it.has_value() && it->payload.size() == 60,
|
|
"rlp::item still accepts an honest 60-byte string");
|
|
}
|
|
|
|
std::printf("\n%s\n", g_fail ? "FAIL" : "PASS — hostile input is refused, not fatal");
|
|
return g_fail ? 1 : 0;
|
|
}
|