Skip to content

Commit

Permalink
Merge pull request #326 from lightninglabs/bip-test-vectors
Browse files Browse the repository at this point in the history
multi: add test vectors for asset/address/proof/PSBT TLV encoding and VM/MS-SMT validation
  • Loading branch information
guggero authored Jul 25, 2023
2 parents e02f22f + c05cefa commit 6e7bd85
Show file tree
Hide file tree
Showing 41 changed files with 93,895 additions and 186 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ jobs:
- name: Run go mod tidy
run: make mod-check

test-vector-check:
name: test vector check
runs-on: ubuntu-latest
steps:
- name: git checkout
uses: actions/checkout@v3

- name: Setup go environment
uses: ./.github/actions/setup-go

- name: Run test vector creation check
run: make test-vector-check

########################
# Compilation check.
########################
Expand Down
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ mod-check: mod-tidy
@$(call print, "Checking modules.")
if test -n "$$(git status | grep -e "go.mod\|go.sum")"; then echo "Running go mod tidy changes go.mod/go.sum"; git status; git diff; exit 1; fi

gen-test-vectors:
@$(call print, "Generating test vectors.")
make unit gen-test-vectors=true pkg=address case=TestAddressEncoding
make unit gen-test-vectors=true pkg=asset case=TestAssetEncoding
make unit gen-test-vectors=true pkg=mssmt case=TestProofEncoding
make unit gen-test-vectors=true pkg=mssmt case=TestInsertionOverflow
make unit gen-test-vectors=true pkg=mssmt case=TestReplaceWithEmptyBranch
make unit gen-test-vectors=true pkg=mssmt case=TestReplace
make unit gen-test-vectors=true pkg=proof case=TestGenesisProofVerification
make unit gen-test-vectors=true pkg=tappsbt case=TestEncodingDecoding
make unit gen-test-vectors=true pkg=vm case=TestVM

test-vector-check: gen-test-vectors
@$(call print, "Checking test vectors.")
if test -n "$$(git status | grep -e ".json")"; then echo "Test vectors not updated"; git status; git diff; exit 1; fi

clean:
@$(call print, "Cleaning source.$(NC)")
$(RM) coverage.txt
Expand Down
155 changes: 142 additions & 13 deletions address/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package address

import (
"encoding/hex"
"math/rand"
"testing"

"github.com/btcsuite/btcd/btcec/v2"
Expand All @@ -11,6 +10,7 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/internal/test"
"github.com/stretchr/testify/require"
)

Expand All @@ -22,6 +22,13 @@ var (
"078f",
)
pubKey, _ = schnorr.ParsePubKey(pubKeyBytes)

generatedTestVectorName = "address_tlv_encoding_generated.json"

allTestVectorFiles = []string{
generatedTestVectorName,
"address_tlv_encoding_error_cases.json",
}
)

func randAddress(t *testing.T, net *ChainParams, groupPubKey, sibling bool,
Expand All @@ -35,7 +42,7 @@ func randAddress(t *testing.T, net *ChainParams, groupPubKey, sibling bool,
}

if amt == nil && assetType == asset.Normal {
amount = rand.Uint64()
amount = test.RandInt[uint64]()
}

var tapscriptSibling *commitment.TapscriptPreimage
Expand Down Expand Up @@ -75,7 +82,9 @@ func randEncodedAddress(t *testing.T, net *ChainParams, groupPubKey,
newAddr, err := randAddress(
t, net, groupPubKey, sibling, nil, assetType,
)
require.NoError(t, err)
if err != nil {
return nil, "", err
}

encodedAddr, err := newAddr.EncodeAddress()

Expand All @@ -95,8 +104,6 @@ func assertAddressEqual(t *testing.T, a, b *Tap) {

// TestNewAddress tests edge cases around creating a new address.
func TestNewAddress(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
f func() (*Tap, error)
Expand Down Expand Up @@ -167,9 +174,9 @@ func TestNewAddress(t *testing.T) {
}

for _, testCase := range testCases {
success := t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
testCase := testCase

success := t.Run(testCase.name, func(t *testing.T) {
address, err := testCase.f()
require.Equal(t, testCase.err, err)

Expand All @@ -188,7 +195,8 @@ func TestNewAddress(t *testing.T) {
func TestAddressEncoding(t *testing.T) {
t.Parallel()

assetAddressEncoding := func(a *Tap) {
testVectors := &TestVectors{}
assertAddressEncoding := func(comment string, a *Tap) {
t.Helper()

assertAddressEqual(t, a, a.Copy())
Expand All @@ -199,6 +207,14 @@ func TestAddressEncoding(t *testing.T) {
b, err := DecodeAddress(addr, net)
require.NoError(t, err)
assertAddressEqual(t, a, b)

testVectors.ValidTestCases = append(
testVectors.ValidTestCases, &ValidTestCase{
Address: NewTestFromAddress(t, a),
Expected: addr,
Comment: comment,
},
)
}

testCases := []struct {
Expand All @@ -207,7 +223,7 @@ func TestAddressEncoding(t *testing.T) {
err error
}{
{
name: "valid address",
name: "valid regtest address",
f: func() (*Tap, string, error) {
return randEncodedAddress(
t, &RegressionNetTap, false, false,
Expand All @@ -217,7 +233,37 @@ func TestAddressEncoding(t *testing.T) {
err: nil,
},
{
name: "group collectible",
name: "valid simnet address",
f: func() (*Tap, string, error) {
return randEncodedAddress(
t, &SimNetTap, false, false,
asset.Normal,
)
},
err: nil,
},
{
name: "valid testnet address",
f: func() (*Tap, string, error) {
return randEncodedAddress(
t, &TestNet3Tap, false, false,
asset.Normal,
)
},
err: nil,
},
{
name: "valid mainnet address",
f: func() (*Tap, string, error) {
return randEncodedAddress(
t, &MainNetTap, false, false,
asset.Normal,
)
},
err: nil,
},
{
name: "signet group collectible",
f: func() (*Tap, string, error) {
return randEncodedAddress(
t, &SigNetTap, true, false,
Expand Down Expand Up @@ -288,17 +334,100 @@ func TestAddressEncoding(t *testing.T) {
}

for _, testCase := range testCases {
success := t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
testCase := testCase

success := t.Run(testCase.name, func(t *testing.T) {
addr, _, err := testCase.f()
require.Equal(t, testCase.err, err)
if testCase.err == nil {
assetAddressEncoding(addr)
assertAddressEncoding(testCase.name, addr)
}
})
if !success {
return
}
}

// Write test vectors to file. This is a no-op if the "gen_test_vectors"
// build tag is not set.
test.WriteTestVectors(t, generatedTestVectorName, testVectors)
}

// TestBIPTestVectors tests that the BIP test vectors are passing.
func TestBIPTestVectors(t *testing.T) {
t.Parallel()

for idx := range allTestVectorFiles {
var (
fileName = allTestVectorFiles[idx]
testVectors = &TestVectors{}
)
test.ParseTestVectors(t, fileName, &testVectors)
t.Run(fileName, func(tt *testing.T) {
tt.Parallel()

runBIPTestVector(tt, testVectors)
})
}
}

// runBIPTestVector runs the tests in a single BIP test vector file.
func runBIPTestVector(t *testing.T, testVectors *TestVectors) {
for _, validCase := range testVectors.ValidTestCases {
validCase := validCase

t.Run(validCase.Comment, func(tt *testing.T) {
tt.Parallel()

a := validCase.Address.ToAddress(tt)

addrString, err := a.EncodeAddress()
require.NoError(tt, err)

areEqual := validCase.Expected == addrString

// Create nice diff if things don't match.
if !areEqual {
chainParams, err := a.Net()
require.NoError(tt, err)

expectedAddress, err := DecodeAddress(
validCase.Expected, chainParams,
)
require.NoError(tt, err)

require.Equal(tt, a, expectedAddress)

// Make sure we still fail the test.
require.Equal(
tt, validCase.Expected,
addrString,
)
}

// We also want to make sure that the address is decoded
// correctly from the encoded TLV stream.
chainParams, err := a.Net()
require.NoError(tt, err)

decoded, err := DecodeAddress(
validCase.Expected, chainParams,
)
require.NoError(tt, err)

require.Equal(tt, a, decoded)
})
}

for _, invalidCase := range testVectors.ErrorTestCases {
invalidCase := invalidCase

t.Run(invalidCase.Comment, func(tt *testing.T) {
tt.Parallel()

require.PanicsWithValue(tt, invalidCase.Error, func() {
invalidCase.Address.ToAddress(tt)
})
})
}
}
Loading

0 comments on commit 6e7bd85

Please sign in to comment.