From 940155e82216f54a76616692b1a20fca83fb0967 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Feb 2026 01:04:59 +0000 Subject: [PATCH] feat(consensus): signature verification scaffold VerifyTransactionSignatures with structural checks for v1 NLSAG and v2+ CLSAG. Crypto bridge calls marked as TODO for wiring. Co-Authored-By: Charon --- consensus/verify.go | 71 ++++++++++++++++++++++++++++++++++++++++ consensus/verify_test.go | 25 ++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 consensus/verify.go create mode 100644 consensus/verify_test.go diff --git a/consensus/verify.go b/consensus/verify.go new file mode 100644 index 0000000..32170b6 --- /dev/null +++ b/consensus/verify.go @@ -0,0 +1,71 @@ +// 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 consensus + +import ( + "fmt" + + "forge.lthn.ai/core/go-blockchain/config" + "forge.lthn.ai/core/go-blockchain/types" +) + +// RingOutputsFn fetches the public keys for a ring at the given amount +// and offsets. Used to decouple consensus/ from chain storage. +type RingOutputsFn func(amount uint64, offsets []uint64) ([]types.PublicKey, error) + +// VerifyTransactionSignatures verifies all ring signatures in a transaction. +// For coinbase transactions, this is a no-op (no signatures). +// For pre-HF4 transactions, NLSAG ring signatures are verified. +// For post-HF4, CLSAG signatures and proofs are verified. +// +// getRingOutputs may be nil for coinbase-only checks. +func VerifyTransactionSignatures(tx *types.Transaction, forks []config.HardFork, + height uint64, getRingOutputs RingOutputsFn) error { + + // Coinbase: no signatures. + if isCoinbase(tx) { + return nil + } + + hf4Active := config.IsHardForkActive(forks, config.HF4Zarcanum, height) + + if !hf4Active { + return verifyV1Signatures(tx, getRingOutputs) + } + + return verifyV2Signatures(tx, getRingOutputs) +} + +// verifyV1Signatures checks NLSAG ring signatures for pre-HF4 transactions. +func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) error { + // Count key inputs. + var keyInputCount int + for _, vin := range tx.Vin { + if _, ok := vin.(types.TxInputToKey); ok { + keyInputCount++ + } + } + + if len(tx.Signatures) != keyInputCount { + return fmt.Errorf("consensus: signature count %d != input count %d", + len(tx.Signatures), keyInputCount) + } + + // Actual NLSAG verification requires the crypto bridge and ring outputs. + // When getRingOutputs is nil, we can only check structural correctness. + if getRingOutputs == nil { + return nil + } + + // TODO: Wire up crypto.CheckRingSignature() for each input. + return nil +} + +// verifyV2Signatures checks CLSAG signatures and proofs for post-HF4 transactions. +func verifyV2Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) error { + // TODO: Wire up CLSAG verification and proof checks. + return nil +} diff --git a/consensus/verify_test.go b/consensus/verify_test.go new file mode 100644 index 0000000..ea4ec87 --- /dev/null +++ b/consensus/verify_test.go @@ -0,0 +1,25 @@ +//go:build !integration + +package consensus + +import ( + "testing" + + "forge.lthn.ai/core/go-blockchain/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVerifyTransactionSignatures_Good_Coinbase(t *testing.T) { + // Coinbase transactions have no signatures to verify. + tx := validMinerTx(100) + err := VerifyTransactionSignatures(tx, config.MainnetForks, 100, nil) + require.NoError(t, err) +} + +func TestVerifyTransactionSignatures_Bad_MissingSigs(t *testing.T) { + tx := validV1Tx() + tx.Signatures = nil // no signatures + err := VerifyTransactionSignatures(tx, config.MainnetForks, 100, nil) + assert.Error(t, err) +}