From cf1a0c51856af48991593e601e1312a6ee375088 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 5 Nov 2024 11:47:00 -0500 Subject: [PATCH 1/2] ACP-77: Implement ids.ID#Append --- ids/id.go | 16 ++++++++++ ids/id_test.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/ids/id.go b/ids/id.go index 91eacfdbd08f..4be8e7ecb1e8 100644 --- a/ids/id.go +++ b/ids/id.go @@ -107,6 +107,22 @@ func (id ID) Prefix(prefixes ...uint64) ID { return hashing.ComputeHash256Array(packer.Bytes) } +// Append this id to create a more selective id. +// +// This is used to generate the ACP-77 validationIDs. +func (id ID) Append(suffixes ...uint32) ID { + packer := wrappers.Packer{ + Bytes: make([]byte, IDLen+len(suffixes)*wrappers.IntLen), + } + + packer.PackFixedBytes(id[:]) + for _, suffix := range suffixes { + packer.PackInt(suffix) + } + + return hashing.ComputeHash256Array(packer.Bytes) +} + // XOR this id and the provided id and return the resulting id. // // Note: this id is not modified. diff --git a/ids/id_test.go b/ids/id_test.go index 930a323e614c..b062aaa0b0c2 100644 --- a/ids/id_test.go +++ b/ids/id_test.go @@ -6,12 +6,14 @@ package ids import ( "encoding/json" "fmt" + "slices" "testing" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/cb58" + "github.com/ava-labs/avalanchego/utils/hashing" ) func TestID(t *testing.T) { @@ -25,6 +27,90 @@ func TestID(t *testing.T) { require.Equal(prefixed, id.Prefix(0)) } +func TestIDPrefix(t *testing.T) { + id := GenerateTestID() + tests := []struct { + name string + id ID + prefix []uint64 + expectedPreimage []byte + }{ + { + name: "empty prefix", + id: id, + prefix: []uint64{}, + expectedPreimage: id[:], + }, + { + name: "1 prefix", + id: id, + prefix: []uint64{1}, + expectedPreimage: slices.Concat( + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + id[:], + ), + }, + { + name: "multiple prefixes", + id: id, + prefix: []uint64{1, 256}, + expectedPreimage: slices.Concat( + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}, + id[:], + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + expected := ID(hashing.ComputeHash256Array(test.expectedPreimage)) + require.Equal(t, expected, test.id.Prefix(test.prefix...)) + }) + } +} + +func TestIDAppend(t *testing.T) { + id := GenerateTestID() + tests := []struct { + name string + id ID + suffix []uint32 + expectedPreimage []byte + }{ + { + name: "empty suffix", + id: id, + suffix: []uint32{}, + expectedPreimage: id[:], + }, + { + name: "1 suffix", + id: id, + suffix: []uint32{1}, + expectedPreimage: slices.Concat( + id[:], + []byte{0x00, 0x00, 0x00, 0x01}, + ), + }, + { + name: "multiple suffixes", + id: id, + suffix: []uint32{1, 256}, + expectedPreimage: slices.Concat( + id[:], + []byte{0x00, 0x00, 0x00, 0x01}, + []byte{0x00, 0x00, 0x01, 0x00}, + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + expected := ID(hashing.ComputeHash256Array(test.expectedPreimage)) + require.Equal(t, expected, test.id.Append(test.suffix...)) + }) + } +} + func TestIDXOR(t *testing.T) { require := require.New(t) From 1b358781d605d81c45ff8abca509e8f9f0fc3a6e Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 5 Nov 2024 12:01:33 -0500 Subject: [PATCH 2/2] update doc --- ids/id.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ids/id.go b/ids/id.go index 4be8e7ecb1e8..ea0993ef25d7 100644 --- a/ids/id.go +++ b/ids/id.go @@ -107,9 +107,12 @@ func (id ID) Prefix(prefixes ...uint64) ID { return hashing.ComputeHash256Array(packer.Bytes) } -// Append this id to create a more selective id. +// Append this id with the provided suffixes and re-hash the result. This +// returns a new ID and does not modify the original ID. // -// This is used to generate the ACP-77 validationIDs. +// This is used to generate ACP-77 validationIDs. +// +// Ref: https://github.com/avalanche-foundation/ACPs/tree/e333b335c34c8692d84259d21bd07b2bb849dc2c/ACPs/77-reinventing-subnets#convertsubnettol1tx func (id ID) Append(suffixes ...uint32) ID { packer := wrappers.Packer{ Bytes: make([]byte, IDLen+len(suffixes)*wrappers.IntLen),