Files
zeekay 6439541c91 Prove the two backends agree, on bytes neither of them wrote
Generating C++ is a claim. This runs it.

Both probes build the same cases from the same inputs — every type the dialect
has, plus the request and response envelopes the generated client and dispatch
produce — and print the hex; the two outputs are the same file. Then each
probe reads both probes' bytes with its own generated accessors, four
crossings, all agreeing. Then both read a corpus of real P-chain transactions
produced by the Go chain implementation, 134 vectors of which 44 are truncated
or malformed on purpose, and answer the same fields in the same places.

Neither probe decides anything: every value printed comes from a generated
accessor or a generated builder, so a disagreement between the probes is a
disagreement between the backends and nowhere else.

It earned its keep immediately. Five corpus vectors separated a Go [N]byte
accessor answering zeros from a C++ span answering empty, and the round trip
found the zero Object panicking in the Go runtime where C++ answered zero.
Both are fixed; neither was visible by reading.

list<T> carries a pointer and a count and nothing about elements — the dialect
cannot state a stride — so both languages agree on the count, which is all
either can say. That gap is in the dialect, in both backends at once.
2026-09-06 08:35:14 -07:00

93 lines
3.3 KiB
C++

// Code generated by zapgen; DO NOT EDIT.
// source: basetx.zap
// SPDX-License-Identifier: BSD-3-Clause-Eco
#pragma once
#include <array>
#include <cstdint>
#include <expected>
#include <span>
#include <vector>
#include <zap/zap.hpp>
namespace xvm {
class BaseTx;
// Field byte offsets for BaseTx, and the size of its fixed section.
inline constexpr std::int64_t kBaseTxNetworkIDOff = 0;
inline constexpr std::int64_t kBaseTxBlockchainIDOff = 4;
inline constexpr std::int64_t kBaseTxOutsOff = 36;
inline constexpr std::int64_t kBaseTxInsOff = 44;
inline constexpr std::int64_t kBaseTxMemoOff = 52;
inline constexpr std::int64_t kBaseTxSize = 60;
// BaseTx is a zero-copy view into a ZAP-encoded BaseTx message. It borrows the
// bytes it was built over, which must outlive it, and copies no field.
class BaseTx {
public:
BaseTx() = default;
explicit BaseTx(zap::Object o) : o_(o) {}
bool is_null() const { return o_.is_null(); }
zap::Object object() const { return o_; }
std::uint32_t NetworkID() const { return o_.u32(kBaseTxNetworkIDOff); }
std::span<const std::uint8_t> BlockchainID() const {
static constexpr std::array<std::uint8_t, 32> zero{};
const auto b = o_.bytes_fixed(kBaseTxBlockchainIDOff, 32);
return b.size() == 32 ? b : std::span<const std::uint8_t>(zero);
}
zap::List Outs() const { return o_.list(kBaseTxOutsOff); }
zap::List Ins() const { return o_.list(kBaseTxInsOff); }
std::span<const std::uint8_t> Memo() const { return o_.bytes(kBaseTxMemoOff); }
private:
zap::Object o_;
};
// WrapBaseTx parses b and returns a typed view over it, or says why b is not a
// message. The view borrows b.
inline std::expected<BaseTx, zap::Error> WrapBaseTx(std::span<const std::uint8_t> b) {
auto m = zap::Message::parse(b);
if (!m) return std::unexpected(m.error());
return BaseTx(m->root());
}
// BaseTxInput collects the field values for NewBaseTx. A borrowed field is a view:
// it must outlive the call, not the buffer the call returns.
struct BaseTxInput {
std::uint32_t NetworkID{};
std::array<std::uint8_t, 32> BlockchainID{};
std::vector<std::span<const std::uint8_t>> Outs{};
std::vector<std::span<const std::uint8_t>> Ins{};
std::span<const std::uint8_t> Memo{};
};
// NewBaseTx writes a ZAP-encoded BaseTx message into a fresh buffer and returns it.
inline std::vector<std::uint8_t> NewBaseTx(const BaseTxInput& in) {
zap::Builder b(256, zap::kVersion1);
auto ob = b.start_object(kBaseTxSize);
ob.set_u32(kBaseTxNetworkIDOff, in.NetworkID);
ob.set_bytes_fixed(kBaseTxBlockchainIDOff, std::span<const std::uint8_t>(in.BlockchainID));
auto outs_list = b.start_list(0);
for (const auto& elem : in.Outs) {
outs_list.add_u32(static_cast<std::uint32_t>(elem.size()));
outs_list.add_bytes(elem);
}
ob.set_list(kBaseTxOutsOff, outs_list.finish().first, static_cast<std::int64_t>(in.Outs.size()));
auto ins_list = b.start_list(0);
for (const auto& elem : in.Ins) {
ins_list.add_u32(static_cast<std::uint32_t>(elem.size()));
ins_list.add_bytes(elem);
}
ob.set_list(kBaseTxInsOff, ins_list.finish().first, static_cast<std::int64_t>(in.Ins.size()));
ob.set_bytes(kBaseTxMemoOff, in.Memo);
ob.finish_as_root();
return b.finish();
}
} // namespace xvm