go-blockchain/chain/integration_test.go
Claude 3fe3b558d7
test(chain): verify ring signatures during full chain sync
All pre-HF4 spending transactions on testnet pass NLSAG ring
signature verification end-to-end.

Co-Authored-By: Charon <charon@lethean.io>
2026-02-21 20:58:29 +00:00

164 lines
4 KiB
Go

//go:build integration
// 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 chain
import (
"context"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/require"
"forge.lthn.ai/core/go-blockchain/config"
"forge.lthn.ai/core/go-blockchain/rpc"
"forge.lthn.ai/core/go-blockchain/types"
store "forge.lthn.ai/core/go-store"
)
const testnetRPCAddr = "http://localhost:46941"
func TestIntegration_SyncFirst10Blocks(t *testing.T) {
client := rpc.NewClientWithHTTP(testnetRPCAddr, &http.Client{Timeout: 30 * time.Second})
// Check daemon is reachable.
remoteHeight, err := client.GetHeight()
if err != nil {
t.Skipf("testnet daemon not reachable at %s: %v", testnetRPCAddr, err)
}
t.Logf("testnet height: %d", remoteHeight)
s, err := store.New(":memory:")
if err != nil {
t.Fatalf("store.New: %v", err)
}
defer s.Close()
c := New(s)
// Sync first 10 blocks (or fewer if chain is shorter).
targetHeight := uint64(10)
if remoteHeight < targetHeight {
targetHeight = remoteHeight
}
// Sync in a loop, stopping early.
for {
h, _ := c.Height()
if h >= targetHeight {
break
}
if err := c.Sync(context.Background(), client, DefaultSyncOptions()); err != nil {
t.Fatalf("Sync: %v", err)
}
}
// Verify genesis block.
_, genMeta, err := c.GetBlockByHeight(0)
if err != nil {
t.Fatalf("GetBlockByHeight(0): %v", err)
}
expectedHash, _ := types.HashFromHex(GenesisHash)
if genMeta.Hash != expectedHash {
t.Errorf("genesis hash: got %s, want %s", genMeta.Hash, expectedHash)
}
t.Logf("genesis block verified: %s", genMeta.Hash)
// Verify chain height.
finalHeight, _ := c.Height()
t.Logf("synced %d blocks", finalHeight)
if finalHeight < targetHeight {
t.Errorf("expected at least %d blocks, got %d", targetHeight, finalHeight)
}
// Verify blocks are sequential.
for i := uint64(1); i < finalHeight; i++ {
_, meta, err := c.GetBlockByHeight(i)
if err != nil {
t.Fatalf("GetBlockByHeight(%d): %v", i, err)
}
_, prevMeta, err := c.GetBlockByHeight(i - 1)
if err != nil {
t.Fatalf("GetBlockByHeight(%d): %v", i-1, err)
}
// Block at height i should reference hash of block at height i-1.
if meta.Height != i {
t.Errorf("block %d: height %d", i, meta.Height)
}
_ = prevMeta // linkage verified during sync
}
}
func TestIntegration_SyncToTip(t *testing.T) {
if testing.Short() {
t.Skip("skipping long sync test in short mode")
}
client := rpc.NewClientWithHTTP(testnetRPCAddr, &http.Client{Timeout: 60 * time.Second})
remoteHeight, err := client.GetHeight()
if err != nil {
t.Skipf("testnet daemon not reachable at %s: %v", testnetRPCAddr, err)
}
t.Logf("testnet height: %d", remoteHeight)
s, err := store.New(":memory:")
require.NoError(t, err)
defer s.Close()
c := New(s)
opts := SyncOptions{
VerifySignatures: false, // first pass: no sigs
Forks: config.TestnetForks,
}
err = c.Sync(context.Background(), client, opts)
require.NoError(t, err)
finalHeight, _ := c.Height()
t.Logf("synced %d blocks", finalHeight)
require.Equal(t, remoteHeight, finalHeight)
// Verify genesis.
_, genMeta, err := c.GetBlockByHeight(0)
require.NoError(t, err)
expectedHash, _ := types.HashFromHex(GenesisHash)
require.Equal(t, expectedHash, genMeta.Hash)
}
func TestIntegration_SyncWithSignatures(t *testing.T) {
if testing.Short() {
t.Skip("skipping long sync test in short mode")
}
client := rpc.NewClientWithHTTP(testnetRPCAddr, &http.Client{Timeout: 60 * time.Second})
remoteHeight, err := client.GetHeight()
if err != nil {
t.Skipf("testnet daemon not reachable: %v", err)
}
s, err := store.New(":memory:")
require.NoError(t, err)
defer s.Close()
c := New(s)
opts := SyncOptions{
VerifySignatures: true,
Forks: config.TestnetForks,
}
err = c.Sync(context.Background(), client, opts)
require.NoError(t, err)
finalHeight, _ := c.Height()
t.Logf("synced %d blocks with signature verification", finalHeight)
require.Equal(t, remoteHeight, finalHeight)
}