go-blockchain/wire/decoder_test.go
Claude 6a3f8829cb
feat(wire): Phase 1 wire serialisation — bit-identical to C++ daemon
Add consensus-critical binary serialisation for blocks and transactions,
verified by computing the testnet genesis block hash and matching the C++
daemon output (cb9d5455...4963). Fixes Phase 0 type mismatches (variant
tags, field widths, missing fields) and adds encoder/decoder, tree hash,
and block/transaction hashing.

Key discovery: CryptoNote's get_object_hash(blobdata) prepends
varint(length) before hashing, so BlockHash = Keccak256(varint(len) || blob).

Co-Authored-By: Charon <charon@lethean.io>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 17:16:08 +00:00

205 lines
4.4 KiB
Go

// Copyright (c) 2017-2026 Lethean (https://lt.hn)
//
// Licensed under the European Union Public Licence (EUPL) version 1.2.
// SPDX-License-Identifier: EUPL-1.2
package wire
import (
"bytes"
"encoding/hex"
"testing"
)
func TestDecoderUint8_Good(t *testing.T) {
dec := NewDecoder(bytes.NewReader([]byte{0x42}))
got := dec.ReadUint8()
if dec.Err() != nil {
t.Fatalf("unexpected error: %v", dec.Err())
}
if got != 0x42 {
t.Errorf("got 0x%02x, want 0x42", got)
}
}
func TestDecoderUint64LE_Good(t *testing.T) {
tests := []struct {
name string
hex string
want uint64
}{
{"zero", "0000000000000000", 0},
{"one", "0100000000000000", 1},
{"genesis_nonce", "adb2b98417000000", 101011010221},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
b, _ := hex.DecodeString(tc.hex)
dec := NewDecoder(bytes.NewReader(b))
got := dec.ReadUint64LE()
if dec.Err() != nil {
t.Fatalf("unexpected error: %v", dec.Err())
}
if got != tc.want {
t.Errorf("got %d, want %d", got, tc.want)
}
})
}
}
func TestDecoderVarint_Good(t *testing.T) {
tests := []struct {
name string
hex string
want uint64
}{
{"zero", "00", 0},
{"one", "01", 1},
{"127", "7f", 127},
{"128", "8001", 128},
{"300", "ac02", 300},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
b, _ := hex.DecodeString(tc.hex)
dec := NewDecoder(bytes.NewReader(b))
got := dec.ReadVarint()
if dec.Err() != nil {
t.Fatalf("unexpected error: %v", dec.Err())
}
if got != tc.want {
t.Errorf("got %d, want %d", got, tc.want)
}
})
}
}
func TestDecoderBlob32_Good(t *testing.T) {
var want [32]byte
want[0] = 0xAB
want[31] = 0xCD
dec := NewDecoder(bytes.NewReader(want[:]))
var got [32]byte
dec.ReadBlob32(&got)
if dec.Err() != nil {
t.Fatalf("unexpected error: %v", dec.Err())
}
if got != want {
t.Error("blob32 mismatch")
}
}
func TestDecoderBlob64_Good(t *testing.T) {
var want [64]byte
want[0] = 0x11
want[63] = 0x99
dec := NewDecoder(bytes.NewReader(want[:]))
var got [64]byte
dec.ReadBlob64(&got)
if dec.Err() != nil {
t.Fatalf("unexpected error: %v", dec.Err())
}
if got != want {
t.Error("blob64 mismatch")
}
}
func TestDecoderStickyError_Bad(t *testing.T) {
dec := NewDecoder(bytes.NewReader(nil))
got := dec.ReadUint8()
if dec.Err() == nil {
t.Fatal("expected error from empty reader")
}
if got != 0 {
t.Errorf("got %d, want 0 on error", got)
}
// Subsequent reads should be no-ops.
_ = dec.ReadUint64LE()
_ = dec.ReadVarint()
var h [32]byte
dec.ReadBlob32(&h)
if h != ([32]byte{}) {
t.Error("expected zero blob on sticky error")
}
}
func TestDecoderReadBytes_Good(t *testing.T) {
data := []byte{0x01, 0x02, 0x03, 0x04}
dec := NewDecoder(bytes.NewReader(data))
got := dec.ReadBytes(4)
if dec.Err() != nil {
t.Fatalf("unexpected error: %v", dec.Err())
}
if !bytes.Equal(got, data) {
t.Error("bytes mismatch")
}
}
func TestDecoderReadBytesZero_Good(t *testing.T) {
dec := NewDecoder(bytes.NewReader(nil))
got := dec.ReadBytes(0)
if dec.Err() != nil {
t.Fatalf("unexpected error: %v", dec.Err())
}
if got != nil {
t.Errorf("expected nil, got %v", got)
}
}
func TestDecoderReadBytesOversize_Bad(t *testing.T) {
dec := NewDecoder(bytes.NewReader(nil))
_ = dec.ReadBytes(MaxBlobSize + 1)
if dec.Err() == nil {
t.Fatal("expected error for oversize blob")
}
}
func TestDecoderVarintOverflow_Ugly(t *testing.T) {
data := make([]byte, 11)
for i := range data {
data[i] = 0x80
}
dec := NewDecoder(bytes.NewReader(data))
_ = dec.ReadVarint()
if dec.Err() == nil {
t.Fatal("expected overflow error")
}
}
func TestEncoderDecoderRoundTrip_Good(t *testing.T) {
var buf bytes.Buffer
enc := NewEncoder(&buf)
enc.WriteUint8(0x01)
enc.WriteUint64LE(101011010221)
enc.WriteVarint(1770897600)
var h [32]byte
h[0] = 0xDE
h[31] = 0xAD
enc.WriteBlob32(&h)
if enc.Err() != nil {
t.Fatalf("encode error: %v", enc.Err())
}
dec := NewDecoder(bytes.NewReader(buf.Bytes()))
if v := dec.ReadUint8(); v != 0x01 {
t.Errorf("uint8: got 0x%02x, want 0x01", v)
}
if v := dec.ReadUint64LE(); v != 101011010221 {
t.Errorf("uint64: got %d, want 101011010221", v)
}
if v := dec.ReadVarint(); v != 1770897600 {
t.Errorf("varint: got %d, want 1770897600", v)
}
var gotH [32]byte
dec.ReadBlob32(&gotH)
if gotH != h {
t.Error("blob32 mismatch")
}
if dec.Err() != nil {
t.Fatalf("decode error: %v", dec.Err())
}
}