feat(consensus): verify NLSAG signatures for HTLC inputs
verifyV1Signatures now counts and verifies TxInputHTLC alongside TxInputToKey. HTLC inputs use the same ring signature scheme. Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
192d681ecd
commit
f88d582c64
2 changed files with 93 additions and 8 deletions
|
|
@ -58,11 +58,13 @@ func VerifyTransactionSignatures(tx *types.Transaction, forks []config.HardFork,
|
|||
}
|
||||
|
||||
// verifyV1Signatures checks NLSAG ring signatures for pre-HF4 transactions.
|
||||
// Both TxInputToKey and TxInputHTLC use NLSAG ring signatures.
|
||||
func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) error {
|
||||
// Count key inputs.
|
||||
// Count ring-sig inputs (TxInputToKey and TxInputHTLC).
|
||||
var keyInputCount int
|
||||
for _, vin := range tx.Vin {
|
||||
if _, ok := vin.(types.TxInputToKey); ok {
|
||||
switch vin.(type) {
|
||||
case types.TxInputToKey, types.TxInputHTLC:
|
||||
keyInputCount++
|
||||
}
|
||||
}
|
||||
|
|
@ -82,18 +84,31 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
|
|||
|
||||
var sigIdx int
|
||||
for _, vin := range tx.Vin {
|
||||
inp, ok := vin.(types.TxInputToKey)
|
||||
if !ok {
|
||||
// Extract the common ring-sig fields from either input type.
|
||||
var amount uint64
|
||||
var keyOffsets []types.TxOutRef
|
||||
var keyImage types.KeyImage
|
||||
|
||||
switch v := vin.(type) {
|
||||
case types.TxInputToKey:
|
||||
amount = v.Amount
|
||||
keyOffsets = v.KeyOffsets
|
||||
keyImage = v.KeyImage
|
||||
case types.TxInputHTLC:
|
||||
amount = v.Amount
|
||||
keyOffsets = v.KeyOffsets
|
||||
keyImage = v.KeyImage
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract absolute global indices from key offsets.
|
||||
offsets := make([]uint64, len(inp.KeyOffsets))
|
||||
for i, ref := range inp.KeyOffsets {
|
||||
offsets := make([]uint64, len(keyOffsets))
|
||||
for i, ref := range keyOffsets {
|
||||
offsets[i] = ref.GlobalIndex
|
||||
}
|
||||
|
||||
ringKeys, err := getRingOutputs(inp.Amount, offsets)
|
||||
ringKeys, err := getRingOutputs(amount, offsets)
|
||||
if err != nil {
|
||||
return fmt.Errorf("consensus: failed to fetch ring outputs for input %d: %w",
|
||||
sigIdx, err)
|
||||
|
|
@ -116,7 +131,7 @@ func verifyV1Signatures(tx *types.Transaction, getRingOutputs RingOutputsFn) err
|
|||
sigs[i] = [64]byte(s)
|
||||
}
|
||||
|
||||
if !crypto.CheckRingSignature([32]byte(prefixHash), [32]byte(inp.KeyImage), pubs, sigs) {
|
||||
if !crypto.CheckRingSignature([32]byte(prefixHash), [32]byte(keyImage), pubs, sigs) {
|
||||
return fmt.Errorf("consensus: ring signature verification failed for input %d", sigIdx)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"forge.lthn.ai/core/go-blockchain/config"
|
||||
"forge.lthn.ai/core/go-blockchain/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -23,3 +24,72 @@ func TestVerifyTransactionSignatures_Bad_MissingSigs(t *testing.T) {
|
|||
err := VerifyTransactionSignatures(tx, config.MainnetForks, 100, nil, nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- HTLC signature verification tests (Task 9) ---
|
||||
|
||||
func TestVerifyV1Signatures_MixedHTLC_Good(t *testing.T) {
|
||||
// Structural check only (getRingOutputs = nil).
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPreHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputToKey{Amount: 100, KeyImage: types.KeyImage{1}},
|
||||
types.TxInputHTLC{Amount: 50, KeyImage: types.KeyImage{2}},
|
||||
},
|
||||
Signatures: [][]types.Signature{
|
||||
{{1}}, // sig for TxInputToKey
|
||||
{{2}}, // sig for TxInputHTLC
|
||||
},
|
||||
}
|
||||
err := VerifyTransactionSignatures(tx, config.MainnetForks, 20000, nil, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyV1Signatures_MixedHTLC_Bad(t *testing.T) {
|
||||
// Wrong signature count.
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPreHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputToKey{Amount: 100, KeyImage: types.KeyImage{1}},
|
||||
types.TxInputHTLC{Amount: 50, KeyImage: types.KeyImage{2}},
|
||||
},
|
||||
Signatures: [][]types.Signature{
|
||||
{{1}}, // only 1 sig for 2 ring inputs
|
||||
},
|
||||
}
|
||||
err := VerifyTransactionSignatures(tx, config.MainnetForks, 20000, nil, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "signature count")
|
||||
}
|
||||
|
||||
func TestVerifyV1Signatures_HTLCOnly_Good(t *testing.T) {
|
||||
// Transaction with only HTLC inputs.
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPreHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputHTLC{Amount: 50, KeyImage: types.KeyImage{1}},
|
||||
types.TxInputHTLC{Amount: 30, KeyImage: types.KeyImage{2}},
|
||||
},
|
||||
Signatures: [][]types.Signature{
|
||||
{{1}},
|
||||
{{2}},
|
||||
},
|
||||
}
|
||||
err := VerifyTransactionSignatures(tx, config.MainnetForks, 20000, nil, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyV1Signatures_MultisigSkipped_Good(t *testing.T) {
|
||||
// Multisig inputs do not participate in NLSAG signatures.
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPreHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputToKey{Amount: 100, KeyImage: types.KeyImage{1}},
|
||||
types.TxInputMultisig{Amount: 50},
|
||||
},
|
||||
Signatures: [][]types.Signature{
|
||||
{{1}}, // only 1 sig, multisig is not counted
|
||||
},
|
||||
}
|
||||
err := VerifyTransactionSignatures(tx, config.MainnetForks, 20000, nil, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue