mirror of
https://github.com/lightningnetwork/lnd.git
synced 2026-08-13 12:32:48 +02:00
Add regression tests that compare every ScriptTemplate-based function against the original ScriptBuilder implementations extracted from git history. This ensures the template migration produces identical script bytes for all 22 script types (segwit v0 + taproot). The legacy builder functions are kept as private test helpers in script_utils_legacy_test.go, extracted verbatim from the pre-template commit.
454 lines
10 KiB
Go
454 lines
10 KiB
Go
package input
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// testKeyBytes returns deterministic key bytes for testing. The index parameter
|
|
// produces different keys for different roles by deriving private keys from a
|
|
// hash and computing the corresponding public key on secp256k1.
|
|
func testKeyBytes(t *testing.T, index byte) *btcec.PublicKey {
|
|
t.Helper()
|
|
|
|
hash := sha256.Sum256([]byte{index})
|
|
privKey, _ := btcec.PrivKeyFromBytes(hash[:])
|
|
|
|
return privKey.PubKey()
|
|
}
|
|
|
|
// testPaymentHash returns a deterministic 32-byte payment hash.
|
|
func testPaymentHash() []byte {
|
|
h := sha256.Sum256([]byte("test-payment-preimage"))
|
|
return h[:]
|
|
}
|
|
|
|
// TestTemplateVsBuilderEquivalence verifies that the new ScriptTemplate-based
|
|
// functions produce byte-for-byte identical output to the old ScriptBuilder
|
|
// versions for all script types.
|
|
func TestTemplateVsBuilderEquivalence(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Set up test keys for various roles.
|
|
senderKey := testKeyBytes(t, 1)
|
|
receiverKey := testKeyBytes(t, 2)
|
|
revokeKey := testKeyBytes(t, 3)
|
|
selfKey := testKeyBytes(t, 4)
|
|
delayKey := testKeyBytes(t, 5)
|
|
remoteKey := testKeyBytes(t, 6)
|
|
|
|
payHash := testPaymentHash()
|
|
|
|
const (
|
|
csvDelay uint32 = 144
|
|
cltvExpiry uint32 = 800000
|
|
leaseExpiry uint32 = 900000
|
|
)
|
|
|
|
t.Run("WitnessScriptHash", func(t *testing.T) {
|
|
t.Parallel()
|
|
witnessScript := []byte("test-witness-script")
|
|
|
|
got, err := WitnessScriptHash(witnessScript)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyWitnessScriptHash(witnessScript)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got,
|
|
"WitnessScriptHash mismatch:\n"+
|
|
" legacy: %x\n template: %x",
|
|
want, got,
|
|
)
|
|
})
|
|
|
|
t.Run("WitnessPubKeyHash", func(t *testing.T) {
|
|
t.Parallel()
|
|
pubkey := senderKey.SerializeCompressed()
|
|
|
|
got, err := WitnessPubKeyHash(pubkey)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyWitnessPubKeyHash(pubkey)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("GenerateP2SH", func(t *testing.T) {
|
|
t.Parallel()
|
|
script := []byte("test-redeem-script")
|
|
|
|
got, err := GenerateP2SH(script)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyGenerateP2SH(script)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("GenerateP2PKH", func(t *testing.T) {
|
|
t.Parallel()
|
|
pubkey := senderKey.SerializeCompressed()
|
|
|
|
got, err := GenerateP2PKH(pubkey)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyGenerateP2PKH(pubkey)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("GenMultiSigScript", func(t *testing.T) {
|
|
t.Parallel()
|
|
aPub := senderKey.SerializeCompressed()
|
|
bPub := receiverKey.SerializeCompressed()
|
|
|
|
got, err := GenMultiSigScript(aPub, bPub)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyGenMultiSigScript(aPub, bPub)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("SenderHTLCScript/confirmed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := SenderHTLCScript(
|
|
senderKey, receiverKey, revokeKey, payHash, true,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacySenderHTLCScript(
|
|
senderKey, receiverKey, revokeKey, payHash, true,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got,
|
|
"SenderHTLCScript(confirmed) mismatch:\n"+
|
|
" legacy: %x\n template: %x",
|
|
want, got,
|
|
)
|
|
})
|
|
|
|
t.Run("SenderHTLCScript/unconfirmed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := SenderHTLCScript(
|
|
senderKey, receiverKey, revokeKey, payHash, false,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacySenderHTLCScript(
|
|
senderKey, receiverKey, revokeKey, payHash, false,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("ReceiverHTLCScript/confirmed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := ReceiverHTLCScript(
|
|
cltvExpiry, senderKey, receiverKey, revokeKey,
|
|
payHash, true,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyReceiverHTLCScript(
|
|
cltvExpiry, senderKey, receiverKey, revokeKey,
|
|
payHash, true,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got,
|
|
"ReceiverHTLCScript(confirmed) mismatch:\n"+
|
|
" legacy: %x\n template: %x",
|
|
want, got,
|
|
)
|
|
})
|
|
|
|
t.Run("ReceiverHTLCScript/unconfirmed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := ReceiverHTLCScript(
|
|
cltvExpiry, senderKey, receiverKey, revokeKey,
|
|
payHash, false,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyReceiverHTLCScript(
|
|
cltvExpiry, senderKey, receiverKey, revokeKey,
|
|
payHash, false,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("SecondLevelHtlcScript", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := SecondLevelHtlcScript(
|
|
revokeKey, delayKey, csvDelay,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacySecondLevelHtlcScript(
|
|
revokeKey, delayKey, csvDelay,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("CommitScriptToSelf", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := CommitScriptToSelf(csvDelay, selfKey, revokeKey)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyCommitScriptToSelf(
|
|
csvDelay, selfKey, revokeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("LeaseCommitScriptToSelf", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := LeaseCommitScriptToSelf(
|
|
selfKey, revokeKey, csvDelay, leaseExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyLeaseCommitScriptToSelf(
|
|
selfKey, revokeKey, csvDelay, leaseExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("CommitScriptUnencumbered", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := CommitScriptUnencumbered(remoteKey)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyCommitScriptUnencumbered(remoteKey)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("CommitScriptToRemoteConfirmed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := CommitScriptToRemoteConfirmed(remoteKey)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyCommitScriptToRemoteConfirmed(remoteKey)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("LeaseCommitScriptToRemoteConfirmed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := LeaseCommitScriptToRemoteConfirmed(
|
|
remoteKey, leaseExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyLeaseCommitScriptToRemoteConfirmed(
|
|
remoteKey, leaseExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("CommitScriptAnchor", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := CommitScriptAnchor(senderKey)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyCommitScriptAnchor(senderKey)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
t.Run("LeaseSecondLevelHtlcScript", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := LeaseSecondLevelHtlcScript(
|
|
revokeKey, delayKey, csvDelay, cltvExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyLeaseSecondLevelHtlcScript(
|
|
revokeKey, delayKey, csvDelay, cltvExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
// Taproot script equivalence tests. These compare the non-prod
|
|
// (default) variant of the template functions against the old builder
|
|
// code which also produced the non-prod scripts.
|
|
t.Run("SenderHTLCTapLeafTimeout", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := SenderHTLCTapLeafTimeout(senderKey, receiverKey)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacySenderHTLCTapLeafTimeout(
|
|
senderKey, receiverKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want.Script, got.Script)
|
|
})
|
|
|
|
t.Run("SenderHTLCTapLeafSuccess", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := SenderHTLCTapLeafSuccess(receiverKey, payHash)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacySenderHTLCTapLeafSuccess(
|
|
receiverKey, payHash,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want.Script, got.Script)
|
|
})
|
|
|
|
t.Run("ReceiverHtlcTapLeafTimeout", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := ReceiverHtlcTapLeafTimeout(
|
|
senderKey, cltvExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyReceiverHtlcTapLeafTimeout(
|
|
senderKey, cltvExpiry,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want.Script, got.Script)
|
|
})
|
|
|
|
t.Run("ReceiverHtlcTapLeafSuccess", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := ReceiverHtlcTapLeafSuccess(
|
|
receiverKey, senderKey, payHash,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyReceiverHtlcTapLeafSuccess(
|
|
receiverKey, senderKey, payHash,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want.Script, got.Script)
|
|
})
|
|
|
|
t.Run("TaprootSecondLevelTapLeaf", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := TaprootSecondLevelTapLeaf(delayKey, csvDelay)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyTaprootSecondLevelTapLeaf(
|
|
delayKey, csvDelay,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want.Script, got.Script)
|
|
})
|
|
|
|
t.Run("TaprootLocalCommitDelayScript", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := TaprootLocalCommitDelayScript(
|
|
csvDelay, selfKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyTaprootLocalCommitDelayScript(
|
|
csvDelay, selfKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got,
|
|
"TaprootLocalCommitDelayScript mismatch:\n"+
|
|
" legacy: %x\n template: %x",
|
|
want, got,
|
|
)
|
|
})
|
|
|
|
t.Run("TaprootLocalCommitRevokeScript", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := TaprootLocalCommitRevokeScript(
|
|
selfKey, revokeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
want, err := legacyTaprootLocalCommitRevokeScript(
|
|
selfKey, revokeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, want, got)
|
|
})
|
|
|
|
// Log a summary of all scripts tested for visual inspection.
|
|
t.Log("All 22 template vs builder script equivalence checks passed")
|
|
}
|
|
|
|
// TestTemplateScriptDisassembly provides human-readable output of a few key
|
|
// scripts to make it easy to verify correctness visually.
|
|
func TestTemplateScriptDisassembly(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
senderKey := testKeyBytes(t, 1)
|
|
receiverKey := testKeyBytes(t, 2)
|
|
revokeKey := testKeyBytes(t, 3)
|
|
payHash := testPaymentHash()
|
|
|
|
// SenderHTLCScript with confirmed spend.
|
|
script, err := SenderHTLCScript(
|
|
senderKey, receiverKey, revokeKey, payHash, true,
|
|
)
|
|
require.NoError(t, err)
|
|
t.Logf("SenderHTLCScript (confirmed):\n %s",
|
|
hex.EncodeToString(script))
|
|
|
|
// ReceiverHTLCScript with confirmed spend.
|
|
script, err = ReceiverHTLCScript(
|
|
800000, senderKey, receiverKey, revokeKey, payHash, true,
|
|
)
|
|
require.NoError(t, err)
|
|
t.Logf("ReceiverHTLCScript (confirmed):\n %s",
|
|
hex.EncodeToString(script))
|
|
}
|