diff --git a/integration-tests/relayinterface/lookups_test.go b/integration-tests/relayinterface/lookups_test.go index 09b53a867..4b0f672dc 100644 --- a/integration-tests/relayinterface/lookups_test.go +++ b/integration-tests/relayinterface/lookups_test.go @@ -23,6 +23,7 @@ import ( type InnerAccountArgs struct { Accounts []*solana.AccountMeta + Bitmap uint64 } type TestAccountArgs struct { @@ -132,27 +133,29 @@ func TestAccountLookups(t *testing.T) { require.Error(t, err) }) - t.Run("AccountLookup works with MetaBool lookups", func(t *testing.T) { + t.Run("AccountLookup works with MetaBool bitmap lookups", func(t *testing.T) { accounts := [3]*solana.AccountMeta{} for i := 0; i < 3; i++ { accounts[i] = &solana.AccountMeta{ PublicKey: chainwriter.GetRandomPubKey(t), - IsSigner: i%2 == 0, - IsWritable: (i+1)%2 == 0, + IsSigner: (i)%2 == 0, + IsWritable: (i)%2 == 0, } } lookupConfig := chainwriter.AccountLookup{ Name: "InvalidAccount", Location: "Inner.Accounts.PublicKey", - IsSigner: chainwriter.MetaBool{Location: "Inner.Accounts.IsSigner"}, - IsWritable: chainwriter.MetaBool{Location: "Inner.Accounts.IsWritable"}, + IsSigner: chainwriter.MetaBool{BitmapLocation: "Inner.Bitmap"}, + IsWritable: chainwriter.MetaBool{BitmapLocation: "Inner.Bitmap"}, } args := TestAccountArgs{ Inner: InnerAccountArgs{ Accounts: accounts[:], + // should be 101... so {true, false, true} + Bitmap: 5, }, } @@ -164,79 +167,10 @@ func TestAccountLookups(t *testing.T) { } }) - t.Run("AccountLookup works with MetaBool lookups when a meta field is missing", func(t *testing.T) { - accounts := [3]*solana.AccountMeta{} - - for i := 0; i < 3; i++ { - accounts[i] = &solana.AccountMeta{ - PublicKey: chainwriter.GetRandomPubKey(t), - IsWritable: true, - } - } - - lookupConfig := chainwriter.AccountLookup{ - Name: "InvalidAccount", - Location: "Inner.Accounts.PublicKey", - IsSigner: chainwriter.MetaBool{Location: "Inner.Accounts.IsSigner"}, - IsWritable: chainwriter.MetaBool{Location: "Inner.Accounts.IsWritable"}, - } - - args := TestAccountArgs{ - Inner: InnerAccountArgs{ - Accounts: accounts[:], - }, - } - - result, err := lookupConfig.Resolve(ctx, args, nil, nil) - require.NoError(t, err) - - for i, meta := range result { - require.Equal(t, accounts[i], meta) - } - }) - - t.Run("AccountLookup works with MetaBool lookups in a different location", func(t *testing.T) { - type TestAccountArgsExtended struct { - Inner InnerAccountArgs - ExternalBool bool - } - - accounts := [3]*solana.AccountMeta{} - - for i := 0; i < 3; i++ { - accounts[i] = &solana.AccountMeta{ - PublicKey: chainwriter.GetRandomPubKey(t), - IsWritable: true, - IsSigner: true, - } - } - - lookupConfig := chainwriter.AccountLookup{ - Name: "InvalidAccount", - Location: "Inner.Accounts.PublicKey", - IsSigner: chainwriter.MetaBool{Location: "ExternalBool"}, - IsWritable: chainwriter.MetaBool{Location: "ExternalBool"}, - } - - args := TestAccountArgsExtended{ - Inner: InnerAccountArgs{ - Accounts: accounts[:], - }, - ExternalBool: true, - } - - result, err := lookupConfig.Resolve(ctx, args, nil, nil) - require.NoError(t, err) - - for i, meta := range result { - require.Equal(t, accounts[i], meta) - } - }) - - t.Run("AccountLookup fails with MetaBool due to an invalid number of Meta lookups", func(t *testing.T) { + t.Run("AccountLookup fails with MetaBool due to an invalid number of bitmaps", func(t *testing.T) { type TestAccountArgsExtended struct { - Inner InnerAccountArgs - ExternalBools []bool + Inner InnerAccountArgs + Bitmaps []uint64 } accounts := [3]*solana.AccountMeta{} @@ -252,22 +186,22 @@ func TestAccountLookups(t *testing.T) { lookupConfig := chainwriter.AccountLookup{ Name: "InvalidAccount", Location: "Inner.Accounts.PublicKey", - IsSigner: chainwriter.MetaBool{Location: "ExternalBools"}, - IsWritable: chainwriter.MetaBool{Location: "ExternalBools"}, + IsSigner: chainwriter.MetaBool{BitmapLocation: "Bitmaps"}, + IsWritable: chainwriter.MetaBool{BitmapLocation: "Bitmaps"}, } args := TestAccountArgsExtended{ Inner: InnerAccountArgs{ Accounts: accounts[:], }, - ExternalBools: []bool{true, true}, + Bitmaps: []uint64{5, 3}, } _, err := lookupConfig.Resolve(ctx, args, nil, nil) - require.Contains(t, err.Error(), "boolean array length 2 doesn't match pubkey count 3 for location") + require.Contains(t, err.Error(), "bitmap value is not a single value") }) - t.Run("AccountLookup fails with MetaBool with an Invalid Location", func(t *testing.T) { + t.Run("AccountLookup fails with MetaBool with an Invalid BitmapLocation", func(t *testing.T) { accounts := [3]*solana.AccountMeta{} for i := 0; i < 3; i++ { @@ -280,8 +214,8 @@ func TestAccountLookups(t *testing.T) { lookupConfig := chainwriter.AccountLookup{ Name: "InvalidAccount", Location: "Inner.Accounts.PublicKey", - IsSigner: chainwriter.MetaBool{Location: "Invalid.IsSigner"}, - IsWritable: chainwriter.MetaBool{Location: "Invalid.IsWritable"}, + IsSigner: chainwriter.MetaBool{BitmapLocation: "Invalid.Bitmap"}, + IsWritable: chainwriter.MetaBool{BitmapLocation: "Invalid.Bitmap"}, } args := TestAccountArgs{ @@ -291,10 +225,10 @@ func TestAccountLookups(t *testing.T) { } _, err := lookupConfig.Resolve(ctx, args, nil, nil) - require.Contains(t, err.Error(), "error reading bools from location") + require.Contains(t, err.Error(), "error reading bitmap from location") }) - t.Run("AccountLookup fails when MetaBool is an invalid type", func(t *testing.T) { + t.Run("AccountLookup fails when MetaBool Bitmap is an invalid type", func(t *testing.T) { accounts := [3]*solana.AccountMeta{} for i := 0; i < 3; i++ { @@ -307,8 +241,8 @@ func TestAccountLookups(t *testing.T) { lookupConfig := chainwriter.AccountLookup{ Name: "InvalidAccount", Location: "Inner.Accounts.PublicKey", - IsSigner: chainwriter.MetaBool{Location: "Inner"}, - IsWritable: chainwriter.MetaBool{Location: "Inner"}, + IsSigner: chainwriter.MetaBool{BitmapLocation: "Inner"}, + IsWritable: chainwriter.MetaBool{BitmapLocation: "Inner"}, } args := TestAccountArgs{ diff --git a/pkg/solana/chainwriter/helpers.go b/pkg/solana/chainwriter/helpers.go index b60b4b76d..c67bb7ca5 100644 --- a/pkg/solana/chainwriter/helpers.go +++ b/pkg/solana/chainwriter/helpers.go @@ -51,12 +51,6 @@ func GetValuesAtLocation(args any, location string) ([][]byte, error) { buf := make([]byte, 8) binary.LittleEndian.PutUint64(buf, num) vals = append(vals, buf) - } else if boolean, ok := value.(bool); ok { - if boolean { - vals = append(vals, []byte{0x01}) - } else { - vals = append(vals, []byte{0x00}) - } } else { return nil, fmt.Errorf("invalid value format at path: %s, type: %s", location, reflect.TypeOf(value).String()) } diff --git a/pkg/solana/chainwriter/lookups.go b/pkg/solana/chainwriter/lookups.go index e7a3e9230..b724b77d9 100644 --- a/pkg/solana/chainwriter/lookups.go +++ b/pkg/solana/chainwriter/lookups.go @@ -2,6 +2,7 @@ package chainwriter import ( "context" + "encoding/binary" "fmt" "reflect" @@ -29,14 +30,14 @@ type AccountConstant struct { type AccountLookup struct { Name string Location string - // IsSigner and IsWritable can either be a constant bool or a location to a bool + // IsSigner and IsWritable can either be a constant bool or a location to a bitmap which decides the bools IsSigner MetaBool IsWritable MetaBool } type MetaBool struct { - Value bool - Location string + Value bool + BitmapLocation string } type Seed struct { @@ -107,18 +108,22 @@ func (al AccountLookup) Resolve( } var metas []*solana.AccountMeta + signerIndexes, err := resolveBitMap(al.IsSigner, args, len(derivedValues)) + if err != nil { + return nil, err + } + + writerIndexes, err := resolveBitMap(al.IsWritable, args, len(derivedValues)) + if err != nil { + return nil, err + } + for i, address := range derivedValues { // Resolve isSigner for this particular pubkey - isSigner, err := resolveMetaBool(al.IsSigner, args, i, len(derivedValues)) - if err != nil { - return nil, err - } + isSigner := signerIndexes[i] // Resolve isWritable - isWritable, err := resolveMetaBool(al.IsWritable, args, i, len(derivedValues)) - if err != nil { - return nil, err - } + isWritable := writerIndexes[i] metas = append(metas, &solana.AccountMeta{ PublicKey: solana.PublicKeyFromBytes(address), @@ -130,38 +135,30 @@ func (al AccountLookup) Resolve( return metas, nil } -func resolveMetaBool(mb MetaBool, args any, pubkeyIndex, pubkeysCount int) (bool, error) { - if mb.Location == "" { - return mb.Value, nil +func resolveBitMap(mb MetaBool, args any, length int) ([]bool, error) { + result := make([]bool, length) + if mb.BitmapLocation == "" { + for i := 0; i < length; i++ { + result[i] = mb.Value + } + return result, nil } - boolVals, err := GetValuesAtLocation(args, mb.Location) + bitmapVals, err := GetValuesAtLocation(args, mb.BitmapLocation) if err != nil { - return false, fmt.Errorf("error reading bools from location '%s': %w", mb.Location, err) + return []bool{}, fmt.Errorf("error reading bitmap from location '%s': %w", mb.BitmapLocation, err) } - if len(boolVals) == 0 { - return false, fmt.Errorf("no boolean found at location '%s'", mb.Location) + if len(bitmapVals) != 1 { + return []bool{}, fmt.Errorf("bitmap value is not a single value: %v, length: %d", bitmapVals, len(bitmapVals)) } - // boolVals should always equal the number of pubkeys or 1 - if len(boolVals) != pubkeysCount && len(boolVals) != 1 { - return false, fmt.Errorf( - "boolean array length %d doesn't match pubkey count %d for location '%s'", - len(boolVals), pubkeysCount, mb.Location, - ) + bitmapInt := binary.LittleEndian.Uint64(bitmapVals[0]) + for i := 0; i < length; i++ { + result[i] = bitmapInt&(1< 0 } - // a single boolean value is valid to apply to all pubkeys - data := boolVals[0] - - if len(boolVals) > 1 { - data = boolVals[pubkeyIndex] - } - if len(data) == 0 { - return false, fmt.Errorf("missing data for boolean at index %d in location '%s'", pubkeyIndex, mb.Location) - } - return data[0] != 0, nil + return result, nil } func (alt AccountsFromLookupTable) Resolve(_ context.Context, _ any, derivedTableMap map[string]map[string][]*solana.AccountMeta, _ client.Reader) ([]*solana.AccountMeta, error) {