diff --git a/chain/avalanche/utils/chain_id.go b/chain/avalanche/lib/chain_id.go similarity index 97% rename from chain/avalanche/utils/chain_id.go rename to chain/avalanche/lib/chain_id.go index 9d6475736..cfb17dd50 100644 --- a/chain/avalanche/utils/chain_id.go +++ b/chain/avalanche/lib/chain_id.go @@ -1,4 +1,4 @@ -package utils +package lib import ( "errors" diff --git a/chain/avalanche/lib/network.go b/chain/avalanche/lib/network.go new file mode 100644 index 000000000..b0e105cae --- /dev/null +++ b/chain/avalanche/lib/network.go @@ -0,0 +1,35 @@ +package lib + +import ( + "context" + "fmt" + "net" + "time" +) + +func IsOpened(host string, port string) bool { + conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), time.Second) + if err != nil { + return false + } + + if conn != nil { + conn.Close() + return true + } + + return false +} + +func WaitPort(ctx context.Context, host, port string) error { + var err error + for done := false; !done && err == nil; { + select { + case <-ctx.Done(): + err = fmt.Errorf("WaitPort(%s, %s) context closed", host, port) + default: + done = IsOpened(host, port) + } + } + return err +} diff --git a/chain/avalanche/node.go b/chain/avalanche/node.go index c75ae8271..433cfcda6 100644 --- a/chain/avalanche/node.go +++ b/chain/avalanche/node.go @@ -5,25 +5,37 @@ import ( "encoding/json" "errors" "fmt" + "io" "path/filepath" "strings" - + "time" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/volume" dockerclient "github.com/docker/docker/client" "github.com/docker/go-connections/nat" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/crypto/secp256k1" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" + "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/lib" "github.com/strangelove-ventures/interchaintest/v7/ibc" "github.com/strangelove-ventures/interchaintest/v7/internal/dockerutil" "go.uber.org/zap" ) var ( - RPCPort = "9650/tcp" - StakingPort = "9651/tcp" + RPCPort = "9650/tcp" + StakingPort = "9651/tcp" + portBindings = nat.PortSet{ + nat.Port(RPCPort): {}, + nat.Port(StakingPort): {}, + } ) type ( @@ -52,8 +64,13 @@ type ( } AvalancheNodeSubnetOpts struct { - Name string - VM []byte + Name string + VmID ids.ID + VM []byte + Genesis []byte + + subnet ids.ID + chain ids.ID } AvalancheNodeOpts struct { @@ -61,7 +78,7 @@ type ( Subnets []AvalancheNodeSubnetOpts Bootstrap []*AvalancheNode Credentials AvalancheNodeCredentials - ChainID utils.ChainID + ChainID lib.ChainID } ) @@ -142,6 +159,15 @@ func NewAvalancheNode( return nil, err } + vmaliases := make(map[ids.ID][]string) + for i := range node.options.Subnets { + vmaliases[node.options.Subnets[i].VmID] = []string{node.options.Subnets[i].Name} + } + vmaliasesData, err := json.MarshalIndent(vmaliases, "", " ") + if err != nil { + return nil, err + } + if err := node.WriteFile(ctx, genesisBz, "genesis.json"); err != nil { return nil, fmt.Errorf("failed to write genesis file: %w", err) } @@ -154,6 +180,16 @@ func NewAvalancheNode( return nil, fmt.Errorf("failed to write TLS key: %w", err) } + if err := node.WriteFile(ctx, vmaliasesData, "configs/vms/aliases.json"); err != nil { + return nil, fmt.Errorf("failed to write TLS key: %w", err) + } + + for _, subnet := range node.options.Subnets { + if err := node.WriteFile(ctx, subnet.VM, fmt.Sprintf("plugins/%s", subnet.VmID)); err != nil { + return nil, fmt.Errorf("failed to write vm body [%s]: %w", subnet.Name, err) + } + } + return node, node.CreateContainer(ctx) } @@ -270,7 +306,7 @@ func (n *AvalancheNode) SendIBCTransfer(ctx context.Context, channelID, keyName } func (n *AvalancheNode) Height(ctx context.Context) (uint64, error) { - panic("ToDo: implement me") + return platformvm.NewClient(fmt.Sprintf("http://127.0.0.1:%s", n.RPCPort())).GetHeight(ctx) } func (n *AvalancheNode) GetBalance(ctx context.Context, address string, denom string) (int64, error) { @@ -334,18 +370,12 @@ func (n *AvalancheNode) CreateContainer(ctx context.Context) error { "--bootstrap-ids", bootstrapIds, ) } - port1, _ := nat.NewPort("tcp", "9650") - port2, _ := nat.NewPort("tcp", "9651") - ports := nat.PortSet{ - port1: {}, - port2: {}, - } return n.containerLifecycle.CreateContainerInNetwork( ctx, n.testName, n.networkID, n.image, - ports, + portBindings, n.Bind(), &network.NetworkingConfig{ EndpointsConfig: map[string](*network.EndpointSettings){ @@ -363,3 +393,142 @@ func (n *AvalancheNode) CreateContainer(ctx context.Context) error { func (n *AvalancheNode) StartContainer(ctx context.Context, testName string, additionalGenesisWallets []ibc.WalletAmount) error { return n.containerLifecycle.StartContainer(ctx) } + +func (n *AvalancheNode) StartSubnets(ctx context.Context) error { + if len(n.options.Subnets) == 0 { + return nil + } + + kc := secp256k1fx.NewKeychain(n.options.Credentials.PK) + ownerAddr := n.options.Credentials.PK.Address() + + wallet, err := primary.NewWalletFromURI(ctx, fmt.Sprintf("http://127.0.0.1:%s", n.RPCPort()), kc) + if err != nil { + return err + } + + // Get the P-chain and the X-chain wallets + pWallet := wallet.P() + xWallet := wallet.X() + + // Pull out useful constants to use when issuing transactions. + xChainID := xWallet.BlockchainID() + owner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ownerAddr}, + } + + // Send AVAX to the P-chain. + exportStartTime := time.Now() + exportTxID, err := xWallet.IssueExportTx( + constants.PlatformChainID, + []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: xWallet.AVAXAssetID(), + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 2 * uint64(len(n.options.Subnets)+1) * pWallet.CreateSubnetTxFee(), + OutputOwners: *owner, + }, + }, + }, + ) + if err != nil { + n.logger.Error( + "failed to issue X->P export transaction", + zap.Error(err), + ) + return err + } + n.logger.Info( + "issued X->P export", + zap.String("exportTxID", exportTxID.String()), + zap.Float64("duration", time.Since(exportStartTime).Seconds()), + ) + + // Import AVAX from the X-chain into the P-chain. + importStartTime := time.Now() + importTxID, err := pWallet.IssueImportTx(xChainID, owner) + if err != nil { + n.logger.Error( + "failed to issue X->P import transaction", + zap.Error(err), + ) + return err + } + n.logger.Info( + "issued X->P import", + zap.String("importTxID", importTxID.String()), + zap.Float64("duration", time.Since(importStartTime).Seconds()), + ) + + for i, subnet := range n.options.Subnets { + createSubnetStartTime := time.Now() + createSubnetTxID, err := pWallet.IssueCreateSubnetTx(owner) + if err != nil { + n.logger.Error( + "failed to issue create subnet transaction", + zap.Error(err), + zap.String("name", subnet.Name), + ) + return err + } + n.logger.Info( + "issued create subnet transaction", + zap.String("name", subnet.Name), + zap.String("createSubnetTxID", createSubnetTxID.String()), + zap.Float64("duration", time.Since(createSubnetStartTime).Seconds()), + ) + + createChainStartTime := time.Now() + createChainTxID, err := pWallet.IssueCreateChainTx(createSubnetTxID, subnet.Genesis, subnet.VmID, nil, subnet.Name) + if err != nil { + n.logger.Error( + "failed to issue create chain transaction", + zap.Error(err), + zap.String("name", subnet.Name), + ) + return err + } + n.logger.Info( + "created new chain", + zap.String("name", subnet.Name), + zap.String("createChainTxID", createChainTxID.String()), + zap.Float64("duration", time.Since(createChainStartTime).Seconds()), + ) + + n.options.Subnets[i].subnet = createSubnetTxID + n.options.Subnets[i].chain = createChainTxID + } + + return nil +} + +func (n *AvalancheNode) Start(ctx context.Context, testName string, additionalGenesisWallets []ibc.WalletAmount) error { + err := n.StartContainer(ctx, testName, additionalGenesisWallets) + if err != nil { + return err + } + + err = lib.WaitPort(ctx, "127.0.0.1", n.RPCPort()) + if err != nil { + return err + } + + infoClient := info.NewClient(fmt.Sprintf("http://127.0.0.1:%s", n.RPCPort())) + for done := false; !done && err == nil; { + select { + case <-ctx.Done(): + return fmt.Errorf("context closed") + default: + done, err = infoClient.IsBootstrapped(ctx, "X") + if errors.Is(err, io.EOF) { + err = nil + } + } + time.Sleep(500 * time.Millisecond) + } + + return err +} diff --git a/chain/avalanche/package.go b/chain/avalanche/package.go index 9c25374d7..592466d7f 100644 --- a/chain/avalanche/package.go +++ b/chain/avalanche/package.go @@ -4,21 +4,26 @@ import ( "context" "fmt" "io" - "math" "net" "os" + "time" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/staking" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/docker/docker/api/types" "github.com/docker/docker/client" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/crypto/secp256k1" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" + "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/lib" "github.com/strangelove-ventures/interchaintest/v7/ibc" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) -var _ ibc.Chain = &AvalancheChain{} +var ( + _ ibc.Chain = &AvalancheChain{} + ChainBootsrapTimeout = 5 * time.Minute +) type AvalancheChain struct { log *zap.Logger @@ -78,7 +83,7 @@ func (c *AvalancheChain) Initialize(ctx context.Context, testName string, cli *c if rawChainID == "" { rawChainID = "localnet-123456" } - chainId, err := utils.ParseChainID(rawChainID) + chainId, err := lib.ParseChainID(rawChainID) if err != nil { c.log.Error("Failed to pull image", zap.Error(err), @@ -92,6 +97,14 @@ func (c *AvalancheChain) Initialize(ctx context.Context, testName string, cli *c subnetOpts = make([]AvalancheNodeSubnetOpts, len(c.cfg.AvalancheSubnets)) for i := range c.cfg.AvalancheSubnets { subnetOpts[i].Name = c.cfg.AvalancheSubnets[i].Name + subnetOpts[i].Genesis = c.cfg.AvalancheSubnets[i].Genesis + vmName := make([]byte, 32) + copy(vmName[:], []byte(c.cfg.AvalancheSubnets[i].Name)) + subnetOpts[i].VmID, err = ids.ToID(vmName) + if err != nil { + return err + } + if len(c.cfg.AvalancheSubnets[i].VMFile) > 0 { file, err := os.Open(c.cfg.AvalancheSubnets[i].VMFile) if err != nil { @@ -107,21 +120,21 @@ func (c *AvalancheChain) Initialize(ctx context.Context, testName string, cli *c } } + keyFactory := secp256k1.Factory{} + key, err := keyFactory.NewPrivateKey() + if err != nil { + return err + } + numNodes := c.numValidators + c.numFullNodes credentials := make([]AvalancheNodeCredentials, numNodes) - keyFactory := secp256k1.Factory{} for i := 0; i < numNodes; i++ { - key, err := keyFactory.NewPrivateKey() + rawTlsCert, rawTlsKey, err := staking.NewCertAndKeyBytes() if err != nil { return err } - rawTlsCert, rawTlsKey, err := utils.NewCertAndKeyBytes() - if err != nil { - return err - } - - cert, err := utils.NewTLSCertFromBytes(rawTlsCert, rawTlsKey) + cert, err := staking.LoadTLSCertFromBytes(rawTlsKey, rawTlsCert) if err != nil { return err } @@ -132,28 +145,56 @@ func (c *AvalancheChain) Initialize(ctx context.Context, testName string, cli *c credentials[i].TLSKey = rawTlsKey } - allocations := make([]GenesisAllocation, 0, c.numValidators) + avaxAddr, _ := address.Format("X", chainId.Name, key.Address().Bytes()) + allocations := []GenesisAllocation{ + { + ETHAddr: "0x" + key.PublicKey().Address().Hex(), + AVAXAddr: avaxAddr, + InitialAmount: 4000000000, + UnlockSchedule: []GenesisLockedAmount{{Amount: 2000000000}, {Amount: 1000000000}}, + }, + { + ETHAddr: "0x" + key.PublicKey().Address().Hex(), + AVAXAddr: avaxAddr, + InitialAmount: 4000000000, + UnlockSchedule: []GenesisLockedAmount{{Amount: 2000000000}, {Amount: 1000000000}}, + }, + { + ETHAddr: "0x" + key.PublicKey().Address().Hex(), + AVAXAddr: avaxAddr, + InitialAmount: 4000000000, + UnlockSchedule: []GenesisLockedAmount{}, + }, + { + ETHAddr: "0x" + key.PublicKey().Address().Hex(), + AVAXAddr: avaxAddr, + InitialAmount: 4000000000, + UnlockSchedule: []GenesisLockedAmount{}, + }, + { + ETHAddr: "0x" + key.PublicKey().Address().Hex(), + AVAXAddr: avaxAddr, + InitialAmount: 4000000000, + UnlockSchedule: []GenesisLockedAmount{{Amount: 4000000000, Locktime: uint32(time.Second)}}, + }, + { + ETHAddr: "0x" + key.PublicKey().Address().Hex(), + AVAXAddr: avaxAddr, + InitialAmount: 4000000000, + UnlockSchedule: []GenesisLockedAmount{{Amount: 4000000000, Locktime: uint32(time.Second)}}, + }, + } stakedFunds := make([]string, 0, c.numValidators) stakes := make([]GenesisStaker, 0, c.numValidators) for i := 0; i < c.numValidators; i++ { - avaxAddr0, _ := utils.Format("X", chainId.Name, credentials[0].PK.PublicKey().Address().Bytes()) - avaxAddr, _ := utils.Format("X", chainId.Name, credentials[i].PK.PublicKey().Address().Bytes()) - allocations = append(allocations, GenesisAllocation{ - ETHAddr: "0x" + credentials[i].PK.PublicKey().Address().Hex(), - AVAXAddr: avaxAddr, - InitialAmount: math.MaxUint32, - UnlockSchedule: []GenesisLockedAmount{{Amount: 1294967295}}, - }) stakes = append(stakes, GenesisStaker{ NodeID: credentials[i].ID.String(), - RewardAddress: avaxAddr0, - DelegationFee: 1000, + RewardAddress: avaxAddr, + DelegationFee: 100000000, }) } - avaxAddr, _ := utils.Format("X", chainId.Name, credentials[0].PK.PublicKey().Address().Bytes()) stakedFunds = append(stakedFunds, avaxAddr) genesis := NewGenesis(chainId.Number, allocations, stakedFunds, stakes) - nodes := make(AvalancheNodes, 0, numNodes) for i := 0; i < numNodes; i++ { var bootstrapOpt []*AvalancheNode = nil @@ -186,10 +227,17 @@ func (c *AvalancheChain) Start(testName string, ctx context.Context, additionalG for _, node := range c.nodes { node := node eg.Go(func() error { - return node.StartContainer(egCtx, testName, additionalGenesisWallets) + tCtx, tCtxCancel := context.WithTimeout(egCtx, ChainBootsrapTimeout) + defer tCtxCancel() + + return node.Start(tCtx, testName, additionalGenesisWallets) }) } - return eg.Wait() + if err := eg.Wait(); err != nil { + return err + } + + return c.node().StartSubnets(ctx) } // Exec runs an arbitrary command using Chain's docker environment. diff --git a/chain/avalanche/utils/cache/cache.go b/chain/avalanche/utils/cache/cache.go deleted file mode 100644 index ea6c41684..000000000 --- a/chain/avalanche/utils/cache/cache.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cache - -// Cacher acts as a best effort key value store. -type Cacher[K comparable, V any] interface { - // Put inserts an element into the cache. If space is required, elements will - // be evicted. - Put(key K, value V) - - // Get returns the entry in the cache with the key specified, if no value - // exists, false is returned. - Get(key K) (V, bool) - - // Evict removes the specified entry from the cache - Evict(key K) - - // Flush removes all entries from the cache - Flush() -} - -// Evictable allows the object to be notified when it is evicted -type Evictable[K comparable] interface { - Key() K - Evict() -} - -// Deduplicator acts as a best effort deduplication service -type Deduplicator[K comparable, V Evictable[K]] interface { - // Deduplicate returns either the provided value, or a previously provided - // value with the same ID that hasn't yet been evicted - Deduplicate(V) V - - // Flush removes all entries from the cache - Flush() -} diff --git a/chain/avalanche/utils/cache/lru_cache.go b/chain/avalanche/utils/cache/lru_cache.go deleted file mode 100644 index 81e7e87b1..000000000 --- a/chain/avalanche/utils/cache/lru_cache.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cache - -import ( - "sync" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/linkedhashmap" -) - -var _ Cacher[struct{}, struct{}] = (*LRU[struct{}, struct{}])(nil) - -// LRU is a key value store with bounded size. If the size is attempted to be -// exceeded, then an element is removed from the cache before the insertion is -// done, based on evicting the least recently used value. -type LRU[K comparable, V any] struct { - lock sync.Mutex - elements linkedhashmap.LinkedHashmap[K, V] - // If set to < 0, will be set internally to 1. - Size int -} - -func (c *LRU[K, V]) Put(key K, value V) { - c.lock.Lock() - defer c.lock.Unlock() - - c.put(key, value) -} - -func (c *LRU[K, V]) Get(key K) (V, bool) { - c.lock.Lock() - defer c.lock.Unlock() - - return c.get(key) -} - -func (c *LRU[K, _]) Evict(key K) { - c.lock.Lock() - defer c.lock.Unlock() - - c.evict(key) -} - -func (c *LRU[_, _]) Flush() { - c.lock.Lock() - defer c.lock.Unlock() - - c.flush() -} - -func (c *LRU[K, V]) put(key K, value V) { - c.resize() - - if c.elements.Len() == c.Size { - oldestKey, _, _ := c.elements.Oldest() - c.elements.Delete(oldestKey) - } - c.elements.Put(key, value) -} - -func (c *LRU[K, V]) get(key K) (V, bool) { - c.resize() - - val, ok := c.elements.Get(key) - if !ok { - return utils.Zero[V](), false - } - c.elements.Put(key, val) // Mark [k] as MRU. - return val, true -} - -func (c *LRU[K, _]) evict(key K) { - c.resize() - - c.elements.Delete(key) -} - -func (c *LRU[K, V]) flush() { - c.elements = linkedhashmap.New[K, V]() -} - -// Initializes [c.elements] if it's nil. -// Sets [c.size] to 1 if it's <= 0. -// Removes oldest elements to make number of elements -// in the cache == [c.size] if necessary. -func (c *LRU[K, V]) resize() { - if c.elements == nil { - c.elements = linkedhashmap.New[K, V]() - } - if c.Size <= 0 { - c.Size = 1 - } - for c.elements.Len() > c.Size { - oldestKey, _, _ := c.elements.Oldest() - c.elements.Delete(oldestKey) - } -} diff --git a/chain/avalanche/utils/cache/lru_cache_benchmark_test.go b/chain/avalanche/utils/cache/lru_cache_benchmark_test.go deleted file mode 100644 index 9d95c7779..000000000 --- a/chain/avalanche/utils/cache/lru_cache_benchmark_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cache - -import ( - "crypto/rand" - "testing" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -func BenchmarkLRUCachePutSmall(b *testing.B) { - smallLen := 5 - cache := &LRU[ids.ID, int]{Size: smallLen} - for n := 0; n < b.N; n++ { - for i := 0; i < smallLen; i++ { - var id ids.ID - if _, err := rand.Read(id[:]); err != nil { - b.Fatal(err) - } - cache.Put(id, n) - } - b.StopTimer() - cache.Flush() - b.StartTimer() - } -} - -func BenchmarkLRUCachePutMedium(b *testing.B) { - mediumLen := 250 - cache := &LRU[ids.ID, int]{Size: mediumLen} - for n := 0; n < b.N; n++ { - for i := 0; i < mediumLen; i++ { - var id ids.ID - if _, err := rand.Read(id[:]); err != nil { - b.Fatal(err) - } - cache.Put(id, n) - } - b.StopTimer() - cache.Flush() - b.StartTimer() - } -} - -func BenchmarkLRUCachePutLarge(b *testing.B) { - largeLen := 10000 - cache := &LRU[ids.ID, int]{Size: largeLen} - for n := 0; n < b.N; n++ { - for i := 0; i < largeLen; i++ { - var id ids.ID - if _, err := rand.Read(id[:]); err != nil { - b.Fatal(err) - } - cache.Put(id, n) - } - b.StopTimer() - cache.Flush() - b.StartTimer() - } -} diff --git a/chain/avalanche/utils/cache/lru_cache_test.go b/chain/avalanche/utils/cache/lru_cache_test.go deleted file mode 100644 index c21b28c56..000000000 --- a/chain/avalanche/utils/cache/lru_cache_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cache - -import ( - "testing" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -func TestLRU(t *testing.T) { - cache := &LRU[ids.ID, int]{Size: 1} - - TestBasic(t, cache) -} - -func TestLRUEviction(t *testing.T) { - cache := &LRU[ids.ID, int]{Size: 2} - - TestEviction(t, cache) -} - -func TestLRUResize(t *testing.T) { - cache := LRU[ids.ID, int]{Size: 2} - - id1 := ids.ID{1} - id2 := ids.ID{2} - - cache.Put(id1, 1) - cache.Put(id2, 2) - - if val, found := cache.Get(id1); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 1 { - t.Fatalf("Retrieved wrong value") - } else if val, found := cache.Get(id2); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 2 { - t.Fatalf("Retrieved wrong value") - } - - cache.Size = 1 - // id1 evicted - - if _, found := cache.Get(id1); found { - t.Fatalf("Retrieve value when none exists") - } else if val, found := cache.Get(id2); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 2 { - t.Fatalf("Retrieved wrong value") - } - - cache.Size = 0 - // We reset the size to 1 in resize - - if _, found := cache.Get(id1); found { - t.Fatalf("Retrieve value when none exists") - } else if val, found := cache.Get(id2); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 2 { - t.Fatalf("Retrieved wrong value") - } -} diff --git a/chain/avalanche/utils/cache/test_cacher.go b/chain/avalanche/utils/cache/test_cacher.go deleted file mode 100644 index 093b02b43..000000000 --- a/chain/avalanche/utils/cache/test_cacher.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cache - -import ( - "testing" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -// CacherTests is a list of all Cacher tests -var CacherTests = []struct { - Size int - Func func(t *testing.T, c Cacher[ids.ID, int]) -}{ - {Size: 1, Func: TestBasic}, - {Size: 2, Func: TestEviction}, -} - -func TestBasic(t *testing.T, cache Cacher[ids.ID, int]) { - id1 := ids.ID{1} - if _, found := cache.Get(id1); found { - t.Fatalf("Retrieved value when none exists") - } - - expectedValue1 := 1 - cache.Put(id1, expectedValue1) - if value, found := cache.Get(id1); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if value != expectedValue1 { - t.Fatalf("Failed to retrieve correct value when one exists") - } - - cache.Put(id1, expectedValue1) - if value, found := cache.Get(id1); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if value != expectedValue1 { - t.Fatalf("Failed to retrieve correct value when one exists") - } - - cache.Put(id1, expectedValue1) - if value, found := cache.Get(id1); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if value != expectedValue1 { - t.Fatalf("Failed to retrieve correct value when one exists") - } - - id2 := ids.ID{2} - - expectedValue2 := 2 - cache.Put(id2, expectedValue2) - if _, found := cache.Get(id1); found { - t.Fatalf("Retrieved value when none exists") - } - if value, found := cache.Get(id2); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if value != expectedValue2 { - t.Fatalf("Failed to retrieve correct value when one exists") - } -} - -func TestEviction(t *testing.T, cache Cacher[ids.ID, int]) { - id1 := ids.ID{1} - id2 := ids.ID{2} - id3 := ids.ID{3} - - cache.Put(id1, 1) - cache.Put(id2, 2) - - if val, found := cache.Get(id1); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 1 { - t.Fatalf("Retrieved wrong value") - } else if val, found := cache.Get(id2); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 2 { - t.Fatalf("Retrieved wrong value") - } else if _, found := cache.Get(id3); found { - t.Fatalf("Retrieve value when none exists") - } - - cache.Put(id3, 3) - - if _, found := cache.Get(id1); found { - t.Fatalf("Retrieve value when none exists") - } else if val, found := cache.Get(id2); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 2 { - t.Fatalf("Retrieved wrong value") - } else if val, found := cache.Get(id3); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 3 { - t.Fatalf("Retrieved wrong value") - } - - cache.Get(id2) - cache.Put(id1, 1) - - if val, found := cache.Get(id1); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 1 { - t.Fatalf("Retrieved wrong value") - } else if val, found := cache.Get(id2); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 2 { - t.Fatalf("Retrieved wrong value") - } else if _, found := cache.Get(id3); found { - t.Fatalf("Retrieved value when none exists") - } - - cache.Evict(id2) - cache.Put(id3, 3) - - if val, found := cache.Get(id1); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 1 { - t.Fatalf("Retrieved wrong value") - } else if _, found := cache.Get(id2); found { - t.Fatalf("Retrieved value when none exists") - } else if val, found := cache.Get(id3); !found { - t.Fatalf("Failed to retrieve value when one exists") - } else if val != 3 { - t.Fatalf("Retrieved wrong value") - } - - cache.Flush() - - if _, found := cache.Get(id1); found { - t.Fatalf("Retrieved value when none exists") - } else if _, found := cache.Get(id2); found { - t.Fatalf("Retrieved value when none exists") - } else if _, found := cache.Get(id3); found { - t.Fatalf("Retrieved value when none exists") - } -} diff --git a/chain/avalanche/utils/cache/unique_cache.go b/chain/avalanche/utils/cache/unique_cache.go deleted file mode 100644 index 24052d793..000000000 --- a/chain/avalanche/utils/cache/unique_cache.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cache - -import ( - "container/list" - "sync" -) - -var _ Deduplicator[struct{}, Evictable[struct{}]] = (*EvictableLRU[struct{}, Evictable[struct{}]])(nil) - -// EvictableLRU is an LRU cache that notifies the objects when they are evicted. -type EvictableLRU[K comparable, _ Evictable[K]] struct { - lock sync.Mutex - entryMap map[K]*list.Element - entryList *list.List - Size int -} - -func (c *EvictableLRU[_, V]) Deduplicate(value V) V { - c.lock.Lock() - defer c.lock.Unlock() - - return c.deduplicate(value) -} - -func (c *EvictableLRU[_, _]) Flush() { - c.lock.Lock() - defer c.lock.Unlock() - - c.flush() -} - -func (c *EvictableLRU[K, _]) init() { - if c.entryMap == nil { - c.entryMap = make(map[K]*list.Element) - } - if c.entryList == nil { - c.entryList = list.New() - } - if c.Size <= 0 { - c.Size = 1 - } -} - -func (c *EvictableLRU[_, V]) resize() { - for c.entryList.Len() > c.Size { - e := c.entryList.Front() - c.entryList.Remove(e) - - val := e.Value.(V) - delete(c.entryMap, val.Key()) - val.Evict() - } -} - -func (c *EvictableLRU[_, V]) deduplicate(value V) V { - c.init() - c.resize() - - key := value.Key() - if e, ok := c.entryMap[key]; !ok { - if c.entryList.Len() >= c.Size { - e = c.entryList.Front() - c.entryList.MoveToBack(e) - - val := e.Value.(V) - delete(c.entryMap, val.Key()) - val.Evict() - - e.Value = value - } else { - e = c.entryList.PushBack(value) - } - c.entryMap[key] = e - } else { - c.entryList.MoveToBack(e) - - val := e.Value.(V) - value = val - } - return value -} - -func (c *EvictableLRU[_, _]) flush() { - c.init() - - size := c.Size - c.Size = 0 - c.resize() - c.Size = size -} diff --git a/chain/avalanche/utils/cache/unique_cache_test.go b/chain/avalanche/utils/cache/unique_cache_test.go deleted file mode 100644 index c25604d43..000000000 --- a/chain/avalanche/utils/cache/unique_cache_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cache - -import ( - "testing" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -type evictable[K comparable] struct { - id K - evicted int -} - -func (e *evictable[K]) Key() K { - return e.id -} - -func (e *evictable[_]) Evict() { - e.evicted++ -} - -func TestEvictableLRU(t *testing.T) { - cache := EvictableLRU[ids.ID, *evictable[ids.ID]]{} - - expectedValue1 := &evictable[ids.ID]{id: ids.ID{1}} - if returnedValue := cache.Deduplicate(expectedValue1); returnedValue != expectedValue1 { - t.Fatalf("Returned unknown value") - } else if expectedValue1.evicted != 0 { - t.Fatalf("Value was evicted unexpectedly") - } else if returnedValue := cache.Deduplicate(expectedValue1); returnedValue != expectedValue1 { - t.Fatalf("Returned unknown value") - } else if expectedValue1.evicted != 0 { - t.Fatalf("Value was evicted unexpectedly") - } - - expectedValue2 := &evictable[ids.ID]{id: ids.ID{2}} - returnedValue := cache.Deduplicate(expectedValue2) - switch { - case returnedValue != expectedValue2: - t.Fatalf("Returned unknown value") - case expectedValue1.evicted != 1: - t.Fatalf("Value should have been evicted") - case expectedValue2.evicted != 0: - t.Fatalf("Value was evicted unexpectedly") - } - - cache.Size = 2 - - expectedValue3 := &evictable[ids.ID]{id: ids.ID{2}} - returnedValue = cache.Deduplicate(expectedValue3) - switch { - case returnedValue != expectedValue2: - t.Fatalf("Returned unknown value") - case expectedValue1.evicted != 1: - t.Fatalf("Value should have been evicted") - case expectedValue2.evicted != 0: - t.Fatalf("Value was evicted unexpectedly") - } - - cache.Flush() - switch { - case expectedValue1.evicted != 1: - t.Fatalf("Value should have been evicted") - case expectedValue2.evicted != 1: - t.Fatalf("Value should have been evicted") - case expectedValue3.evicted != 0: - t.Fatalf("Value was evicted unexpectedly") - } -} diff --git a/chain/avalanche/utils/cb58/cb58.go b/chain/avalanche/utils/cb58/cb58.go deleted file mode 100644 index 88edf1e6d..000000000 --- a/chain/avalanche/utils/cb58/cb58.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cb58 - -import ( - "bytes" - "errors" - "math" - - "github.com/mr-tron/base58/base58" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" -) - -const ( - checksumLen = 4 -) - -var ( - errEncodingOverFlow = errors.New("encoding overflow") - errMissingChecksum = errors.New("input string is smaller than the checksum size") - errBadChecksum = errors.New("invalid input checksum") -) - -// Encode [bytes] to a string using cb58 format. -// [bytes] may be nil, in which case it will be treated the same as an empty -// slice. -func Encode(bytes []byte) (string, error) { - bytesLen := len(bytes) - if bytesLen > math.MaxInt32-checksumLen { - return "", errEncodingOverFlow - } - checked := make([]byte, bytesLen+checksumLen) - copy(checked, bytes) - copy(checked[len(bytes):], hashing.Checksum(bytes, checksumLen)) - return base58.Encode(checked), nil -} - -// Decode [str] to bytes from cb58. -func Decode(str string) ([]byte, error) { - decodedBytes, err := base58.Decode(str) - if err != nil { - return nil, err - } - if len(decodedBytes) < checksumLen { - return nil, errMissingChecksum - } - // Verify the checksum - rawBytes := decodedBytes[:len(decodedBytes)-checksumLen] - checksum := decodedBytes[len(decodedBytes)-checksumLen:] - if !bytes.Equal(checksum, hashing.Checksum(rawBytes, checksumLen)) { - return nil, errBadChecksum - } - return rawBytes, nil -} diff --git a/chain/avalanche/utils/cb58/cb58_test.go b/chain/avalanche/utils/cb58/cb58_test.go deleted file mode 100644 index 59710b623..000000000 --- a/chain/avalanche/utils/cb58/cb58_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cb58 - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" -) - -// Test encoding bytes to a string and decoding back to bytes -func TestEncodeDecode(t *testing.T) { - type test struct { - bytes []byte - str string - } - - id := [32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} - tests := []test{ - { - nil, - "45PJLL", - }, - { - []byte{}, - "45PJLL", - }, - { - []byte{0}, - "1c7hwa", - }, - { - []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255}, - "1NVSVezva3bAtJesnUj", - }, - { - id[:], - "SkB92YpWm4Q2ijQHH34cqbKkCZWszsiQgHVjtNeFF2HdvDQU", - }, - } - - for _, test := range tests { - // Encode the bytes - strResult, err := Encode(test.bytes) - if err != nil { - t.Fatal(err) - } - // Make sure the string repr. is what we expected - require.Equal(t, test.str, strResult) - // Decode the string - bytesResult, err := Decode(strResult) - if err != nil { - t.Fatal(err) - } - // Make sure we got the same bytes back - if !bytes.Equal(test.bytes, bytesResult) { - t.Fatal("bytes not symmetric") - } - } -} - -func FuzzEncodeDecode(f *testing.F) { - f.Fuzz(func(t *testing.T, data []byte) { - require := require.New(t) - - // Encode bytes to string - dataStr, err := Encode(data) - require.NoError(err) - - // Decode string to bytes - gotData, err := Decode(dataStr) - require.NoError(err) - - require.Equal(data, gotData) - }) -} diff --git a/chain/avalanche/utils/chain_id_test.go b/chain/avalanche/utils/chain_id_test.go deleted file mode 100644 index bbe38d14f..000000000 --- a/chain/avalanche/utils/chain_id_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package utils - -import ( - "testing" -) - -func TestNetworkID(t *testing.T) { - inputs := []struct { - str string - obj ChainID - err error - }{ - { - str: "somestring", - err: ErrBadChainID, - }, - { - str: "network-123", - obj: ChainID{"network", 123}, - }, - } - for i, input := range inputs { - netId, err := ParseChainID(input.str) - if input.err != nil && (err == nil || input.err != err) { - t.Errorf("[%d] error > want: %s, got: %s", i, input.err, err) - } - if input.err == nil && (netId == nil || netId.Name != input.obj.Name || netId.Number != input.obj.Number) { - t.Errorf("[%d] result > want: %#v, got: %#v", i, input.obj, netId) - } - } -} diff --git a/chain/avalanche/utils/crypto/secp256k1/rfc6979_test.go b/chain/avalanche/utils/crypto/secp256k1/rfc6979_test.go deleted file mode 100644 index d4c0a9c45..000000000 --- a/chain/avalanche/utils/crypto/secp256k1/rfc6979_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package secp256k1 - -import ( - "encoding/hex" - "fmt" - "testing" - - "github.com/stretchr/testify/require" -) - -// See: https://bitcointalk.org/index.php?topic=285142.msg3300992#msg3300992 as -// the source of these test vectors. -var rfc6979Tests = []test{ - { - skHex: "0000000000000000000000000000000000000000000000000000000000000001", - msg: "Everything should be made as simple as possible, but not simpler.", - rsHex: "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", - }, - { - skHex: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - msg: "Equations are more important to me, because politics is for the present, but an equation is something for eternity.", - rsHex: "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5", - }, - { - skHex: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - msg: "Not only is the Universe stranger than we think, it is stranger than we can think.", - rsHex: "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283", - }, - { - skHex: "0000000000000000000000000000000000000000000000000000000000000001", - msg: "How wonderful that we have met with a paradox. Now we have some hope of making progress.", - rsHex: "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3", - }, - { - skHex: "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64", - msg: "Computer science is no more about computers than astronomy is about telescopes.", - rsHex: "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6", - }, - { - skHex: "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637", - msg: "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough", - rsHex: "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37", - }, - { - skHex: "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3", - msg: "The question of whether computers can think is like the question of whether submarines can swim.", - rsHex: "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef", - }, -} - -type test struct { - skHex string - msg string - rsHex string -} - -func TestRFC6979Compliance(t *testing.T) { - f := Factory{} - for i, tt := range rfc6979Tests { - t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) { - require := require.New(t) - - skBytes, err := hex.DecodeString(tt.skHex) - require.NoError(err) - - sk, err := f.ToPrivateKey(skBytes) - require.NoError(err) - - msgBytes := []byte(tt.msg) - sigBytes, err := sk.Sign(msgBytes) - require.NoError(err) - - expectedRSBytes, err := hex.DecodeString(tt.rsHex) - require.NoError(err) - - // sigBytes is returned in [R || S || V] format, so we drop last - // byte to get [R || S] - rsBytes := sigBytes[:len(sigBytes)-1] - require.Equal(expectedRSBytes, rsBytes) - }) - } -} diff --git a/chain/avalanche/utils/crypto/secp256k1/secp256k1.go b/chain/avalanche/utils/crypto/secp256k1/secp256k1.go deleted file mode 100644 index 2b2e0b4e2..000000000 --- a/chain/avalanche/utils/crypto/secp256k1/secp256k1.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package secp256k1 - -import ( - "errors" - "fmt" - "strings" - - stdecdsa "crypto/ecdsa" - - "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" - - secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/cache" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/cb58" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -const ( - // SignatureLen is the number of bytes in a secp2561k recoverable signature - SignatureLen = 65 - - // PrivateKeyLen is the number of bytes in a secp2561k recoverable private - // key - PrivateKeyLen = 32 - - // PublicKeyLen is the number of bytes in a secp2561k recoverable public key - PublicKeyLen = 33 - - // from the decred library: - // compactSigMagicOffset is a value used when creating the compact signature - // recovery code inherited from Bitcoin and has no meaning, but has been - // retained for compatibility. For historical purposes, it was originally - // picked to avoid a binary representation that would allow compact - // signatures to be mistaken for other components. - compactSigMagicOffset = 27 - - PrivateKeyPrefix = "PrivateKey-" - nullStr = "null" -) - -var ( - errCompressed = errors.New("wasn't expecting a compressed key") - errMissingQuotes = errors.New("first and last characters should be quotes") - errMissingKeyPrefix = fmt.Errorf("private key missing %s prefix", PrivateKeyPrefix) - errInvalidPrivateKeyLength = fmt.Errorf("private key has unexpected length, expected %d", PrivateKeyLen) - errInvalidPublicKeyLength = fmt.Errorf("public key has unexpected length, expected %d", PublicKeyLen) - errInvalidSigLen = errors.New("invalid signature length") - errMutatedSig = errors.New("signature was mutated from its original format") -) - -type Factory struct { - Cache cache.LRU[ids.ID, *PublicKey] -} - -func (*Factory) NewPrivateKey() (*PrivateKey, error) { - k, err := secp256k1.GeneratePrivateKey() - return &PrivateKey{sk: k}, err -} - -func (*Factory) ToPublicKey(b []byte) (*PublicKey, error) { - if len(b) != PublicKeyLen { - return nil, errInvalidPublicKeyLength - } - - key, err := secp256k1.ParsePubKey(b) - return &PublicKey{ - pk: key, - bytes: b, - }, err -} - -func (*Factory) ToPrivateKey(b []byte) (*PrivateKey, error) { - if len(b) != PrivateKeyLen { - return nil, errInvalidPrivateKeyLength - } - return &PrivateKey{ - sk: secp256k1.PrivKeyFromBytes(b), - bytes: b, - }, nil -} - -func (f *Factory) RecoverPublicKey(msg, sig []byte) (*PublicKey, error) { - return f.RecoverHashPublicKey(hashing.ComputeHash256(msg), sig) -} - -func (f *Factory) RecoverHashPublicKey(hash, sig []byte) (*PublicKey, error) { - cacheBytes := make([]byte, len(hash)+len(sig)) - copy(cacheBytes, hash) - copy(cacheBytes[len(hash):], sig) - id := hashing.ComputeHash256Array(cacheBytes) - if cachedPublicKey, ok := f.Cache.Get(id); ok { - return cachedPublicKey, nil - } - - if err := verifySECP256K1RSignatureFormat(sig); err != nil { - return nil, err - } - - sig, err := sigToRawSig(sig) - if err != nil { - return nil, err - } - - rawPubkey, compressed, err := ecdsa.RecoverCompact(sig, hash) - if err != nil { - return nil, err - } - - if compressed { - return nil, errCompressed - } - - pubkey := &PublicKey{pk: rawPubkey} - f.Cache.Put(id, pubkey) - return pubkey, nil -} - -type PublicKey struct { - pk *secp256k1.PublicKey - addr ids.ShortID - bytes []byte -} - -func (k *PublicKey) Verify(msg, sig []byte) bool { - return k.VerifyHash(hashing.ComputeHash256(msg), sig) -} - -func (k *PublicKey) VerifyHash(hash, sig []byte) bool { - factory := Factory{} - pk, err := factory.RecoverHashPublicKey(hash, sig) - if err != nil { - return false - } - return k.Address() == pk.Address() -} - -// ToECDSA returns the ecdsa representation of this public key -func (k *PublicKey) ToECDSA() *stdecdsa.PublicKey { - return k.pk.ToECDSA() -} - -func (k *PublicKey) Address() ids.ShortID { - if k.addr == ids.ShortEmpty { - addr, err := ids.ToShortID(hashing.PubkeyBytesToAddress(k.Bytes())) - if err != nil { - panic(err) - } - k.addr = addr - } - return k.addr -} - -func (k *PublicKey) Bytes() []byte { - if k.bytes == nil { - k.bytes = k.pk.SerializeCompressed() - } - return k.bytes -} - -type PrivateKey struct { - sk *secp256k1.PrivateKey - pk *PublicKey - bytes []byte -} - -func (k *PrivateKey) PublicKey() *PublicKey { - if k.pk == nil { - k.pk = &PublicKey{pk: k.sk.PubKey()} - } - return k.pk -} - -func (k *PrivateKey) Address() ids.ShortID { - return k.PublicKey().Address() -} - -func (k *PrivateKey) Sign(msg []byte) ([]byte, error) { - return k.SignHash(hashing.ComputeHash256(msg)) -} - -func (k *PrivateKey) SignHash(hash []byte) ([]byte, error) { - sig := ecdsa.SignCompact(k.sk, hash, false) // returns [v || r || s] - return rawSigToSig(sig) -} - -// ToECDSA returns the ecdsa representation of this private key -func (k *PrivateKey) ToECDSA() *stdecdsa.PrivateKey { - return k.sk.ToECDSA() -} - -func (k *PrivateKey) Bytes() []byte { - if k.bytes == nil { - k.bytes = k.sk.Serialize() - } - return k.bytes -} - -func (k *PrivateKey) String() string { - // We assume that the maximum size of a byte slice that - // can be stringified is at least the length of a SECP256K1 private key - keyStr, _ := cb58.Encode(k.Bytes()) - return PrivateKeyPrefix + keyStr -} - -func (k *PrivateKey) MarshalJSON() ([]byte, error) { - return []byte("\"" + k.String() + "\""), nil -} - -func (k *PrivateKey) MarshalText() ([]byte, error) { - return []byte(k.String()), nil -} - -func (k *PrivateKey) UnmarshalJSON(b []byte) error { - str := string(b) - if str == nullStr { // If "null", do nothing - return nil - } else if len(str) < 2 { - return errMissingQuotes - } - - lastIndex := len(str) - 1 - if str[0] != '"' || str[lastIndex] != '"' { - return errMissingQuotes - } - - strNoQuotes := str[1:lastIndex] - if !strings.HasPrefix(strNoQuotes, PrivateKeyPrefix) { - return errMissingKeyPrefix - } - - strNoPrefix := strNoQuotes[len(PrivateKeyPrefix):] - keyBytes, err := cb58.Decode(strNoPrefix) - if err != nil { - return err - } - if len(keyBytes) != PrivateKeyLen { - return errInvalidPrivateKeyLength - } - - *k = PrivateKey{ - sk: secp256k1.PrivKeyFromBytes(keyBytes), - bytes: keyBytes, - } - return nil -} - -func (k *PrivateKey) UnmarshalText(text []byte) error { - return k.UnmarshalJSON(text) -} - -// raw sig has format [v || r || s] whereas the sig has format [r || s || v] -func rawSigToSig(sig []byte) ([]byte, error) { - if len(sig) != SignatureLen { - return nil, errInvalidSigLen - } - recCode := sig[0] - copy(sig, sig[1:]) - sig[SignatureLen-1] = recCode - compactSigMagicOffset - return sig, nil -} - -// sig has format [r || s || v] whereas the raw sig has format [v || r || s] -func sigToRawSig(sig []byte) ([]byte, error) { - if len(sig) != SignatureLen { - return nil, errInvalidSigLen - } - newSig := make([]byte, SignatureLen) - newSig[0] = sig[SignatureLen-1] + compactSigMagicOffset - copy(newSig[1:], sig) - return newSig, nil -} - -// verifies the signature format in format [r || s || v] -func verifySECP256K1RSignatureFormat(sig []byte) error { - if len(sig) != SignatureLen { - return errInvalidSigLen - } - - var s secp256k1.ModNScalar - s.SetByteSlice(sig[32:64]) - if s.IsOverHalfOrder() { - return errMutatedSig - } - return nil -} diff --git a/chain/avalanche/utils/crypto/secp256k1/secp256k1_benchmark_test.go b/chain/avalanche/utils/crypto/secp256k1/secp256k1_benchmark_test.go deleted file mode 100644 index af71a4996..000000000 --- a/chain/avalanche/utils/crypto/secp256k1/secp256k1_benchmark_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package secp256k1 - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -// NumVerifies is the number of verifications to run per operation -const NumVerifies = 1 - -var ( - hashes [][]byte - - keys []*PublicKey - sigs [][]byte -) - -func init() { - // Setup hashes: - bytes := ids.ID{} - for i := uint64(0); i < NumVerifies; i++ { - bytes[i%32]++ - hash := hashing.ComputeHash256(bytes[:]) - hashes = append(hashes, hash) - } - - // Setup signatures: - f := &Factory{} - for i := uint64(0); i < NumVerifies; i++ { - privateKey, err := f.NewPrivateKey() - if err != nil { - panic(err) - } - - publicKey := privateKey.PublicKey() - sig, err := privateKey.SignHash(hashes[i]) - if err != nil { - panic(err) - } - - keys = append(keys, publicKey) - sigs = append(sigs, sig) - } -} - -// BenchmarkSECP256k1Verify runs the benchmark with SECP256K1 keys -func BenchmarkSECP256k1Verify(b *testing.B) { - require := require.New(b) - - for n := 0; n < b.N; n++ { - for i := 0; i < NumVerifies; i++ { - require.True(keys[i].VerifyHash(hashes[i], sigs[i])) - } - } -} diff --git a/chain/avalanche/utils/crypto/secp256k1/secp256k1_test.go b/chain/avalanche/utils/crypto/secp256k1/secp256k1_test.go deleted file mode 100644 index 10db8bf48..000000000 --- a/chain/avalanche/utils/crypto/secp256k1/secp256k1_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package secp256k1 - -import ( - "testing" - - "github.com/stretchr/testify/require" - - secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/cache" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -func TestRecover(t *testing.T) { - require := require.New(t) - - f := Factory{} - key, err := f.NewPrivateKey() - require.NoError(err) - - msg := []byte{1, 2, 3} - sig, err := key.Sign(msg) - require.NoError(err) - - pub := key.PublicKey() - pubRec, err := f.RecoverPublicKey(msg, sig) - require.NoError(err) - - require.Equal(pub, pubRec) -} - -func TestCachedRecover(t *testing.T) { - require := require.New(t) - - f := Factory{Cache: cache.LRU[ids.ID, *PublicKey]{Size: 1}} - key, err := f.NewPrivateKey() - require.NoError(err) - - msg := []byte{1, 2, 3} - sig, err := key.Sign(msg) - require.NoError(err) - - pub1, err := f.RecoverPublicKey(msg, sig) - require.NoError(err) - pub2, err := f.RecoverPublicKey(msg, sig) - require.NoError(err) - - require.Equal(pub1, pub2) -} - -func TestExtensive(t *testing.T) { - require := require.New(t) - - f := Factory{} - hash := hashing.ComputeHash256([]byte{1, 2, 3}) - for i := 0; i < 1000; i++ { - key, err := f.NewPrivateKey() - require.NoError(err) - - _, err = key.SignHash(hash) - require.NoError(err) - } -} - -func TestGenRecreate(t *testing.T) { - require := require.New(t) - - f := Factory{} - for i := 0; i < 1000; i++ { - sk, err := f.NewPrivateKey() - require.NoError(err) - - skBytes := sk.Bytes() - recoveredSk, err := f.ToPrivateKey(skBytes) - require.NoError(err) - - require.Equal(sk.PublicKey(), recoveredSk.PublicKey()) - } -} - -func TestVerifyMutatedSignature(t *testing.T) { - require := require.New(t) - - f := Factory{} - sk, err := f.NewPrivateKey() - require.NoError(err) - - msg := []byte{'h', 'e', 'l', 'l', 'o'} - sig, err := sk.Sign(msg) - require.NoError(err) - - var s secp256k1.ModNScalar - s.SetByteSlice(sig[32:64]) - s.Negate() - newSBytes := s.Bytes() - copy(sig[32:], newSBytes[:]) - - _, err = f.RecoverPublicKey(msg, sig) - require.Error(err) -} - -func TestPrivateKeySECP256K1RUnmarshalJSON(t *testing.T) { - require := require.New(t) - f := Factory{} - - key, err := f.NewPrivateKey() - require.NoError(err) - - keyJSON, err := key.MarshalJSON() - require.NoError(err) - - key2 := PrivateKey{} - err = key2.UnmarshalJSON(keyJSON) - require.NoError(err) - require.Equal(key.PublicKey(), key2.PublicKey()) -} - -func TestPrivateKeySECP256K1RUnmarshalJSONError(t *testing.T) { - tests := []struct { - label string - in []byte - }{ - { - "too short", - []byte(`"`), - }, - { - "missing start quote", - []byte(`PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"`), - }, - { - "missing end quote", - []byte(`"PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN`), - }, - { - "incorrect prefix", - []byte(`"PrivateKfy-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"`), - }, - { - `"PrivateKey-"`, - []byte(`"PrivateKey-"`), - }, - { - `"PrivateKey-1"`, - []byte(`"PrivateKey-1"`), - }, - } - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - require := require.New(t) - - foo := PrivateKey{} - err := foo.UnmarshalJSON(tt.in) - require.Error(err) - }) - } -} - -func TestSigning(t *testing.T) { - tests := []struct { - msg []byte - sig []byte - }{ - { - []byte("hello world"), - []byte{ - 0x17, 0x8c, 0xb6, 0x09, 0x6b, 0x3c, 0xa5, 0x82, - 0x0a, 0x4c, 0x6e, 0xce, 0xdf, 0x15, 0xb6, 0x8b, - 0x6f, 0x50, 0xe2, 0x52, 0xc2, 0xb6, 0x4f, 0x37, - 0x74, 0x88, 0x86, 0x02, 0xcc, 0x9f, 0xa0, 0x8c, - 0x5d, 0x01, 0x9d, 0x82, 0xfd, 0xde, 0x95, 0xfd, - 0xf2, 0x34, 0xaa, 0x2d, 0x12, 0xad, 0x79, 0xb5, - 0xab, 0xb3, 0x45, 0xfe, 0x95, 0x3a, 0x9f, 0x72, - 0xf7, 0x09, 0x14, 0xfd, 0x31, 0x39, 0x06, 0x3b, - 0x00, - }, - }, - { - []byte("scooby doo"), - []byte{ - 0xc2, 0x57, 0x3f, 0x29, 0xb0, 0xd1, 0x7a, 0xe7, - 0x00, 0x9a, 0x9f, 0x17, 0xa4, 0x55, 0x8d, 0x32, - 0x46, 0x2e, 0x5b, 0x8d, 0x05, 0x9e, 0x38, 0x32, - 0xec, 0xb0, 0x32, 0x54, 0x1a, 0xbc, 0x7d, 0xaf, - 0x57, 0x51, 0xf9, 0x6b, 0x85, 0x71, 0xbc, 0xb7, - 0x18, 0xd2, 0x6b, 0xe8, 0xed, 0x8d, 0x59, 0xb0, - 0xd6, 0x03, 0x69, 0xab, 0x57, 0xac, 0xc0, 0xf7, - 0x13, 0x3b, 0x21, 0x94, 0x56, 0x03, 0x8e, 0xc7, - 0x01, - }, - }, - { - []byte("a really long string"), - []byte{ - 0x1b, 0xf5, 0x61, 0xc3, 0x60, 0x07, 0xd2, 0xa6, - 0x12, 0x68, 0xe9, 0xe1, 0x3a, 0x90, 0x2a, 0x9c, - 0x2b, 0xa4, 0x3e, 0x28, 0xf8, 0xd4, 0x75, 0x54, - 0x21, 0x57, 0x11, 0xdc, 0xdc, 0xc6, 0xd3, 0x5e, - 0x78, 0x43, 0x18, 0xf6, 0x22, 0x91, 0x37, 0x3c, - 0x95, 0x77, 0x9f, 0x67, 0x94, 0x91, 0x0a, 0x44, - 0x16, 0xbf, 0xa3, 0xae, 0x9f, 0x25, 0xfa, 0x34, - 0xa0, 0x14, 0xea, 0x9c, 0x6f, 0xe0, 0x20, 0x37, - 0x00, - }, - }, - } - - key := TestKeys()[0] - - for _, tt := range tests { - t.Run(string(tt.msg), func(t *testing.T) { - require := require.New(t) - - bytes, err := key.Sign(tt.msg) - require.NoError(err) - require.Equal(tt.sig, bytes) - }) - } -} diff --git a/chain/avalanche/utils/crypto/secp256k1/test_keys.go b/chain/avalanche/utils/crypto/secp256k1/test_keys.go deleted file mode 100644 index 030db3b7f..000000000 --- a/chain/avalanche/utils/crypto/secp256k1/test_keys.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package secp256k1 - -import ( - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/cb58" -) - -func TestKeys() []*PrivateKey { - var ( - keyStrings = []string{ - "24jUJ9vZexUM6expyMcT48LBx27k1m7xpraoV62oSQAHdziao5", - "2MMvUMsxx6zsHSNXJdFD8yc5XkancvwyKPwpw4xUK3TCGDuNBY", - "cxb7KpGWhDMALTjNNSJ7UQkkomPesyWAPUaWRGdyeBNzR6f35", - "ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN", - "2RWLv6YVEXDiWLpaCbXhhqxtLbnFaKQsWPSSMSPhpWo47uJAeV", - } - keys = make([]*PrivateKey, len(keyStrings)) - factory = Factory{} - ) - - for i, key := range keyStrings { - privKeyBytes, err := cb58.Decode(key) - if err != nil { - panic(err) - } - - keys[i], err = factory.ToPrivateKey(privKeyBytes) - if err != nil { - panic(err) - } - } - return keys -} diff --git a/chain/avalanche/utils/format.go b/chain/avalanche/utils/format.go deleted file mode 100644 index e2d1a5d6b..000000000 --- a/chain/avalanche/utils/format.go +++ /dev/null @@ -1,31 +0,0 @@ -package utils - -import ( - "errors" - "fmt" - - "github.com/cosmos/btcutil/bech32" -) - -var ( - errBits8To5 = errors.New("unable to convert address from 8-bit to 5-bit formatting") -) - -// FormatBech32 takes an address's bytes as input and returns a bech32 address -func FormatBech32(hrp string, payload []byte) (string, error) { - fiveBits, err := bech32.ConvertBits(payload, 8, 5, true) - if err != nil { - return "", errBits8To5 - } - return bech32.Encode(hrp, fiveBits) -} - -// Format takes in a chain prefix, HRP, and byte slice to produce a string for -// an address. -func Format(chainIDAlias string, hrp string, addr []byte) (string, error) { - addrStr, err := FormatBech32(hrp, addr) - if err != nil { - return "", err - } - return fmt.Sprintf("%s-%s", chainIDAlias, addrStr), nil -} diff --git a/chain/avalanche/utils/hashing/consistent/hashable.go b/chain/avalanche/utils/hashing/consistent/hashable.go deleted file mode 100644 index df4a08d0b..000000000 --- a/chain/avalanche/utils/hashing/consistent/hashable.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package consistent - -// Hashable is an interface to be implemented by structs that need to be sharded via consistent hashing. -type Hashable interface { - // ConsistentHashKey is the key used to shard the blob. - // This should be constant for a given blob. - ConsistentHashKey() []byte -} diff --git a/chain/avalanche/utils/hashing/consistent/ring.go b/chain/avalanche/utils/hashing/consistent/ring.go deleted file mode 100644 index dc818681f..000000000 --- a/chain/avalanche/utils/hashing/consistent/ring.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package consistent - -import ( - "errors" - "sync" - - "github.com/google/btree" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" -) - -var ( - _ Ring = (*hashRing)(nil) - _ btree.LessFunc[ringItem] = ringItem.Less - - errEmptyRing = errors.New("ring doesn't have any members") -) - -// Ring is an interface for a consistent hashing ring -// (ref: https://en.wikipedia.org/wiki/Consistent_hashing). -// -// Consistent hashing is a method of distributing keys across an arbitrary set -// of destinations. -// -// Consider a naive approach, which uses modulo to map a key to a node to serve -// cached requests. Let N be the number of keys and M is the amount of possible -// hashing destinations. Here, a key would be routed to a node based on -// h(key) % M = node assigned. -// -// With this approach, we can route a key to a node in O(1) time, but this -// results in cache misses as nodes are introduced and removed, which results in -// the keys being reshuffled across nodes, as modulo's output changes with M. -// This approach results in O(N) keys being shuffled, which is undesirable for a -// caching use-case. -// -// Consistent hashing works by hashing all keys into a circle, which can be -// visualized as a clock. Keys are routed to a node by hashing the key, and -// searching for the first clockwise neighbor. This requires O(N) memory to -// maintain the state of the ring and log(N) time to route a key using -// binary-search, but results in O(N/M) amount of keys being shuffled when a -// node is added/removed because the addition/removal of a node results in a -// split/merge of its counter-clockwise neighbor's hash space. -// -// As an example, assume we have a ring that supports hashes from 1-12. -// -// 12 -// 11 1 -// -// 10 2 -// -// 9 3 -// -// 8 4 -// -// 7 5 -// 6 -// -// Add node 1 (n1). Let h(n1) = 12. -// First, we compute the hash the node, and insert it into its corresponding -// location on the ring. -// -// 12 (n1) -// 11 1 -// -// 10 2 -// -// 9 3 -// -// 8 4 -// -// 7 5 -// 6 -// -// Now, to see which node a key (k1) should map to, we hash the key and search -// for its closest clockwise neighbor. -// Let h(k1) = 3. Here, we see that since n1 is the closest neighbor, as there -// are no other nodes in the ring. -// -// 12 (n1) -// 11 1 -// -// 10 2 -// -// 9 3 (k1) -// -// 8 4 -// -// 7 5 -// 6 -// -// Now, let's insert another node (n2), such that h(n2) = 6. -// Here we observe that k1 has shuffled to n2, as n2 is the closest clockwise -// neighbor to k1. -// -// 12 (n1) -// 11 1 -// -// 10 2 -// -// 9 3 (k1) -// -// 8 4 -// -// 7 5 -// 6 (n2) -// -// Other optimizations can be made to help reduce blast radius of failures and -// the variance in keys (hot shards). One such optimization is introducing -// virtual nodes, which is to replicate a single node multiple times by salting -// it, (e.g n1-0, n1-1...). -// -// Without virtualization, failures of a node cascade as each node failing -// results in the load of the failed node being shuffled into its clockwise -// neighbor, which can result in a snowball effect across the network. -type Ring interface { - RingReader - ringMutator -} - -// RingReader is an interface to read values from Ring. -type RingReader interface { - // Get gets the closest clockwise node for a key in the ring. - // - // Each ring member is responsible for the hashes which fall in the range - // between (myself, clockwise-neighbor]. - // This behavior is desirable so that we can re-use the return value of Get - // to iterate around the ring (Ex. replication, retries, etc). - // - // Returns the node routed to and an error if we're unable to resolve a node - // to map to. - Get(Hashable) (Hashable, error) -} - -// ringMutator defines an interface that mutates Ring. -type ringMutator interface { - // Add adds a node to the ring. - Add(Hashable) - - // Remove removes the node from the ring. - // - // Returns true if the node was removed, and false if it wasn't present to - // begin with. - Remove(Hashable) bool -} - -// hashRing is an implementation of Ring -type hashRing struct { - // Hashing algorithm to use when hashing keys. - hasher hashing.Hasher - - // Replication factor for nodes; must be greater than zero. - virtualNodes int - - lock sync.RWMutex - ring *btree.BTreeG[ringItem] -} - -// RingConfig configures settings for a Ring. -type RingConfig struct { - // Replication factor for nodes in the ring. - VirtualNodes int - // Hashing implementation to use. - Hasher hashing.Hasher - // Degree represents the degree of the b-tree - Degree int -} - -// NewHashRing instantiates an instance of hashRing. -func NewHashRing(config RingConfig) Ring { - return &hashRing{ - hasher: config.Hasher, - virtualNodes: config.VirtualNodes, - ring: btree.NewG(config.Degree, ringItem.Less), - } -} - -func (h *hashRing) Get(key Hashable) (Hashable, error) { - h.lock.RLock() - defer h.lock.RUnlock() - - return h.get(key) -} - -func (h *hashRing) get(key Hashable) (Hashable, error) { - // If we have no members in the ring, it's not possible to find where the - // key belongs. - if h.ring.Len() == 0 { - return nil, errEmptyRing - } - - var ( - // Compute this key's hash - hash = h.hasher.Hash(key.ConsistentHashKey()) - result Hashable - ) - h.ring.AscendGreaterOrEqual( - ringItem{ - hash: hash, - value: key, - }, - func(item ringItem) bool { - if hash < item.hash { - result = item.value - return false - } - return true - }, - ) - - // If found nothing ascending the tree, we need to wrap around the ring to - // the left-most (min) node. - if result == nil { - min, _ := h.ring.Min() - result = min.value - } - return result, nil -} - -func (h *hashRing) Add(key Hashable) { - h.lock.Lock() - defer h.lock.Unlock() - - h.add(key) -} - -func (h *hashRing) add(key Hashable) { - // Replicate the node in the ring. - hashKey := key.ConsistentHashKey() - for i := 0; i < h.virtualNodes; i++ { - virtualNode := getHashKey(hashKey, i) - virtualNodeHash := h.hasher.Hash(virtualNode) - - // Insert it into the ring. - h.ring.ReplaceOrInsert(ringItem{ - hash: virtualNodeHash, - value: key, - }) - } -} - -func (h *hashRing) Remove(key Hashable) bool { - h.lock.Lock() - defer h.lock.Unlock() - - return h.remove(key) -} - -func (h *hashRing) remove(key Hashable) bool { - var ( - hashKey = key.ConsistentHashKey() - removed = false - ) - - // We need to delete all virtual nodes created for a single node. - for i := 0; i < h.virtualNodes; i++ { - virtualNode := getHashKey(hashKey, i) - virtualNodeHash := h.hasher.Hash(virtualNode) - item := ringItem{ - hash: virtualNodeHash, - } - _, removed = h.ring.Delete(item) - } - return removed -} - -// getHashKey builds a key given a base key and a virtual node number. -func getHashKey(key []byte, virtualNode int) []byte { - return append(key, byte(virtualNode)) -} - -// ringItem is a helper class to represent ring nodes in the b-tree. -type ringItem struct { - hash uint64 - value Hashable -} - -func (r ringItem) Less(than ringItem) bool { - return r.hash < than.hash -} diff --git a/chain/avalanche/utils/hashing/hasher.go b/chain/avalanche/utils/hashing/hasher.go deleted file mode 100644 index 7519dfbb6..000000000 --- a/chain/avalanche/utils/hashing/hasher.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package hashing - -// Hasher is an interface to compute a hash value. -type Hasher interface { - // Hash takes a string and computes its hash value. - // Values must be computed deterministically. - Hash([]byte) uint64 -} diff --git a/chain/avalanche/utils/hashing/hashing.go b/chain/avalanche/utils/hashing/hashing.go deleted file mode 100644 index a74ef8fe0..000000000 --- a/chain/avalanche/utils/hashing/hashing.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package hashing - -import ( - "crypto/sha256" - "fmt" - "io" - - "golang.org/x/crypto/ripemd160" -) - -const ( - HashLen = sha256.Size - AddrLen = ripemd160.Size -) - -// Hash256 A 256 bit long hash value. -type Hash256 = [HashLen]byte - -// Hash160 A 160 bit long hash value. -type Hash160 = [ripemd160.Size]byte - -// ComputeHash256Array computes a cryptographically strong 256 bit hash of the -// input byte slice. -func ComputeHash256Array(buf []byte) Hash256 { - return sha256.Sum256(buf) -} - -// ComputeHash256 computes a cryptographically strong 256 bit hash of the input -// byte slice. -func ComputeHash256(buf []byte) []byte { - arr := ComputeHash256Array(buf) - return arr[:] -} - -// ComputeHash256Ranges computes a cryptographically strong 256 bit hash of the input -// byte slice in the ranges specified. -// Example: -// ComputeHash256Ranges({1, 2, 4, 8, 16}, {{1, 2}, {3, 5}}) is equivalent to -// ComputeHash256({2, 8, 16}). -func ComputeHash256Ranges(buf []byte, ranges [][2]int) []byte { - hashBuilder := sha256.New() - for _, r := range ranges { - _, err := hashBuilder.Write(buf[r[0]:r[1]]) - if err != nil { - panic(err) - } - } - return hashBuilder.Sum(nil) -} - -// ComputeHash160Array computes a cryptographically strong 160 bit hash of the -// input byte slice. -func ComputeHash160Array(buf []byte) Hash160 { - h, err := ToHash160(ComputeHash160(buf)) - if err != nil { - panic(err) - } - return h -} - -// ComputeHash160 computes a cryptographically strong 160 bit hash of the input -// byte slice. -func ComputeHash160(buf []byte) []byte { - ripe := ripemd160.New() - _, err := io.Writer(ripe).Write(buf) - if err != nil { - panic(err) - } - return ripe.Sum(nil) -} - -// Checksum creates a checksum of [length] bytes from the 256 bit hash of the -// byte slice. -// -// Returns: the lower [length] bytes of the hash -// Panics if length > 32. -func Checksum(bytes []byte, length int) []byte { - hash := ComputeHash256Array(bytes) - return hash[len(hash)-length:] -} - -func ToHash256(bytes []byte) (Hash256, error) { - hash := Hash256{} - if bytesLen := len(bytes); bytesLen != HashLen { - return hash, fmt.Errorf("expected 32 bytes but got %d", bytesLen) - } - copy(hash[:], bytes) - return hash, nil -} - -func ToHash160(bytes []byte) (Hash160, error) { - hash := Hash160{} - if bytesLen := len(bytes); bytesLen != ripemd160.Size { - return hash, fmt.Errorf("expected 20 bytes but got %d", bytesLen) - } - copy(hash[:], bytes) - return hash, nil -} - -func PubkeyBytesToAddress(key []byte) []byte { - return ComputeHash160(ComputeHash256(key)) -} diff --git a/chain/avalanche/utils/hashing/mock_hasher.go b/chain/avalanche/utils/hashing/mock_hasher.go deleted file mode 100644 index b903ba8fb..000000000 --- a/chain/avalanche/utils/hashing/mock_hasher.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ava-labs/avalanchego/utils/hashing (interfaces: Hasher) - -// Package hashing is a generated GoMock package. -package hashing - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockHasher is a mock of Hasher interface. -type MockHasher struct { - ctrl *gomock.Controller - recorder *MockHasherMockRecorder -} - -// MockHasherMockRecorder is the mock recorder for MockHasher. -type MockHasherMockRecorder struct { - mock *MockHasher -} - -// NewMockHasher creates a new mock instance. -func NewMockHasher(ctrl *gomock.Controller) *MockHasher { - mock := &MockHasher{ctrl: ctrl} - mock.recorder = &MockHasherMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockHasher) EXPECT() *MockHasherMockRecorder { - return m.recorder -} - -// Hash mocks base method. -func (m *MockHasher) Hash(arg0 []byte) uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Hash", arg0) - ret0, _ := ret[0].(uint64) - return ret0 -} - -// Hash indicates an expected call of Hash. -func (mr *MockHasherMockRecorder) Hash(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Hash", reflect.TypeOf((*MockHasher)(nil).Hash), arg0) -} diff --git a/chain/avalanche/utils/ids/aliases.go b/chain/avalanche/utils/ids/aliases.go deleted file mode 100644 index f0f101393..000000000 --- a/chain/avalanche/utils/ids/aliases.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "fmt" - "sync" -) - -// AliaserReader allows one to lookup the aliases given to an ID. -type AliaserReader interface { - Lookup(alias string) (ID, error) - PrimaryAlias(id ID) (string, error) - Aliases(id ID) ([]string, error) -} - -// Aliaser allows one to give an ID aliases. An ID can have arbitrarily many -// aliases; two IDs may not have the same alias. -type AliaserWriter interface { - Alias(id ID, alias string) error - RemoveAliases(id ID) -} - -// Aliaser allows one to give an ID aliases and lookup the aliases given to an -// ID. -type Aliaser interface { - AliaserReader - AliaserWriter - PrimaryAliasOrDefault(id ID) string -} - -type aliaser struct { - lock sync.RWMutex - dealias map[string]ID - aliases map[ID][]string -} - -func NewAliaser() Aliaser { - return &aliaser{ - dealias: make(map[string]ID), - aliases: make(map[ID][]string), - } -} - -// Lookup returns the ID associated with alias -func (a *aliaser) Lookup(alias string) (ID, error) { - a.lock.RLock() - defer a.lock.RUnlock() - - if id, ok := a.dealias[alias]; ok { - return id, nil - } - return ID{}, fmt.Errorf("there is no ID with alias %s", alias) -} - -// PrimaryAlias returns the first alias of [id] -func (a *aliaser) PrimaryAlias(id ID) (string, error) { - a.lock.RLock() - defer a.lock.RUnlock() - - aliases := a.aliases[id] - if len(aliases) == 0 { - return "", fmt.Errorf("there is no alias for ID %s", id) - } - return aliases[0], nil -} - -// PrimaryAliasOrDefault returns the first alias of [id], or ID string as default -func (a *aliaser) PrimaryAliasOrDefault(id ID) string { - alias, err := a.PrimaryAlias(id) - if err != nil { - return id.String() - } - return alias -} - -// Aliases returns the aliases of an ID -func (a *aliaser) Aliases(id ID) ([]string, error) { - a.lock.RLock() - defer a.lock.RUnlock() - - return a.aliases[id], nil -} - -// Alias gives [id] the alias [alias] -func (a *aliaser) Alias(id ID, alias string) error { - a.lock.Lock() - defer a.lock.Unlock() - - if _, exists := a.dealias[alias]; exists { - return fmt.Errorf("%s is already used as an alias for an ID", alias) - } - - a.dealias[alias] = id - a.aliases[id] = append(a.aliases[id], alias) - return nil -} - -// RemoveAliases of the provided ID -func (a *aliaser) RemoveAliases(id ID) { - a.lock.Lock() - defer a.lock.Unlock() - - aliases := a.aliases[id] - delete(a.aliases, id) - for _, alias := range aliases { - delete(a.dealias, alias) - } -} - -// GetRelevantAliases returns the aliases with the redundant identity alias -// removed (each id is aliased to at least itself). -func GetRelevantAliases(aliaser Aliaser, ids []ID) (map[ID][]string, error) { - result := make(map[ID][]string, len(ids)) - for _, id := range ids { - aliases, err := aliaser.Aliases(id) - if err != nil { - return nil, err - } - - // remove the redundant alias where alias = id. - relevantAliases := make([]string, 0, len(aliases)-1) - for _, alias := range aliases { - if alias != id.String() { - relevantAliases = append(relevantAliases, alias) - } - } - result[id] = relevantAliases - } - return result, nil -} diff --git a/chain/avalanche/utils/ids/aliases_test.go b/chain/avalanche/utils/ids/aliases_test.go deleted file mode 100644 index 624d40eb1..000000000 --- a/chain/avalanche/utils/ids/aliases_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestAliaser(t *testing.T) { - require := require.New(t) - for _, test := range AliasTests { - aliaser := NewAliaser() - test(require, aliaser, aliaser) - } -} - -func TestPrimaryAliasOrDefaultTest(t *testing.T) { - require := require.New(t) - aliaser := NewAliaser() - id1 := ID{'J', 'a', 'm', 'e', 's', ' ', 'G', 'o', 'r', 'd', 'o', 'n'} - id2 := ID{'B', 'r', 'u', 'c', 'e', ' ', 'W', 'a', 'y', 'n', 'e'} - err := aliaser.Alias(id2, "Batman") - require.NoError(err) - - err = aliaser.Alias(id2, "Dark Knight") - require.NoError(err) - - res := aliaser.PrimaryAliasOrDefault(id1) - require.Equal(res, id1.String()) - - expected := "Batman" - res = aliaser.PrimaryAliasOrDefault(id2) - require.NoError(err) - require.Equal(expected, res) -} diff --git a/chain/avalanche/utils/ids/bits.go b/chain/avalanche/utils/ids/bits.go deleted file mode 100644 index a884578f4..000000000 --- a/chain/avalanche/utils/ids/bits.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "bytes" - "math/bits" -) - -// NumBits is the number of bits this patricia tree manages -const NumBits = 256 - -// BitsPerByte is the number of bits per byte -const BitsPerByte = 8 - -// EqualSubset takes in two indices and two ids and returns if the ids are -// equal from bit start to bit end (non-inclusive). Bit indices are defined as: -// [7 6 5 4 3 2 1 0] [15 14 13 12 11 10 9 8] ... [255 254 253 252 251 250 249 248] -// Where index 7 is the MSB of byte 0. -func EqualSubset(start, stop int, id1, id2 ID) bool { - stop-- - if start > stop || stop < 0 { - return true - } - if stop >= NumBits { - return false - } - - startIndex := start / BitsPerByte - stopIndex := stop / BitsPerByte - - // If there is a series of bytes between the first byte and the last byte, they must be equal - if startIndex+1 < stopIndex && !bytes.Equal(id1[startIndex+1:stopIndex], id2[startIndex+1:stopIndex]) { - return false - } - - startBit := uint(start % BitsPerByte) // Index in the byte that the first bit is at - stopBit := uint(stop % BitsPerByte) // Index in the byte that the last bit is at - - startMask := -1 << startBit // 111...0... The number of 0s is equal to startBit - stopMask := (1 << (stopBit + 1)) - 1 // 000...1... The number of 1s is equal to stopBit+1 - - if startIndex == stopIndex { - // If we are looking at the same byte, both masks need to be applied - mask := startMask & stopMask - - // The index here could be startIndex or stopIndex, as they are equal - b1 := mask & int(id1[startIndex]) - b2 := mask & int(id2[startIndex]) - - return b1 == b2 - } - - start1 := startMask & int(id1[startIndex]) - start2 := startMask & int(id2[startIndex]) - - stop1 := stopMask & int(id1[stopIndex]) - stop2 := stopMask & int(id2[stopIndex]) - - return start1 == start2 && stop1 == stop2 -} - -// FirstDifferenceSubset takes in two indices and two ids and returns the index -// of the first difference between the ids inside bit start to bit end -// (non-inclusive). Bit indices are defined above -func FirstDifferenceSubset(start, stop int, id1, id2 ID) (int, bool) { - stop-- - if start > stop || stop < 0 || stop >= NumBits { - return 0, false - } - - startIndex := start / BitsPerByte - stopIndex := stop / BitsPerByte - - startBit := uint(start % BitsPerByte) // Index in the byte that the first bit is at - stopBit := uint(stop % BitsPerByte) // Index in the byte that the last bit is at - - startMask := -1 << startBit // 111...0... The number of 0s is equal to startBit - stopMask := (1 << (stopBit + 1)) - 1 // 000...1... The number of 1s is equal to stopBit+1 - - if startIndex == stopIndex { - // If we are looking at the same byte, both masks need to be applied - mask := startMask & stopMask - - // The index here could be startIndex or stopIndex, as they are equal - b1 := mask & int(id1[startIndex]) - b2 := mask & int(id2[startIndex]) - - if b1 == b2 { - return 0, false - } - - bd := b1 ^ b2 - return bits.TrailingZeros8(uint8(bd)) + startIndex*BitsPerByte, true - } - - // Check the first byte, may have some bits masked - start1 := startMask & int(id1[startIndex]) - start2 := startMask & int(id2[startIndex]) - - if start1 != start2 { - bd := start1 ^ start2 - return bits.TrailingZeros8(uint8(bd)) + startIndex*BitsPerByte, true - } - - // Check all the interior bits - for i := startIndex + 1; i < stopIndex; i++ { - b1 := int(id1[i]) - b2 := int(id2[i]) - if b1 != b2 { - bd := b1 ^ b2 - return bits.TrailingZeros8(uint8(bd)) + i*BitsPerByte, true - } - } - - // Check the last byte, may have some bits masked - stop1 := stopMask & int(id1[stopIndex]) - stop2 := stopMask & int(id2[stopIndex]) - - if stop1 != stop2 { - bd := stop1 ^ stop2 - return bits.TrailingZeros8(uint8(bd)) + stopIndex*BitsPerByte, true - } - - // No difference was found - return 0, false -} diff --git a/chain/avalanche/utils/ids/bits_test.go b/chain/avalanche/utils/ids/bits_test.go deleted file mode 100644 index 429da1004..000000000 --- a/chain/avalanche/utils/ids/bits_test.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "fmt" - "math" - "math/rand" - "strings" - "testing" - "time" -) - -func flip(b uint8) uint8 { - b = b>>4 | b<<4 - b = (b&0xCC)>>2 | (b&0x33)<<2 - b = (b&0xAA)>>1 | (b&0x55)<<1 - return b -} - -func BitString(id ID) string { - sb := strings.Builder{} - for _, b := range id { - sb.WriteString(fmt.Sprintf("%08b", flip(b))) - } - return sb.String() -} - -func Check(start, stop int, id1, id2 ID) bool { - s1 := BitString(id1) - s2 := BitString(id2) - - shorts1 := s1[start:stop] - shorts2 := s2[start:stop] - - return shorts1 == shorts2 -} - -func TestEqualSubsetEarlyStop(t *testing.T) { - id1 := ID{0xf0, 0x0f} - id2 := ID{0xf0, 0x1f} - - if !EqualSubset(0, 12, id1, id2) { - t.Fatalf("Should have passed: %08b %08b == %08b %08b", id1[0], id1[1], id2[0], id2[1]) - } else if EqualSubset(0, 13, id1, id2) { - t.Fatalf("Should not have passed: %08b %08b == %08b %08b", id1[0], id1[1], id2[0], id2[1]) - } -} - -func TestEqualSubsetLateStart(t *testing.T) { - id1 := ID{0x1f, 0xf8} - id2 := ID{0x10, 0x08} - - if !EqualSubset(4, 12, id1, id2) { - t.Fatalf("Should have passed: %08b %08b == %08b %08b", id1[0], id1[1], id2[0], id2[1]) - } -} - -func TestEqualSubsetSameByte(t *testing.T) { - id1 := ID{0x18} - id2 := ID{0xfc} - - if !EqualSubset(3, 5, id1, id2) { - t.Fatalf("Should have passed: %08b == %08b", id1[0], id2[0]) - } -} - -func TestEqualSubsetBadMiddle(t *testing.T) { - id1 := ID{0x18, 0xe8, 0x55} - id2 := ID{0x18, 0x8e, 0x55} - - if EqualSubset(0, 8*3, id1, id2) { - t.Fatalf("Should not have passed: %08b == %08b", id1[1], id2[1]) - } -} - -func TestEqualSubsetAll3Bytes(t *testing.T) { - rand.Seed(time.Now().UnixNano()) - seed := uint64(rand.Int63()) // #nosec G404 - id1 := ID{}.Prefix(seed) - - for i := 0; i < BitsPerByte; i++ { - for j := i; j < BitsPerByte; j++ { - for k := j; k < BitsPerByte; k++ { - id2 := ID{uint8(i), uint8(j), uint8(k)} - - for start := 0; start < BitsPerByte*3; start++ { - for end := start; end <= BitsPerByte*3; end++ { - if EqualSubset(start, end, id1, id2) != Check(start, end, id1, id2) { - t.Fatalf("Subset failed on seed %d:\ns = %d\ne = %d\n%08b %08b %08b == %08b %08b %08b", - seed, start, end, - id1[0], id1[1], id1[2], - id2[0], id2[1], id2[2]) - } - } - } - } - } - } -} - -func TestEqualSubsetOutOfBounds(t *testing.T) { - id1 := ID{0x18, 0xe8, 0x55} - id2 := ID{0x18, 0x8e, 0x55} - - if EqualSubset(0, math.MaxInt32, id1, id2) { - t.Fatalf("Should not have passed") - } -} - -func TestFirstDifferenceSubsetEarlyStop(t *testing.T) { - id1 := ID{0xf0, 0x0f} - id2 := ID{0xf0, 0x1f} - - if _, found := FirstDifferenceSubset(0, 12, id1, id2); found { - t.Fatalf("Shouldn't have found a difference: %08b %08b == %08b %08b", id1[0], id1[1], id2[0], id2[1]) - } else if index, found := FirstDifferenceSubset(0, 13, id1, id2); !found { - t.Fatalf("Should have found a difference: %08b %08b == %08b %08b", id1[0], id1[1], id2[0], id2[1]) - } else if index != 12 { - t.Fatalf("Found a difference at index %d expected %d: %08b %08b == %08b %08b", index, 12, id1[0], id1[1], id2[0], id2[1]) - } -} - -func TestFirstDifferenceEqualByte4(t *testing.T) { - id1 := ID{0x10} - id2 := ID{0x00} - - if _, found := FirstDifferenceSubset(0, 4, id1, id2); found { - t.Fatalf("Shouldn't have found a difference: %08b == %08b", id1[0], id2[0]) - } else if index, found := FirstDifferenceSubset(0, 5, id1, id2); !found { - t.Fatalf("Should have found a difference: %08b == %08b", id1[0], id2[0]) - } else if index != 4 { - t.Fatalf("Found a difference at index %d expected %d: %08b == %08b", index, 4, id1[0], id2[0]) - } -} - -func TestFirstDifferenceEqualByte5(t *testing.T) { - id1 := ID{0x20} - id2 := ID{0x00} - - if _, found := FirstDifferenceSubset(0, 5, id1, id2); found { - t.Fatalf("Shouldn't have found a difference: %08b == %08b", id1[0], id2[0]) - } else if index, found := FirstDifferenceSubset(0, 6, id1, id2); !found { - t.Fatalf("Should have found a difference: %08b == %08b", id1[0], id2[0]) - } else if index != 5 { - t.Fatalf("Found a difference at index %d expected %d: %08b == %08b", index, 5, id1[0], id2[0]) - } -} - -func TestFirstDifferenceSubsetMiddle(t *testing.T) { - id1 := ID{0xf0, 0x0f, 0x11} - id2 := ID{0xf0, 0x1f, 0xff} - - if index, found := FirstDifferenceSubset(0, 24, id1, id2); !found { - t.Fatalf("Should have found a difference: %08b %08b %08b == %08b %08b %08b", id1[0], id1[1], id1[2], id2[0], id2[1], id2[2]) - } else if index != 12 { - t.Fatalf("Found a difference at index %d expected %d: %08b %08b %08b == %08b %08b %08b", index, 12, id1[0], id1[1], id1[2], id2[0], id2[1], id2[2]) - } -} - -func TestFirstDifferenceStartMiddle(t *testing.T) { - id1 := ID{0x1f, 0x0f, 0x11} - id2 := ID{0x0f, 0x1f, 0xff} - - if index, found := FirstDifferenceSubset(0, 24, id1, id2); !found { - t.Fatalf("Should have found a difference: %08b %08b %08b == %08b %08b %08b", id1[0], id1[1], id1[2], id2[0], id2[1], id2[2]) - } else if index != 4 { - t.Fatalf("Found a difference at index %d expected %d: %08b %08b %08b == %08b %08b %08b", index, 4, id1[0], id1[1], id1[2], id2[0], id2[1], id2[2]) - } -} - -func TestFirstDifferenceVacuous(t *testing.T) { - id1 := ID{0xf0, 0x0f, 0x11} - id2 := ID{0xf0, 0x1f, 0xff} - - if _, found := FirstDifferenceSubset(0, 0, id1, id2); found { - t.Fatalf("Shouldn't have found a difference") - } -} diff --git a/chain/avalanche/utils/ids/id.go b/chain/avalanche/utils/ids/id.go deleted file mode 100644 index ab7d78863..000000000 --- a/chain/avalanche/utils/ids/id.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "bytes" - "encoding/hex" - "errors" - "fmt" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/cb58" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/wrappers" -) - -const nullStr = "null" - -var ( - // Empty is a useful all zero value - Empty = ID{} - - errMissingQuotes = errors.New("first and last characters should be quotes") - - _ utils.Sortable[ID] = ID{} -) - -// ID wraps a 32 byte hash used as an identifier -type ID [32]byte - -// ToID attempt to convert a byte slice into an id -func ToID(bytes []byte) (ID, error) { - return hashing.ToHash256(bytes) -} - -// FromString is the inverse of ID.String() -func FromString(idStr string) (ID, error) { - bytes, err := cb58.Decode(idStr) - if err != nil { - return ID{}, err - } - return ToID(bytes) -} - -func (id ID) MarshalJSON() ([]byte, error) { - str, err := cb58.Encode(id[:]) - if err != nil { - return nil, err - } - return []byte("\"" + str + "\""), nil -} - -func (id *ID) UnmarshalJSON(b []byte) error { - str := string(b) - if str == nullStr { // If "null", do nothing - return nil - } else if len(str) < 2 { - return errMissingQuotes - } - - lastIndex := len(str) - 1 - if str[0] != '"' || str[lastIndex] != '"' { - return errMissingQuotes - } - - // Parse CB58 formatted string to bytes - bytes, err := cb58.Decode(str[1:lastIndex]) - if err != nil { - return fmt.Errorf("couldn't decode ID to bytes: %w", err) - } - *id, err = ToID(bytes) - return err -} - -func (id *ID) UnmarshalText(text []byte) error { - return id.UnmarshalJSON(text) -} - -// Prefix this id to create a more selective id. This can be used to store -// multiple values under the same key. For example: -// prefix1(id) -> confidence -// prefix2(id) -> vertex -// This will return a new id and not modify the original id. -func (id ID) Prefix(prefixes ...uint64) ID { - packer := wrappers.Packer{ - Bytes: make([]byte, len(prefixes)*wrappers.LongLen+hashing.HashLen), - } - - for _, prefix := range prefixes { - packer.PackLong(prefix) - } - packer.PackFixedBytes(id[:]) - - return hashing.ComputeHash256Array(packer.Bytes) -} - -// Bit returns the bit value at the ith index of the byte array. Returns 0 or 1 -func (id ID) Bit(i uint) int { - byteIndex := i / BitsPerByte - bitIndex := i % BitsPerByte - - b := id[byteIndex] - - // b = [7, 6, 5, 4, 3, 2, 1, 0] - - b >>= bitIndex - - // b = [0, ..., bitIndex + 1, bitIndex] - // 1 = [0, 0, 0, 0, 0, 0, 0, 1] - - b &= 1 - - // b = [0, 0, 0, 0, 0, 0, 0, bitIndex] - - return int(b) -} - -// Hex returns a hex encoded string of this id. -func (id ID) Hex() string { - return hex.EncodeToString(id[:]) -} - -func (id ID) String() string { - // We assume that the maximum size of a byte slice that - // can be stringified is at least the length of an ID - s, _ := cb58.Encode(id[:]) - return s -} - -func (id ID) MarshalText() ([]byte, error) { - return []byte(id.String()), nil -} - -func (id ID) Less(other ID) bool { - return bytes.Compare(id[:], other[:]) < 0 -} diff --git a/chain/avalanche/utils/ids/id_test.go b/chain/avalanche/utils/ids/id_test.go deleted file mode 100644 index 83e697c90..000000000 --- a/chain/avalanche/utils/ids/id_test.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "bytes" - "encoding/json" - "reflect" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" -) - -func TestID(t *testing.T) { - id := ID{24} - idCopy := ID{24} - prefixed := id.Prefix(0) - - if id != idCopy { - t.Fatalf("ID.Prefix mutated the ID") - } - if nextPrefix := id.Prefix(0); prefixed != nextPrefix { - t.Fatalf("ID.Prefix not consistent") - } -} - -func TestIDBit(t *testing.T) { - id0 := ID{1 << 0} - id1 := ID{1 << 1} - id2 := ID{1 << 2} - id3 := ID{1 << 3} - id4 := ID{1 << 4} - id5 := ID{1 << 5} - id6 := ID{1 << 6} - id7 := ID{1 << 7} - id8 := ID{0, 1 << 0} - - switch { - case id0.Bit(0) != 1: - t.Fatalf("Wrong bit") - case id1.Bit(1) != 1: - t.Fatalf("Wrong bit") - case id2.Bit(2) != 1: - t.Fatalf("Wrong bit") - case id3.Bit(3) != 1: - t.Fatalf("Wrong bit") - case id4.Bit(4) != 1: - t.Fatalf("Wrong bit") - case id5.Bit(5) != 1: - t.Fatalf("Wrong bit") - case id6.Bit(6) != 1: - t.Fatalf("Wrong bit") - case id7.Bit(7) != 1: - t.Fatalf("Wrong bit") - case id8.Bit(8) != 1: - t.Fatalf("Wrong bit") - } -} - -func TestFromString(t *testing.T) { - id := ID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - idStr := id.String() - id2, err := FromString(idStr) - if err != nil { - t.Fatal(err) - } - if id != id2 { - t.Fatal("Expected FromString to be inverse of String but it wasn't") - } -} - -func TestIDFromStringError(t *testing.T) { - tests := []struct { - in string - }{ - {""}, - {"foo"}, - {"foobar"}, - } - for _, tt := range tests { - t.Run(tt.in, func(t *testing.T) { - _, err := FromString(tt.in) - if err == nil { - t.Error("Unexpected success") - } - }) - } -} - -func TestIDMarshalJSON(t *testing.T) { - tests := []struct { - label string - in ID - out []byte - err error - }{ - {"ID{}", ID{}, []byte("\"11111111111111111111111111111111LpoYY\""), nil}, - { - "ID(\"ava labs\")", - ID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - []byte("\"jvYi6Tn9idMi7BaymUVi9zWjg5tpmW7trfKG1AYJLKZJ2fsU7\""), - nil, - }, - } - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - out, err := tt.in.MarshalJSON() - if err != tt.err { - t.Errorf("Expected err %s, got error %v", tt.err, err) - } else if !bytes.Equal(out, tt.out) { - t.Errorf("got %q, expected %q", out, tt.out) - } - }) - } -} - -func TestIDUnmarshalJSON(t *testing.T) { - tests := []struct { - label string - in []byte - out ID - err error - }{ - {"ID{}", []byte("null"), ID{}, nil}, - { - "ID(\"ava labs\")", - []byte("\"jvYi6Tn9idMi7BaymUVi9zWjg5tpmW7trfKG1AYJLKZJ2fsU7\""), - ID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - nil, - }, - } - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - foo := ID{} - err := foo.UnmarshalJSON(tt.in) - if err != tt.err { - t.Errorf("Expected err %s, got error %v", tt.err, err) - } else if foo != tt.out { - t.Errorf("got %q, expected %q", foo, tt.out) - } - }) - } -} - -func TestIDHex(t *testing.T) { - id := ID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - expected := "617661206c616273000000000000000000000000000000000000000000000000" - actual := id.Hex() - if actual != expected { - t.Fatalf("got %s, expected %s", actual, expected) - } -} - -func TestIDString(t *testing.T) { - tests := []struct { - label string - id ID - expected string - }{ - {"ID{}", ID{}, "11111111111111111111111111111111LpoYY"}, - {"ID{24}", ID{24}, "Ba3mm8Ra8JYYebeZ9p7zw1ayorDbeD1euwxhgzSLsncKqGoNt"}, - } - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - result := tt.id.String() - if result != tt.expected { - t.Errorf("got %q, expected %q", result, tt.expected) - } - }) - } -} - -func TestSortIDs(t *testing.T) { - ids := []ID{ - {'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - {'W', 'a', 'l', 'l', 'e', ' ', 'l', 'a', 'b', 's'}, - {'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - } - utils.Sort(ids) - expected := []ID{ - {'W', 'a', 'l', 'l', 'e', ' ', 'l', 'a', 'b', 's'}, - {'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - {'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - } - if !reflect.DeepEqual(ids, expected) { - t.Fatal("[]ID was not sorted lexographically") - } -} - -func TestIDMapMarshalling(t *testing.T) { - originalMap := map[ID]int{ - {'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'}: 1, - {'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}: 2, - } - mapJSON, err := json.Marshal(originalMap) - if err != nil { - t.Fatal(err) - } - - var unmarshalledMap map[ID]int - err = json.Unmarshal(mapJSON, &unmarshalledMap) - if err != nil { - t.Fatal(err) - } - - if len(originalMap) != len(unmarshalledMap) { - t.Fatalf("wrong map lengths") - } - for originalID, num := range originalMap { - if unmarshalledMap[originalID] != num { - t.Fatalf("map was incorrectly Unmarshalled") - } - } -} - -func TestIDLess(t *testing.T) { - require := require.New(t) - - id1 := ID{} - id2 := ID{} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = ID{1} - id2 = ID{0} - require.False(id1.Less(id2)) - require.True(id2.Less(id1)) - - id1 = ID{1} - id2 = ID{1} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = ID{1, 0} - id2 = ID{1, 2} - require.True(id1.Less(id2)) - require.False(id2.Less(id1)) -} diff --git a/chain/avalanche/utils/ids/node_id.go b/chain/avalanche/utils/ids/node_id.go deleted file mode 100644 index c7dd8108d..000000000 --- a/chain/avalanche/utils/ids/node_id.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "bytes" - "crypto/x509" - "fmt" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" -) - -const NodeIDPrefix = "NodeID-" - -var ( - EmptyNodeID = NodeID{} - - _ utils.Sortable[NodeID] = NodeID{} -) - -type NodeID ShortID - -func (id NodeID) String() string { - return ShortID(id).PrefixedString(NodeIDPrefix) -} - -func (id NodeID) Bytes() []byte { - return id[:] -} - -func (id NodeID) MarshalJSON() ([]byte, error) { - return []byte("\"" + id.String() + "\""), nil -} - -func (id NodeID) MarshalText() ([]byte, error) { - return []byte(id.String()), nil -} - -func (id *NodeID) UnmarshalJSON(b []byte) error { - str := string(b) - if str == nullStr { // If "null", do nothing - return nil - } else if len(str) <= 2+len(NodeIDPrefix) { - return fmt.Errorf("expected NodeID length to be > %d", 2+len(NodeIDPrefix)) - } - - lastIndex := len(str) - 1 - if str[0] != '"' || str[lastIndex] != '"' { - return errMissingQuotes - } - - var err error - *id, err = NodeIDFromString(str[1:lastIndex]) - return err -} - -func (id *NodeID) UnmarshalText(text []byte) error { - return id.UnmarshalJSON(text) -} - -func (id NodeID) Less(other NodeID) bool { - return bytes.Compare(id[:], other[:]) == -1 -} - -// ToNodeID attempt to convert a byte slice into a node id -func ToNodeID(bytes []byte) (NodeID, error) { - nodeID, err := ToShortID(bytes) - return NodeID(nodeID), err -} - -func NodeIDFromCert(cert *x509.Certificate) NodeID { - return hashing.ComputeHash160Array( - hashing.ComputeHash256(cert.Raw), - ) -} - -// NodeIDFromString is the inverse of NodeID.String() -func NodeIDFromString(nodeIDStr string) (NodeID, error) { - asShort, err := ShortFromPrefixedString(nodeIDStr, NodeIDPrefix) - if err != nil { - return NodeID{}, err - } - return NodeID(asShort), nil -} diff --git a/chain/avalanche/utils/ids/node_id_test.go b/chain/avalanche/utils/ids/node_id_test.go deleted file mode 100644 index 52c90c8ec..000000000 --- a/chain/avalanche/utils/ids/node_id_test.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "bytes" - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestNodeIDEquality(t *testing.T) { - id := NodeID{24} - idCopy := NodeID{24} - if id != idCopy { - t.Fatalf("ID.Prefix mutated the ID") - } - id2 := NodeID{} - if id == id2 { - t.Fatal("expected Node IDs to be unequal") - } -} - -func TestNodeIDFromString(t *testing.T) { - id := NodeID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'} - idStr := id.String() - id2, err := NodeIDFromString(idStr) - if err != nil { - t.Fatal(err) - } - if id != id2 { - t.Fatal("Expected FromString to be inverse of String but it wasn't") - } - expected := "NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz" - if idStr != expected { - t.Fatalf("expected %s but got %s", expected, idStr) - } -} - -func TestNodeIDFromStringError(t *testing.T) { - tests := []struct { - in string - }{ - {""}, - {"foo"}, - {"foobar"}, - } - for _, tt := range tests { - t.Run(tt.in, func(t *testing.T) { - _, err := FromString(tt.in) - if err == nil { - t.Error("Unexpected success") - } - }) - } -} - -func TestNodeIDMarshalJSON(t *testing.T) { - tests := []struct { - label string - in NodeID - out []byte - err error - }{ - {"NodeID{}", NodeID{}, []byte("\"NodeID-111111111111111111116DBWJs\""), nil}, - { - "ID(\"ava labs\")", - NodeID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - []byte("\"NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz\""), - nil, - }, - } - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - out, err := tt.in.MarshalJSON() - if err != tt.err { - t.Errorf("Expected err %s, got error %v", tt.err, err) - } else if !bytes.Equal(out, tt.out) { - t.Errorf("got %q, expected %q", out, tt.out) - } - }) - } -} - -func TestNodeIDUnmarshalJSON(t *testing.T) { - tests := []struct { - label string - in []byte - out NodeID - shouldErr bool - }{ - {"NodeID{}", []byte("null"), NodeID{}, false}, - { - "NodeID(\"ava labs\")", - []byte("\"NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz\""), - NodeID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}, - false, - }, - { - "missing start quote", - []byte("NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz\""), - NodeID{}, - true, - }, - { - "missing end quote", - []byte("\"NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz"), - NodeID{}, - true, - }, - { - "NodeID-", - []byte("\"NodeID-\""), - NodeID{}, - true, - }, - { - "NodeID-1", - []byte("\"NodeID-1\""), - NodeID{}, - true, - }, - { - "NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz1", - []byte("\"NodeID-1\""), - NodeID{}, - true, - }, - } - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - foo := NodeID{} - err := foo.UnmarshalJSON(tt.in) - switch { - case err == nil && tt.shouldErr: - t.Errorf("Expected no error but got error %v", err) - case err != nil && !tt.shouldErr: - t.Errorf("unxpected error: %v", err) - case foo != tt.out: - t.Errorf("got %q, expected %q", foo, tt.out) - } - }) - } -} - -func TestNodeIDString(t *testing.T) { - tests := []struct { - label string - id NodeID - expected string - }{ - {"NodeID{}", NodeID{}, "NodeID-111111111111111111116DBWJs"}, - {"NodeID{24}", NodeID{24}, "NodeID-3BuDc2d1Efme5Apba6SJ8w3Tz7qeh6mHt"}, - } - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - result := tt.id.String() - if result != tt.expected { - t.Errorf("got %q, expected %q", result, tt.expected) - } - }) - } -} - -func TestNodeIDMapMarshalling(t *testing.T) { - originalMap := map[NodeID]int{ - {'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'}: 1, - {'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}: 2, - } - mapJSON, err := json.Marshal(originalMap) - if err != nil { - t.Fatal(err) - } - - var unmarshalledMap map[NodeID]int - err = json.Unmarshal(mapJSON, &unmarshalledMap) - if err != nil { - t.Fatal(err) - } - - if len(originalMap) != len(unmarshalledMap) { - t.Fatalf("wrong map lengths") - } - for originalID, num := range originalMap { - if unmarshalledMap[originalID] != num { - t.Fatalf("map was incorrectly Unmarshalled") - } - } -} - -func TestNodeIDLess(t *testing.T) { - require := require.New(t) - - id1 := NodeID{} - id2 := NodeID{} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = NodeID{1} - id2 = NodeID{} - require.False(id1.Less(id2)) - require.True(id2.Less(id1)) - - id1 = NodeID{1} - id2 = NodeID{1} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = NodeID{1} - id2 = NodeID{1, 2} - require.True(id1.Less(id2)) - require.False(id2.Less(id1)) -} diff --git a/chain/avalanche/utils/ids/request_id.go b/chain/avalanche/utils/ids/request_id.go deleted file mode 100644 index 779f819d9..000000000 --- a/chain/avalanche/utils/ids/request_id.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -// RequestID is a unique identifier for an in-flight request pending a response. -type RequestID struct { - // The node this request came from - NodeID NodeID - // The chain this request came from - SourceChainID ID - // The chain the expected response should come from - DestinationChainID ID - // The unique identifier for this request - RequestID uint32 - // The message opcode - Op byte -} diff --git a/chain/avalanche/utils/ids/short.go b/chain/avalanche/utils/ids/short.go deleted file mode 100644 index b8d2be73b..000000000 --- a/chain/avalanche/utils/ids/short.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "bytes" - "encoding/hex" - "fmt" - "strings" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/cb58" - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" -) - -// ShortEmpty is a useful all zero value -var ( - ShortEmpty = ShortID{} - - _ utils.Sortable[ShortID] = ShortID{} -) - -// ShortID wraps a 20 byte hash as an identifier -type ShortID [20]byte - -// ToShortID attempt to convert a byte slice into an id -func ToShortID(bytes []byte) (ShortID, error) { - return hashing.ToHash160(bytes) -} - -// ShortFromString is the inverse of ShortID.String() -func ShortFromString(idStr string) (ShortID, error) { - bytes, err := cb58.Decode(idStr) - if err != nil { - return ShortID{}, err - } - return ToShortID(bytes) -} - -// ShortFromPrefixedString returns a ShortID assuming the cb58 format is -// prefixed -func ShortFromPrefixedString(idStr, prefix string) (ShortID, error) { - if !strings.HasPrefix(idStr, prefix) { - return ShortID{}, fmt.Errorf("ID: %s is missing the prefix: %s", idStr, prefix) - } - return ShortFromString(strings.TrimPrefix(idStr, prefix)) -} - -func (id ShortID) MarshalJSON() ([]byte, error) { - str, err := cb58.Encode(id[:]) - if err != nil { - return nil, err - } - return []byte("\"" + str + "\""), nil -} - -func (id *ShortID) UnmarshalJSON(b []byte) error { - str := string(b) - if str == nullStr { // If "null", do nothing - return nil - } else if len(str) < 2 { - return errMissingQuotes - } - - lastIndex := len(str) - 1 - if str[0] != '"' || str[lastIndex] != '"' { - return errMissingQuotes - } - - // Parse CB58 formatted string to bytes - bytes, err := cb58.Decode(str[1:lastIndex]) - if err != nil { - return fmt.Errorf("couldn't decode ID to bytes: %w", err) - } - *id, err = ToShortID(bytes) - return err -} - -func (id *ShortID) UnmarshalText(text []byte) error { - return id.UnmarshalJSON(text) -} - -// Bytes returns the 20 byte hash as a slice. It is assumed this slice is not -// modified. -func (id ShortID) Bytes() []byte { - return id[:] -} - -// Hex returns a hex encoded string of this id. -func (id ShortID) Hex() string { - return hex.EncodeToString(id.Bytes()) -} - -func (id ShortID) String() string { - // We assume that the maximum size of a byte slice that - // can be stringified is at least the length of an ID - str, _ := cb58.Encode(id.Bytes()) - return str -} - -// PrefixedString returns the String representation with a prefix added -func (id ShortID) PrefixedString(prefix string) string { - return prefix + id.String() -} - -func (id ShortID) MarshalText() ([]byte, error) { - return []byte(id.String()), nil -} - -func (id ShortID) Less(other ShortID) bool { - return bytes.Compare(id[:], other[:]) == -1 -} - -// ShortIDsToStrings converts an array of shortIDs to an array of their string -// representations -func ShortIDsToStrings(ids []ShortID) []string { - idStrs := make([]string, len(ids)) - for i, id := range ids { - idStrs[i] = id.String() - } - return idStrs -} diff --git a/chain/avalanche/utils/ids/test_aliases.go b/chain/avalanche/utils/ids/test_aliases.go deleted file mode 100644 index 06a7fe3af..000000000 --- a/chain/avalanche/utils/ids/test_aliases.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import "github.com/stretchr/testify/require" - -var AliasTests = []func(require *require.Assertions, r AliaserReader, w AliaserWriter){ - AliaserLookupErrorTest, - AliaserLookupTest, - AliaserAliasesEmptyTest, - AliaserAliasesTest, - AliaserPrimaryAliasTest, - AliaserAliasClashTest, - AliaserRemoveAliasTest, -} - -func AliaserLookupErrorTest(require *require.Assertions, r AliaserReader, _ AliaserWriter) { - _, err := r.Lookup("Batman") - require.Error(err, "expected an error due to missing alias") -} - -func AliaserLookupTest(require *require.Assertions, r AliaserReader, w AliaserWriter) { - id := ID{'K', 'a', 't', 'e', ' ', 'K', 'a', 'n', 'e'} - err := w.Alias(id, "Batwoman") - require.NoError(err) - - res, err := r.Lookup("Batwoman") - require.NoError(err) - require.Equal(id, res) -} - -func AliaserAliasesEmptyTest(require *require.Assertions, r AliaserReader, _ AliaserWriter) { - id := ID{'J', 'a', 'm', 'e', 's', ' ', 'G', 'o', 'r', 'd', 'o', 'n'} - - aliases, err := r.Aliases(id) - require.NoError(err) - require.Empty(aliases) -} - -func AliaserAliasesTest(require *require.Assertions, r AliaserReader, w AliaserWriter) { - id := ID{'B', 'r', 'u', 'c', 'e', ' ', 'W', 'a', 'y', 'n', 'e'} - err := w.Alias(id, "Batman") - require.NoError(err) - - err = w.Alias(id, "Dark Knight") - require.NoError(err) - - aliases, err := r.Aliases(id) - require.NoError(err) - - expected := []string{"Batman", "Dark Knight"} - require.Equal(expected, aliases) -} - -func AliaserPrimaryAliasTest(require *require.Assertions, r AliaserReader, w AliaserWriter) { - id1 := ID{'J', 'a', 'm', 'e', 's', ' ', 'G', 'o', 'r', 'd', 'o', 'n'} - id2 := ID{'B', 'r', 'u', 'c', 'e', ' ', 'W', 'a', 'y', 'n', 'e'} - err := w.Alias(id2, "Batman") - require.NoError(err) - - err = w.Alias(id2, "Dark Knight") - require.NoError(err) - - _, err = r.PrimaryAlias(id1) - require.Error(err) - - expected := "Batman" - res, err := r.PrimaryAlias(id2) - require.NoError(err) - require.Equal(expected, res) -} - -func AliaserAliasClashTest(require *require.Assertions, _ AliaserReader, w AliaserWriter) { - id1 := ID{'B', 'r', 'u', 'c', 'e', ' ', 'W', 'a', 'y', 'n', 'e'} - id2 := ID{'D', 'i', 'c', 'k', ' ', 'G', 'r', 'a', 'y', 's', 'o', 'n'} - err := w.Alias(id1, "Batman") - require.NoError(err) - - err = w.Alias(id2, "Batman") - require.Error(err) -} - -func AliaserRemoveAliasTest(require *require.Assertions, r AliaserReader, w AliaserWriter) { - id1 := ID{'B', 'r', 'u', 'c', 'e', ' ', 'W', 'a', 'y', 'n', 'e'} - id2 := ID{'J', 'a', 'm', 'e', 's', ' ', 'G', 'o', 'r', 'd', 'o', 'n'} - err := w.Alias(id1, "Batman") - require.NoError(err) - - err = w.Alias(id1, "Dark Knight") - require.NoError(err) - - w.RemoveAliases(id1) - - _, err = r.PrimaryAlias(id1) - require.Error(err) - - err = w.Alias(id2, "Batman") - require.NoError(err) - - err = w.Alias(id2, "Dark Knight") - require.NoError(err) - - err = w.Alias(id1, "Dark Night Rises") - require.NoError(err) -} diff --git a/chain/avalanche/utils/ids/test_generator.go b/chain/avalanche/utils/ids/test_generator.go deleted file mode 100644 index 2c1344af8..000000000 --- a/chain/avalanche/utils/ids/test_generator.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package ids - -import ( - "sync/atomic" -) - -var offset = uint64(0) - -// GenerateTestID returns a new ID that should only be used for testing -func GenerateTestID() ID { - return Empty.Prefix(atomic.AddUint64(&offset, 1)) -} - -// GenerateTestShortID returns a new ID that should only be used for testing -func GenerateTestShortID() ShortID { - newID := GenerateTestID() - newShortID, _ := ToShortID(newID[:20]) - return newShortID -} - -// GenerateTestNodeID returns a new ID that should only be used for testing -func GenerateTestNodeID() NodeID { - return NodeID(GenerateTestShortID()) -} diff --git a/chain/avalanche/utils/linkedhashmap/iterator.go b/chain/avalanche/utils/linkedhashmap/iterator.go deleted file mode 100644 index c3beb3495..000000000 --- a/chain/avalanche/utils/linkedhashmap/iterator.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inte. All rights reserved. -// See the file LICENSE for licensing terms. - -package linkedhashmap - -import ( - "container/list" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" -) - -var _ Iter[int, struct{}] = (*iterator[int, struct{}])(nil) - -// Iterates over the keys and values in a LinkedHashmap -// from oldest to newest elements. -// Assumes the underlying LinkedHashmap is not modified while -// the iterator is in use, except to delete elements that -// have already been iterated over. -type Iter[K, V any] interface { - Next() bool - Key() K - Value() V -} - -type iterator[K comparable, V any] struct { - lh *linkedHashmap[K, V] - key K - value V - next *list.Element - initialized, exhausted bool -} - -func (it *iterator[K, V]) Next() bool { - // If the iterator has been exhausted, there is no next value. - if it.exhausted { - it.key = utils.Zero[K]() - it.value = utils.Zero[V]() - it.next = nil - return false - } - - it.lh.lock.RLock() - defer it.lh.lock.RUnlock() - - // If the iterator was not yet initialized, do it now. - if !it.initialized { - it.initialized = true - oldest := it.lh.entryList.Front() - if oldest == nil { - it.exhausted = true - it.key = utils.Zero[K]() - it.value = utils.Zero[V]() - it.next = nil - return false - } - it.next = oldest - } - - // It's important to ensure that [it.next] is not nil - // by not deleting elements that have not yet been iterated - // over from [it.lh] - it.key = it.next.Value.(keyValue[K, V]).key - it.value = it.next.Value.(keyValue[K, V]).value - it.next = it.next.Next() // Next time, return next element - it.exhausted = it.next == nil - return true -} - -func (it *iterator[K, V]) Key() K { - return it.key -} - -func (it *iterator[K, V]) Value() V { - return it.value -} diff --git a/chain/avalanche/utils/linkedhashmap/linkedhashmap.go b/chain/avalanche/utils/linkedhashmap/linkedhashmap.go deleted file mode 100644 index a65d9b7b8..000000000 --- a/chain/avalanche/utils/linkedhashmap/linkedhashmap.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inte. All rights reserved. -// See the file LICENSE for licensing terms. - -package linkedhashmap - -import ( - "container/list" - "sync" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils" -) - -var _ LinkedHashmap[int, struct{}] = (*linkedHashmap[int, struct{}])(nil) - -// Hashmap provides an O(1) mapping from a comparable key to any value. -// Comparable is defined by https://golang.org/ref/spec#Comparison_operators. -type Hashmap[K, V any] interface { - Put(key K, val V) - Get(key K) (val V, exists bool) - Delete(key K) - Len() int -} - -// LinkedHashmap is a hashmap that keeps track of the oldest pairing an the -// newest pairing. -type LinkedHashmap[K, V any] interface { - Hashmap[K, V] - - Oldest() (key K, val V, exists bool) - Newest() (key K, val V, exists bool) - NewIterator() Iter[K, V] -} - -type keyValue[K, V any] struct { - key K - value V -} - -type linkedHashmap[K comparable, V any] struct { - lock sync.RWMutex - entryMap map[K]*list.Element - entryList *list.List -} - -func New[K comparable, V any]() LinkedHashmap[K, V] { - return &linkedHashmap[K, V]{ - entryMap: make(map[K]*list.Element), - entryList: list.New(), - } -} - -func (lh *linkedHashmap[K, V]) Put(key K, val V) { - lh.lock.Lock() - defer lh.lock.Unlock() - - lh.put(key, val) -} - -func (lh *linkedHashmap[K, V]) Get(key K) (V, bool) { - lh.lock.Lock() - defer lh.lock.Unlock() - - return lh.get(key) -} - -func (lh *linkedHashmap[K, V]) Delete(key K) { - lh.lock.Lock() - defer lh.lock.Unlock() - - lh.delete(key) -} - -func (lh *linkedHashmap[K, V]) Len() int { - lh.lock.Lock() - defer lh.lock.Unlock() - - return lh.len() -} - -func (lh *linkedHashmap[K, V]) Oldest() (K, V, bool) { - lh.lock.Lock() - defer lh.lock.Unlock() - - return lh.oldest() -} - -func (lh *linkedHashmap[K, V]) Newest() (K, V, bool) { - lh.lock.Lock() - defer lh.lock.Unlock() - - return lh.newest() -} - -func (lh *linkedHashmap[K, V]) put(key K, value V) { - if e, ok := lh.entryMap[key]; ok { - lh.entryList.MoveToBack(e) - e.Value = keyValue[K, V]{ - key: key, - value: value, - } - } else { - lh.entryMap[key] = lh.entryList.PushBack(keyValue[K, V]{ - key: key, - value: value, - }) - } -} - -func (lh *linkedHashmap[K, V]) get(key K) (V, bool) { - if e, ok := lh.entryMap[key]; ok { - return e.Value.(keyValue[K, V]).value, true - } - return utils.Zero[V](), false -} - -func (lh *linkedHashmap[K, V]) delete(key K) { - if e, ok := lh.entryMap[key]; ok { - lh.entryList.Remove(e) - delete(lh.entryMap, key) - } -} - -func (lh *linkedHashmap[K, V]) len() int { - return len(lh.entryMap) -} - -func (lh *linkedHashmap[K, V]) oldest() (K, V, bool) { - if val := lh.entryList.Front(); val != nil { - return val.Value.(keyValue[K, V]).key, val.Value.(keyValue[K, V]).value, true - } - return utils.Zero[K](), utils.Zero[V](), false -} - -func (lh *linkedHashmap[K, V]) newest() (K, V, bool) { - if val := lh.entryList.Back(); val != nil { - return val.Value.(keyValue[K, V]).key, val.Value.(keyValue[K, V]).value, true - } - return utils.Zero[K](), utils.Zero[V](), false -} - -func (lh *linkedHashmap[K, V]) NewIterator() Iter[K, V] { - return &iterator[K, V]{lh: lh} -} diff --git a/chain/avalanche/utils/linkedhashmap/linkedhashmap_test.go b/chain/avalanche/utils/linkedhashmap/linkedhashmap_test.go deleted file mode 100644 index 2d48c5cc3..000000000 --- a/chain/avalanche/utils/linkedhashmap/linkedhashmap_test.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package linkedhashmap - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/ids" -) - -func TestLinkedHashmap(t *testing.T) { - require := require.New(t) - - lh := New[ids.ID, int]() - require.Equal(0, lh.Len(), "a new hashmap should be empty") - - key0 := ids.GenerateTestID() - _, exists := lh.Get(key0) - require.False(exists, "shouldn't have found the value") - - _, _, exists = lh.Oldest() - require.False(exists, "shouldn't have found a value") - - _, _, exists = lh.Newest() - require.False(exists, "shouldn't have found a value") - - lh.Put(key0, 0) - require.Equal(1, lh.Len(), "wrong hashmap length") - - val0, exists := lh.Get(key0) - require.True(exists, "should have found the value") - require.Equal(0, val0, "wrong value") - - rkey0, val0, exists := lh.Oldest() - require.True(exists, "should have found the value") - require.Equal(key0, rkey0, "wrong key") - require.Equal(0, val0, "wrong value") - - rkey0, val0, exists = lh.Newest() - require.True(exists, "should have found the value") - require.Equal(key0, rkey0, "wrong key") - require.Equal(0, val0, "wrong value") - - key1 := ids.GenerateTestID() - lh.Put(key1, 1) - require.Equal(2, lh.Len(), "wrong hashmap length") - - val1, exists := lh.Get(key1) - require.True(exists, "should have found the value") - require.Equal(1, val1, "wrong value") - - rkey0, val0, exists = lh.Oldest() - require.True(exists, "should have found the value") - require.Equal(key0, rkey0, "wrong key") - require.Equal(0, val0, "wrong value") - - rkey1, val1, exists := lh.Newest() - require.True(exists, "should have found the value") - require.Equal(key1, rkey1, "wrong key") - require.Equal(1, val1, "wrong value") - - lh.Delete(key0) - require.Equal(1, lh.Len(), "wrong hashmap length") - - _, exists = lh.Get(key0) - require.False(exists, "shouldn't have found the value") - - rkey1, val1, exists = lh.Oldest() - require.True(exists, "should have found the value") - require.Equal(rkey1, key1, "wrong key") - require.Equal(1, val1, "wrong value") - - rkey1, val1, exists = lh.Newest() - require.True(exists, "should have found the value") - require.Equal(key1, rkey1, "wrong key") - require.Equal(1, val1, "wrong value") - - lh.Put(key0, 0) - require.Equal(2, lh.Len(), "wrong hashmap length") - - lh.Put(key1, 1) - require.Equal(2, lh.Len(), "wrong hashmap length") - - rkey0, val0, exists = lh.Oldest() - require.True(exists, "should have found the value") - require.Equal(key0, rkey0, "wrong key") - require.Equal(0, val0, "wrong value") - - rkey1, val1, exists = lh.Newest() - require.True(exists, "should have found the value") - require.Equal(key1, rkey1, "wrong key") - require.Equal(1, val1, "wrong value") -} - -func TestIterator(t *testing.T) { - require := require.New(t) - id1, id2, id3 := ids.GenerateTestID(), ids.GenerateTestID(), ids.GenerateTestID() - - // Case: No elements - { - lh := New[ids.ID, int]() - iter := lh.NewIterator() - require.NotNil(iter) - // Should immediately be exhausted - require.False(iter.Next()) - require.False(iter.Next()) - // Should be empty - require.EqualValues(ids.Empty, iter.Key()) - require.Zero(iter.Value()) - } - - // Case: 1 element - { - lh := New[ids.ID, int]() - iter := lh.NewIterator() - require.NotNil(iter) - lh.Put(id1, 1) - require.True(iter.Next()) - require.EqualValues(id1, iter.Key()) - require.EqualValues(1, iter.Value()) - // Should be empty - require.False(iter.Next()) - // Re-assign id1 --> 10 - lh.Put(id1, 10) - iter = lh.NewIterator() // New iterator - require.True(iter.Next()) - require.EqualValues(id1, iter.Key()) - require.EqualValues(10, iter.Value()) - // Should be empty - require.False(iter.Next()) - // Delete id1 - lh.Delete(id1) - iter = lh.NewIterator() - require.NotNil(iter) - // Should immediately be exhausted - require.False(iter.Next()) - } - - // Case: Multiple elements - { - lh := New[ids.ID, int]() - lh.Put(id1, 1) - lh.Put(id2, 2) - lh.Put(id3, 3) - iter := lh.NewIterator() - // Should give back all 3 elements - require.True(iter.Next()) - require.EqualValues(id1, iter.Key()) - require.EqualValues(1, iter.Value()) - require.True(iter.Next()) - require.EqualValues(id2, iter.Key()) - require.EqualValues(2, iter.Value()) - require.True(iter.Next()) - require.EqualValues(id3, iter.Key()) - require.EqualValues(3, iter.Value()) - // Should be exhausted - require.False(iter.Next()) - } - - // Case: Delete element that has been iterated over - { - lh := New[ids.ID, int]() - lh.Put(id1, 1) - lh.Put(id2, 2) - lh.Put(id3, 3) - iter := lh.NewIterator() - require.True(iter.Next()) - require.True(iter.Next()) - lh.Delete(id1) - lh.Delete(id2) - require.True(iter.Next()) - require.EqualValues(id3, iter.Key()) - require.EqualValues(3, iter.Value()) - // Should be exhausted - require.False(iter.Next()) - } -} diff --git a/chain/avalanche/utils/sorting.go b/chain/avalanche/utils/sorting.go deleted file mode 100644 index d0ec98a64..000000000 --- a/chain/avalanche/utils/sorting.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -import ( - "bytes" - "sort" - - "golang.org/x/exp/constraints" - "golang.org/x/exp/slices" - - "github.com/strangelove-ventures/interchaintest/v7/chain/avalanche/utils/hashing" -) - -// TODO can we handle sorting where the Less function relies on a codec? - -type Sortable[T any] interface { - Less(T) bool -} - -// Sorts the elements of [s]. -func Sort[T Sortable[T]](s []T) { - slices.SortFunc(s, func(i, j T) bool { - return i.Less(j) - }) -} - -// Sorts the elements of [s] based on their hashes. -func SortByHash[T ~[]byte](s []T) { - slices.SortFunc(s, func(i, j T) bool { - iHash := hashing.ComputeHash256(i) - jHash := hashing.ComputeHash256(j) - return bytes.Compare(iHash, jHash) == -1 - }) -} - -// Sorts a 2D byte slice. -// Each byte slice is not sorted internally; the byte slices are sorted relative -// to one another. -func SortBytes[T ~[]byte](arr []T) { - slices.SortFunc(arr, func(i, j T) bool { - return bytes.Compare(i, j) == -1 - }) -} - -// Returns true iff the elements in [s] are unique and sorted. -func IsSortedAndUniqueSortable[T Sortable[T]](s []T) bool { - for i := 0; i < len(s)-1; i++ { - if !s[i].Less(s[i+1]) { - return false - } - } - return true -} - -// Returns true iff the elements in [s] are unique and sorted. -func IsSortedAndUniqueOrdered[T constraints.Ordered](s []T) bool { - for i := 0; i < len(s)-1; i++ { - if s[i] >= s[i+1] { - return false - } - } - return true -} - -// Returns true iff the elements in [s] are unique and sorted -// based by their hashes. -func IsSortedAndUniqueByHash[T ~[]byte](s []T) bool { - if len(s) <= 1 { - return true - } - rightHash := hashing.ComputeHash256(s[0]) - for i := 1; i < len(s); i++ { - leftHash := rightHash - rightHash = hashing.ComputeHash256(s[i]) - if bytes.Compare(leftHash, rightHash) != -1 { - return false - } - } - return true -} - -// Returns true iff the elements in [s] are unique. -func IsUnique[T comparable](elts []T) bool { - // Can't use set.Set because it'd be a circular import. - asMap := make(map[T]struct{}, len(elts)) - for _, elt := range elts { - if _, ok := asMap[elt]; ok { - return false - } - asMap[elt] = struct{}{} - } - return true -} - -// IsSortedAndUnique returns true if the elements in the data are unique and -// sorted. -// -// Deprecated: Use one of the other [IsSortedAndUnique...] functions instead. -func IsSortedAndUnique(data sort.Interface) bool { - for i := 0; i < data.Len()-1; i++ { - if !data.Less(i, i+1) { - return false - } - } - return true -} diff --git a/chain/avalanche/utils/tls.go b/chain/avalanche/utils/tls.go deleted file mode 100644 index 3fdf97a28..000000000 --- a/chain/avalanche/utils/tls.go +++ /dev/null @@ -1,68 +0,0 @@ -package utils - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "encoding/pem" - "fmt" - "math/big" - "time" -) - -// Creates a new staking private key / staking certificate pair. -// Returns the PEM byte representations of both. -func NewCertAndKeyBytes() ([]byte, []byte, error) { - // Create key to sign cert with - key, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - return nil, nil, fmt.Errorf("couldn't generate rsa key: %w", err) - } - - // Create self-signed staking cert - certTemplate := &x509.Certificate{ - SerialNumber: big.NewInt(0), - NotBefore: time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC), - NotAfter: time.Now().AddDate(100, 0, 0), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment, - BasicConstraintsValid: true, - } - certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, &key.PublicKey, key) - if err != nil { - return nil, nil, fmt.Errorf("couldn't create certificate: %w", err) - } - var certBuff bytes.Buffer - if err := pem.Encode(&certBuff, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil { - return nil, nil, fmt.Errorf("couldn't write cert file: %w", err) - } - - privBytes, err := x509.MarshalPKCS8PrivateKey(key) - if err != nil { - return nil, nil, fmt.Errorf("couldn't marshal private key: %w", err) - } - - var keyBuff bytes.Buffer - if err := pem.Encode(&keyBuff, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { - return nil, nil, fmt.Errorf("couldn't write private key: %w", err) - } - return certBuff.Bytes(), keyBuff.Bytes(), nil -} - -func NewTLSCert() (*tls.Certificate, error) { - certBytes, keyBytes, err := NewCertAndKeyBytes() - if err != nil { - return nil, err - } - return NewTLSCertFromBytes(certBytes, keyBytes) -} - -func NewTLSCertFromBytes(certBytes, keyBytes []byte) (*tls.Certificate, error) { - cert, err := tls.X509KeyPair(certBytes, keyBytes) - if err != nil { - return nil, err - } - cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) - return &cert, err -} diff --git a/chain/avalanche/utils/wrappers/closers.go b/chain/avalanche/utils/wrappers/closers.go deleted file mode 100644 index d366e928c..000000000 --- a/chain/avalanche/utils/wrappers/closers.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package wrappers - -import ( - "io" - "sync" -) - -// Closer is a nice utility for closing a group of objects while reporting an -// error if one occurs. -type Closer struct { - lock sync.Mutex - closers []io.Closer -} - -// Add a new object to be closed. -func (c *Closer) Add(closer io.Closer) { - c.lock.Lock() - defer c.lock.Unlock() - - c.closers = append(c.closers, closer) -} - -// Close closes each of the closers add to [c] and returns the first error -// that occurs or nil if no error occurs. -func (c *Closer) Close() error { - c.lock.Lock() - closers := c.closers - c.closers = nil - c.lock.Unlock() - - errs := Errs{} - for _, closer := range closers { - errs.Add(closer.Close()) - } - return errs.Err -} diff --git a/chain/avalanche/utils/wrappers/errors.go b/chain/avalanche/utils/wrappers/errors.go deleted file mode 100644 index dab207057..000000000 --- a/chain/avalanche/utils/wrappers/errors.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package wrappers - -import ( - "strings" -) - -var _ error = (*aggregate)(nil) - -type Errs struct{ Err error } - -func (errs *Errs) Errored() bool { - return errs.Err != nil -} - -func (errs *Errs) Add(errors ...error) { - if errs.Err == nil { - for _, err := range errors { - if err != nil { - errs.Err = err - break - } - } - } -} - -// NewAggregate returns an aggregate error from a list of errors -func NewAggregate(errs []error) error { - err := &aggregate{errs} - if len(err.Errors()) == 0 { - return nil - } - return err -} - -type aggregate struct{ errs []error } - -// Error returns the slice of errors with comma separated messsages wrapped in brackets -// [ error string 0 ], [ error string 1 ] ... -func (a *aggregate) Error() string { - errString := make([]string, len(a.errs)) - for i, err := range a.errs { - errString[i] = "[" + err.Error() + "]" - } - return strings.Join(errString, ",") -} - -func (a *aggregate) Errors() []error { - return a.errs -} diff --git a/chain/avalanche/utils/wrappers/packing.go b/chain/avalanche/utils/wrappers/packing.go deleted file mode 100644 index e6869b1b9..000000000 --- a/chain/avalanche/utils/wrappers/packing.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package wrappers - -import ( - "encoding/binary" - "errors" - "math" -) - -const ( - MaxStringLen = math.MaxUint16 - - // ByteLen is the number of bytes per byte... - ByteLen = 1 - // ShortLen is the number of bytes per short - ShortLen = 2 - // IntLen is the number of bytes per int - IntLen = 4 - // LongLen is the number of bytes per long - LongLen = 8 - // BoolLen is the number of bytes per bool - BoolLen = 1 - // IPLen is the number of bytes per IP - IPLen = 16 + ShortLen -) - -func StringLen(str string) int { - // note: there is a max length for string ([MaxStringLen]) - // we defer to PackString checking whether str is within limits - return ShortLen + len(str) -} - -var ( - errBadLength = errors.New("packer has insufficient length for input") - errNegativeOffset = errors.New("negative offset") - errInvalidInput = errors.New("input does not match expected format") - errBadBool = errors.New("unexpected value when unpacking bool") - errOversized = errors.New("size is larger than limit") -) - -// Packer packs and unpacks a byte array from/to standard values -type Packer struct { - Errs - - // The largest allowed size of expanding the byte array - MaxSize int - // The current byte array - Bytes []byte - // The offset that is being written to in the byte array - Offset int -} - -// PackByte append a byte to the byte array -func (p *Packer) PackByte(val byte) { - p.expand(ByteLen) - if p.Errored() { - return - } - - p.Bytes[p.Offset] = val - p.Offset++ -} - -// UnpackByte unpack a byte from the byte array -func (p *Packer) UnpackByte() byte { - p.checkSpace(ByteLen) - if p.Errored() { - return 0 - } - - val := p.Bytes[p.Offset] - p.Offset += ByteLen - return val -} - -// PackShort append a short to the byte array -func (p *Packer) PackShort(val uint16) { - p.expand(ShortLen) - if p.Errored() { - return - } - - binary.BigEndian.PutUint16(p.Bytes[p.Offset:], val) - p.Offset += ShortLen -} - -// UnpackShort unpack a short from the byte array -func (p *Packer) UnpackShort() uint16 { - p.checkSpace(ShortLen) - if p.Errored() { - return 0 - } - - val := binary.BigEndian.Uint16(p.Bytes[p.Offset:]) - p.Offset += ShortLen - return val -} - -// PackInt append an int to the byte array -func (p *Packer) PackInt(val uint32) { - p.expand(IntLen) - if p.Errored() { - return - } - - binary.BigEndian.PutUint32(p.Bytes[p.Offset:], val) - p.Offset += IntLen -} - -// UnpackInt unpack an int from the byte array -func (p *Packer) UnpackInt() uint32 { - p.checkSpace(IntLen) - if p.Errored() { - return 0 - } - - val := binary.BigEndian.Uint32(p.Bytes[p.Offset:]) - p.Offset += IntLen - return val -} - -// PackLong append a long to the byte array -func (p *Packer) PackLong(val uint64) { - p.expand(LongLen) - if p.Errored() { - return - } - - binary.BigEndian.PutUint64(p.Bytes[p.Offset:], val) - p.Offset += LongLen -} - -// UnpackLong unpack a long from the byte array -func (p *Packer) UnpackLong() uint64 { - p.checkSpace(LongLen) - if p.Errored() { - return 0 - } - - val := binary.BigEndian.Uint64(p.Bytes[p.Offset:]) - p.Offset += LongLen - return val -} - -// PackBool packs a bool into the byte array -func (p *Packer) PackBool(b bool) { - if b { - p.PackByte(1) - } else { - p.PackByte(0) - } -} - -// UnpackBool unpacks a bool from the byte array -func (p *Packer) UnpackBool() bool { - b := p.UnpackByte() - switch b { - case 0: - return false - case 1: - return true - default: - p.Add(errBadBool) - return false - } -} - -// PackFixedBytes append a byte slice, with no length descriptor to the byte -// array -func (p *Packer) PackFixedBytes(bytes []byte) { - p.expand(len(bytes)) - if p.Errored() { - return - } - - copy(p.Bytes[p.Offset:], bytes) - p.Offset += len(bytes) -} - -// UnpackFixedBytes unpack a byte slice, with no length descriptor from the byte -// array -func (p *Packer) UnpackFixedBytes(size int) []byte { - p.checkSpace(size) - if p.Errored() { - return nil - } - - bytes := p.Bytes[p.Offset : p.Offset+size] - p.Offset += size - return bytes -} - -// PackBytes append a byte slice to the byte array -func (p *Packer) PackBytes(bytes []byte) { - p.PackInt(uint32(len(bytes))) - p.PackFixedBytes(bytes) -} - -// UnpackBytes unpack a byte slice from the byte array -func (p *Packer) UnpackBytes() []byte { - size := p.UnpackInt() - return p.UnpackFixedBytes(int(size)) -} - -// UnpackLimitedBytes unpacks a byte slice. If the size of the slice is greater -// than [limit], adds [errOversized] to the packer and returns nil. -func (p *Packer) UnpackLimitedBytes(limit uint32) []byte { - size := p.UnpackInt() - if size > limit { - p.Add(errOversized) - return nil - } - return p.UnpackFixedBytes(int(size)) -} - -// PackStr append a string to the byte array -func (p *Packer) PackStr(str string) { - strSize := len(str) - if strSize > MaxStringLen { - p.Add(errInvalidInput) - return - } - p.PackShort(uint16(strSize)) - p.PackFixedBytes([]byte(str)) -} - -// UnpackStr unpacks a string from the byte array -func (p *Packer) UnpackStr() string { - strSize := p.UnpackShort() - return string(p.UnpackFixedBytes(int(strSize))) -} - -// UnpackLimitedStr unpacks a string. If the size of the string is greater than -// [limit], adds [errOversized] to the packer and returns the empty string. -func (p *Packer) UnpackLimitedStr(limit uint16) string { - strSize := p.UnpackShort() - if strSize > limit { - p.Add(errOversized) - return "" - } - return string(p.UnpackFixedBytes(int(strSize))) -} - -// checkSpace requires that there is at least [bytes] of write space left in the -// byte array. If this is not true, an error is added to the packer -func (p *Packer) checkSpace(bytes int) { - switch { - case p.Offset < 0: - p.Add(errNegativeOffset) - case bytes < 0: - p.Add(errInvalidInput) - case len(p.Bytes)-p.Offset < bytes: - p.Add(errBadLength) - } -} - -// expand ensures that there is [bytes] bytes left of space in the byte slice. -// If this is not allowed due to the maximum size, an error is added to the packer -// In order to understand this code, its important to understand the difference -// between a slice's length and its capacity. -func (p *Packer) expand(bytes int) { - neededSize := bytes + p.Offset // Need byte slice's length to be at least [neededSize] - switch { - case neededSize <= len(p.Bytes): // Byte slice has sufficient length already - return - case neededSize > p.MaxSize: // Lengthening the byte slice would cause it to grow too large - p.Err = errBadLength - return - case neededSize <= cap(p.Bytes): // Byte slice has sufficient capacity to lengthen it without mem alloc - p.Bytes = p.Bytes[:neededSize] - return - default: // Add capacity/length to byte slice - p.Bytes = append(p.Bytes[:cap(p.Bytes)], make([]byte, neededSize-cap(p.Bytes))...) - } -} diff --git a/chain/avalanche/utils/wrappers/packing_test.go b/chain/avalanche/utils/wrappers/packing_test.go deleted file mode 100644 index c88b9ed69..000000000 --- a/chain/avalanche/utils/wrappers/packing_test.go +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package wrappers - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" -) - -const ( - ByteSentinel = 0 - ShortSentinel = 0 - IntSentinel = 0 - LongSentinel = 0 - BoolSentinel = false -) - -func TestPackerCheckSpace(t *testing.T) { - p := Packer{Offset: -1} - p.checkSpace(1) - if !p.Errored() { - t.Fatal("Expected errNegativeOffset") - } - - p = Packer{} - p.checkSpace(-1) - if !p.Errored() { - t.Fatal("Expected errInvalidInput") - } - - p = Packer{Bytes: []byte{0x01}, Offset: 1} - p.checkSpace(1) - if !p.Errored() { - t.Fatal("Expected errBadLength") - } - - p = Packer{Bytes: []byte{0x01}, Offset: 2} - p.checkSpace(0) - if !p.Errored() { - t.Fatal("Expected errBadLength, due to out of bounds offset") - } -} - -func TestPackerExpand(t *testing.T) { - p := Packer{Bytes: []byte{0x01}, Offset: 2} - p.expand(1) - if !p.Errored() { - t.Fatal("packer.Expand didn't notice packer had out of bounds offset") - } - - p = Packer{Bytes: []byte{0x01, 0x02, 0x03}, Offset: 0} - p.expand(1) - if p.Errored() { - t.Fatalf("packer.Expand unexpectedly had error %s", p.Err) - } else if len(p.Bytes) != 3 { - t.Fatalf("packer.Expand modified byte array, when it didn't need to") - } -} - -func TestPackerPackByte(t *testing.T) { - p := Packer{MaxSize: 1} - - p.PackByte(0x01) - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != 1 { - t.Fatalf("Packer.PackByte wrote %d byte(s) but expected %d byte(s)", size, 1) - } - - expected := []byte{0x01} - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackByte wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } - - p.PackByte(0x02) - if !p.Errored() { - t.Fatal("Packer.PackByte did not fail when attempt was beyond p.MaxSize") - } -} - -func TestPackerUnpackByte(t *testing.T) { - var ( - p = Packer{Bytes: []byte{0x01}, Offset: 0} - actual = p.UnpackByte() - expected byte = 1 - expectedLen = ByteLen - ) - switch { - case p.Errored(): - t.Fatalf("Packer.UnpackByte unexpectedly raised %s", p.Err) - case actual != expected: - t.Fatalf("Packer.UnpackByte returned %d, but expected %d", actual, expected) - case p.Offset != expectedLen: - t.Fatalf("Packer.UnpackByte left Offset %d, expected %d", p.Offset, expectedLen) - } - - actual = p.UnpackByte() - if !p.Errored() { - t.Fatalf("Packer.UnpackByte should have set error, due to attempted out of bounds read") - } else if actual != ByteSentinel { - t.Fatalf("Packer.UnpackByte returned %d, expected sentinel value %d", actual, ByteSentinel) - } -} - -func TestPackerPackShort(t *testing.T) { - p := Packer{MaxSize: 2} - - p.PackShort(0x0102) - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != 2 { - t.Fatalf("Packer.PackShort wrote %d byte(s) but expected %d byte(s)", size, 2) - } - - expected := []byte{0x01, 0x02} - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackShort wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } -} - -func TestPackerUnpackShort(t *testing.T) { - var ( - p = Packer{Bytes: []byte{0x01, 0x02}, Offset: 0} - actual = p.UnpackShort() - expected uint16 = 0x0102 - expectedLen = ShortLen - ) - - switch { - case p.Errored(): - t.Fatalf("Packer.UnpackShort unexpectedly raised %s", p.Err) - case actual != expected: - t.Fatalf("Packer.UnpackShort returned %d, but expected %d", actual, expected) - case p.Offset != expectedLen: - t.Fatalf("Packer.UnpackShort left Offset %d, expected %d", p.Offset, expectedLen) - } - - actual = p.UnpackShort() - if !p.Errored() { - t.Fatalf("Packer.UnpackShort should have set error, due to attempted out of bounds read") - } else if actual != ShortSentinel { - t.Fatalf("Packer.UnpackShort returned %d, expected sentinel value %d", actual, ShortSentinel) - } -} - -func TestPackerPackInt(t *testing.T) { - p := Packer{MaxSize: 4} - - p.PackInt(0x01020304) - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != 4 { - t.Fatalf("Packer.PackInt wrote %d byte(s) but expected %d byte(s)", size, 4) - } - - expected := []byte{0x01, 0x02, 0x03, 0x04} - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackInt wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } - - p.PackInt(0x05060708) - if !p.Errored() { - t.Fatal("Packer.PackInt did not fail when attempt was beyond p.MaxSize") - } -} - -func TestPackerUnpackInt(t *testing.T) { - var ( - p = Packer{Bytes: []byte{0x01, 0x02, 0x03, 0x04}, Offset: 0} - actual = p.UnpackInt() - expected uint32 = 0x01020304 - expectedLen = IntLen - ) - - switch { - case p.Errored(): - t.Fatalf("Packer.UnpackInt unexpectedly raised %s", p.Err) - case actual != expected: - t.Fatalf("Packer.UnpackInt returned %d, but expected %d", actual, expected) - case p.Offset != expectedLen: - t.Fatalf("Packer.UnpackInt left Offset %d, expected %d", p.Offset, expectedLen) - } - - actual = p.UnpackInt() - if !p.Errored() { - t.Fatalf("Packer.UnpackInt should have set error, due to attempted out of bounds read") - } else if actual != IntSentinel { - t.Fatalf("Packer.UnpackInt returned %d, expected sentinel value %d", actual, IntSentinel) - } -} - -func TestPackerPackLong(t *testing.T) { - maxSize := 8 - p := Packer{MaxSize: maxSize} - - p.PackLong(0x0102030405060708) - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != maxSize { - t.Fatalf("Packer.PackLong wrote %d byte(s) but expected %d byte(s)", size, maxSize) - } - - expected := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackLong wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } - - p.PackLong(0x090a0b0c0d0e0f00) - if !p.Errored() { - t.Fatal("Packer.PackLong did not fail when attempt was beyond p.MaxSize") - } -} - -func TestPackerUnpackLong(t *testing.T) { - var ( - p = Packer{Bytes: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, Offset: 0} - actual = p.UnpackLong() - expected uint64 = 0x0102030405060708 - expectedLen = LongLen - ) - - switch { - case p.Errored(): - t.Fatalf("Packer.UnpackLong unexpectedly raised %s", p.Err) - case actual != expected: - t.Fatalf("Packer.UnpackLong returned %d, but expected %d", actual, expected) - case p.Offset != expectedLen: - t.Fatalf("Packer.UnpackLong left Offset %d, expected %d", p.Offset, expectedLen) - } - - actual = p.UnpackLong() - if !p.Errored() { - t.Fatalf("Packer.UnpackLong should have set error, due to attempted out of bounds read") - } else if actual != LongSentinel { - t.Fatalf("Packer.UnpackLong returned %d, expected sentinel value %d", actual, LongSentinel) - } -} - -func TestPackerPackFixedBytes(t *testing.T) { - p := Packer{MaxSize: 4} - - p.PackFixedBytes([]byte("Avax")) - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != 4 { - t.Fatalf("Packer.PackFixedBytes wrote %d byte(s) but expected %d byte(s)", size, 4) - } - - expected := []byte("Avax") - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackFixedBytes wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } - - p.PackFixedBytes([]byte("Avax")) - if !p.Errored() { - t.Fatal("Packer.PackFixedBytes did not fail when attempt was beyond p.MaxSize") - } -} - -func TestPackerUnpackFixedBytes(t *testing.T) { - var ( - p = Packer{Bytes: []byte("Avax")} - actual = p.UnpackFixedBytes(4) - expected = []byte("Avax") - expectedLen = 4 - ) - - switch { - case p.Errored(): - t.Fatalf("Packer.UnpackFixedBytes unexpectedly raised %s", p.Err) - case !bytes.Equal(actual, expected): - t.Fatalf("Packer.UnpackFixedBytes returned %d, but expected %d", actual, expected) - case p.Offset != expectedLen: - t.Fatalf("Packer.UnpackFixedBytes left Offset %d, expected %d", p.Offset, expectedLen) - } - - actual = p.UnpackFixedBytes(4) - if !p.Errored() { - t.Fatalf("Packer.UnpackFixedBytes should have set error, due to attempted out of bounds read") - } else if actual != nil { - t.Fatalf("Packer.UnpackFixedBytes returned %v, expected sentinel value %v", actual, nil) - } -} - -func TestPackerPackBytes(t *testing.T) { - p := Packer{MaxSize: 8} - - p.PackBytes([]byte("Avax")) - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != 8 { - t.Fatalf("Packer.PackBytes wrote %d byte(s) but expected %d byte(s)", size, 8) - } - - expected := []byte("\x00\x00\x00\x04Avax") - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackBytes wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } - - p.PackBytes([]byte("Avax")) - if !p.Errored() { - t.Fatal("Packer.PackBytes did not fail when attempt was beyond p.MaxSize") - } -} - -func TestPackerUnpackBytes(t *testing.T) { - var ( - p = Packer{Bytes: []byte("\x00\x00\x00\x04Avax")} - actual = p.UnpackBytes() - expected = []byte("Avax") - expectedLen = 8 - ) - - switch { - case p.Errored(): - t.Fatalf("Packer.UnpackBytes unexpectedly raised %s", p.Err) - case !bytes.Equal(actual, expected): - t.Fatalf("Packer.UnpackBytes returned %x, but expected %x", actual, expected) - case p.Offset != expectedLen: - t.Fatalf("Packer.UnpackBytes left Offset %d, expected %d", p.Offset, expectedLen) - } - - actual = p.UnpackBytes() - if !p.Errored() { - t.Fatalf("Packer.UnpackBytes should have set error, due to attempted out of bounds read") - } else if actual != nil { - t.Fatalf("Packer.UnpackBytes returned %v, expected sentinel value %v", actual, nil) - } -} - -func TestPackerUnpackLimitedBytes(t *testing.T) { - var ( - p = Packer{Bytes: []byte("\x00\x00\x00\x04Avax")} - actual = p.UnpackLimitedBytes(10) - expected = []byte("Avax") - expectedLen = 8 - require = require.New(t) - ) - require.NoError(p.Err) - require.False(p.Errored()) - require.Equal(expected, actual) - require.Equal(expectedLen, p.Offset) - - actual = p.UnpackLimitedBytes(10) - require.ErrorIs(p.Err, errBadLength) - require.Nil(actual) - - // Reset and don't allow enough bytes - p = Packer{Bytes: p.Bytes} - actual = p.UnpackLimitedBytes(2) - require.ErrorIs(p.Err, errOversized) - require.Nil(actual) -} - -func TestPackerString(t *testing.T) { - p := Packer{MaxSize: 6} - - p.PackStr("Avax") - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != 6 { - t.Fatalf("Packer.PackStr wrote %d byte(s) but expected %d byte(s)", size, 5) - } - - expected := []byte{0x00, 0x04, 0x41, 0x76, 0x61, 0x78} - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackStr wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } -} - -func TestPackerUnpackString(t *testing.T) { - var ( - p = Packer{Bytes: []byte("\x00\x04Avax")} - actual = p.UnpackStr() - expected = "Avax" - expectedLen = 6 - require = require.New(t) - ) - require.NoError(p.Err) - require.False(p.Errored()) - require.Equal(expected, actual) - require.Equal(expectedLen, p.Offset) - - actual = p.UnpackStr() - require.ErrorIs(p.Err, errBadLength) - require.Equal("", actual) -} - -func TestPackerUnpackLimitedString(t *testing.T) { - var ( - p = Packer{Bytes: []byte("\x00\x04Avax")} - actual = p.UnpackLimitedStr(10) - expected = "Avax" - expectedLen = 6 - require = require.New(t) - ) - require.NoError(p.Err) - require.False(p.Errored()) - require.Equal(expected, actual) - require.Equal(expectedLen, p.Offset) - - actual = p.UnpackLimitedStr(10) - require.ErrorIs(p.Err, errBadLength) - require.Equal("", actual) - - // Reset and don't allow enough bytes - p = Packer{Bytes: p.Bytes} - actual = p.UnpackLimitedStr(2) - require.ErrorIs(p.Err, errOversized) - require.Equal("", actual) -} - -func TestPacker(t *testing.T) { - packer := Packer{ - MaxSize: 3, - } - - if packer.Errored() { - t.Fatalf("Packer has error %s", packer.Err) - } - - packer.PackShort(17) - if len(packer.Bytes) != 2 { - t.Fatalf("Wrong byte length") - } - - packer.PackShort(1) - if !packer.Errored() { - t.Fatalf("Packer should have error") - } - - newPacker := Packer{ - Bytes: packer.Bytes, - } - - if newPacker.UnpackShort() != 17 { - t.Fatalf("Unpacked wrong value") - } -} - -func TestPackBool(t *testing.T) { - p := Packer{MaxSize: 3} - p.PackBool(false) - p.PackBool(true) - p.PackBool(false) - if p.Errored() { - t.Fatal("should have been able to pack 3 bools") - } - - p2 := Packer{Bytes: p.Bytes} - bool1, bool2, bool3 := p2.UnpackBool(), p2.UnpackBool(), p2.UnpackBool() - - if p.Errored() { - t.Fatalf("errors while unpacking bools: %v", p.Errs) - } - - if bool1 || !bool2 || bool3 { - t.Fatal("got back wrong values") - } -} - -func TestPackerPackBool(t *testing.T) { - p := Packer{MaxSize: 1} - - p.PackBool(true) - - if p.Errored() { - t.Fatal(p.Err) - } - - if size := len(p.Bytes); size != 1 { - t.Fatalf("Packer.PackBool wrote %d byte(s) but expected %d byte(s)", size, 1) - } - - expected := []byte{0x01} - if !bytes.Equal(p.Bytes, expected) { - t.Fatalf("Packer.PackBool wrote:\n%v\nExpected:\n%v", p.Bytes, expected) - } - - p.PackBool(false) - if !p.Errored() { - t.Fatal("Packer.PackLong did not fail when attempt was beyond p.MaxSize") - } -} - -func TestPackerUnpackBool(t *testing.T) { - var ( - p = Packer{Bytes: []byte{0x01}, Offset: 0} - actual = p.UnpackBool() - expected = true - expectedLen = BoolLen - ) - - switch { - case p.Errored(): - t.Fatalf("Packer.UnpackBool unexpectedly raised %s", p.Err) - case actual != expected: - t.Fatalf("Packer.UnpackBool returned %t, but expected %t", actual, expected) - case p.Offset != expectedLen: - t.Fatalf("Packer.UnpackBool left Offset %d, expected %d", p.Offset, expectedLen) - } - - actual = p.UnpackBool() - if !p.Errored() { - t.Fatalf("Packer.UnpackBool should have set error, due to attempted out of bounds read") - } else if actual != BoolSentinel { - t.Fatalf("Packer.UnpackBool returned %t, expected sentinel value %t", actual, BoolSentinel) - } - - p = Packer{Bytes: []byte{0x42}, Offset: 0} - expected = false - actual = p.UnpackBool() - if !p.Errored() { - t.Fatalf("Packer.UnpackBool id not raise error for invalid boolean value %v", p.Bytes) - } else if actual != expected { - t.Fatalf("Packer.UnpackBool returned %t, expected sentinel value %t", actual, BoolSentinel) - } -} diff --git a/chain/avalanche/utils/zero.go b/chain/avalanche/utils/zero.go deleted file mode 100644 index c5ca3b9c6..000000000 --- a/chain/avalanche/utils/zero.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -// Returns a new instance of a T. -func Zero[T any]() T { - return *new(T) -} diff --git a/chainspec.go b/chainspec.go index b3176f7db..db86c2bb2 100644 --- a/chainspec.go +++ b/chainspec.go @@ -152,6 +152,10 @@ func (s *ChainSpec) applyConfigOverrides(cfg ibc.ChainConfig) (*ibc.ChainConfig, cfg.UsingChainIDFlagCLI = s.UsingChainIDFlagCLI + if s.AvalancheSubnets != nil { + cfg.AvalancheSubnets = s.AvalancheSubnets + } + // Set the version depending on the chain type. switch cfg.Type { case "cosmos", "avalanche": diff --git a/examples/avalanche/avalanche_chain_test.go b/examples/avalanche/avalanche_chain_test.go index ccb98a632..2b6ede55b 100644 --- a/examples/avalanche/avalanche_chain_test.go +++ b/examples/avalanche/avalanche_chain_test.go @@ -28,6 +28,13 @@ func TestAvalancheChainStart(t *testing.T) { Version: "v1.9.16", ChainConfig: ibc.ChainConfig{ ChainID: "neto-123123", + AvalancheSubnets: []ibc.AvalancheSubnetConfig{ + { + Name: "timestampvm", + VMFile: "", + Genesis: []byte("{}"), + }, + }, }, NumFullNodes: &nf, NumValidators: &nv, @@ -48,7 +55,7 @@ func TestAvalancheChainStart(t *testing.T) { err = chain.Start(t.Name(), ctx) require.NoError(t, err, "failed to start avalanche chain") - err = testutil.WaitForBlocks(ctx, 10, chain) + err = testutil.WaitForBlocks(ctx, 2, chain) require.NoError(t, err, "avalanche chain failed to make blocks") } diff --git a/ibc/types.go b/ibc/types.go index e842b299d..297e5c449 100644 --- a/ibc/types.go +++ b/ibc/types.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/ava-labs/avalanchego/ids" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module/testutil" @@ -13,8 +14,12 @@ import ( ) type AvalancheSubnetConfig struct { - Name string - VMFile string + Name string + VMFile string + Genesis []byte + + subnet ids.ID + chain ids.ID } // ChainConfig defines the chain parameters requires to run an interchaintest testnet for a chain.