From b36c9bc6c7220bc8adc77f7f058ff2cce801a67c Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Mon, 18 May 2026 05:57:54 -0400 Subject: [PATCH 1/3] add field to mark requests as external for solana service --- pkg/chains/solana/proto_helpers.go | 39 ++++++-- pkg/chains/solana/proto_helpers_test.go | 67 ++++++++++++++ pkg/chains/solana/solana.pb.go | 56 ++++++++++-- pkg/chains/solana/solana.proto | 4 + pkg/http/limited_transport.go | 90 +++++++++++++++++++ pkg/loop/internal/relayer/solana.go | 10 +-- .../internal/relayerset/relayerset_test.go | 5 +- pkg/loop/internal/relayerset/solana.go | 5 +- pkg/types/chains/solana/solana.go | 10 ++- 9 files changed, 258 insertions(+), 28 deletions(-) create mode 100644 pkg/http/limited_transport.go diff --git a/pkg/chains/solana/proto_helpers.go b/pkg/chains/solana/proto_helpers.go index 608d6b1c5c..0866533473 100644 --- a/pkg/chains/solana/proto_helpers.go +++ b/pkg/chains/solana/proto_helpers.go @@ -358,6 +358,29 @@ func ConvertGetAccountInfoOptsToProto(o *solana.GetAccountInfoOpts) *GetAccountI } } +func ConvertGetAccountInfoRequestFromProto(p *GetAccountInfoWithOptsRequest) (solana.GetAccountInfoRequest, error) { + if p == nil { + return solana.GetAccountInfoRequest{}, fmt.Errorf("nil GetAccountInfoWithOptsRequest") + } + addr, err := ConvertPublicKeyFromProto(p.GetAccount()) + if err != nil { + return solana.GetAccountInfoRequest{}, err + } + return solana.GetAccountInfoRequest{ + Account: addr, + Opts: ConvertGetAccountInfoOptsFromProto(p.GetOpts()), + IsExternal: p.GetIsExternal(), + }, nil +} + +func ConvertGetAccountInfoRequestToProto(r solana.GetAccountInfoRequest) *GetAccountInfoWithOptsRequest { + return &GetAccountInfoWithOptsRequest{ + Account: r.Account[:], + Opts: ConvertGetAccountInfoOptsToProto(r.Opts), + IsExternal: r.IsExternal, + } +} + func ConvertGetMultipleAccountsOptsFromProto(p *GetMultipleAccountsOpts) *solana.GetMultipleAccountsOpts { if p == nil { return nil @@ -825,11 +848,11 @@ func ConvertGetTransactionRequestFromProto(p *GetTransactionRequest) (solana.Get if err != nil { return solana.GetTransactionRequest{}, err } - return solana.GetTransactionRequest{Signature: sig}, nil + return solana.GetTransactionRequest{Signature: sig, IsExternal: p.GetIsExternal()}, nil } func ConvertGetTransactionRequestToProto(r solana.GetTransactionRequest) *GetTransactionRequest { - return &GetTransactionRequest{Signature: r.Signature[:]} + return &GetTransactionRequest{Signature: r.Signature[:], IsExternal: r.IsExternal} } func ConvertGetBalanceReplyFromProto(p *GetBalanceReply) *solana.GetBalanceReply { @@ -996,8 +1019,9 @@ func ConvertGetMultipleAccountsRequestFromProto(p *GetMultipleAccountsWithOptsRe } accts, _ := ConvertPublicKeysFromProto(p.Accounts) return &solana.GetMultipleAccountsRequest{ - Accounts: accts, - Opts: ConvertGetMultipleAccountsOptsFromProto(p.Opts), + Accounts: accts, + Opts: ConvertGetMultipleAccountsOptsFromProto(p.Opts), + IsExternal: p.GetIsExternal(), } } @@ -1006,8 +1030,9 @@ func ConvertGetMultipleAccountsRequestToProto(r *solana.GetMultipleAccountsReque return nil } return &GetMultipleAccountsWithOptsRequest{ - Accounts: ConvertPublicKeysToProto(r.Accounts), - Opts: ConvertGetMultipleAccountsOptsToProto(r.Opts), + Accounts: ConvertPublicKeysToProto(r.Accounts), + Opts: ConvertGetMultipleAccountsOptsToProto(r.Opts), + IsExternal: r.IsExternal, } } @@ -1150,6 +1175,7 @@ func ConvertSimulateTXRequestFromProto(p *SimulateTXRequest) (solana.SimulateTXR Receiver: recv, EncodedTransaction: p.EncodedTransaction, Opts: ConvertSimulateTXOptsFromProto(p.Opts), + IsExternal: p.GetIsExternal(), }, nil } @@ -1158,6 +1184,7 @@ func ConvertSimulateTXRequestToProto(r solana.SimulateTXRequest) *SimulateTXRequ Receiver: r.Receiver[:], EncodedTransaction: r.EncodedTransaction, Opts: ConvertSimulateTXOptsToProto(r.Opts), + IsExternal: r.IsExternal, } } diff --git a/pkg/chains/solana/proto_helpers_test.go b/pkg/chains/solana/proto_helpers_test.go index 2ecd631c3f..1dba017623 100644 --- a/pkg/chains/solana/proto_helpers_test.go +++ b/pkg/chains/solana/proto_helpers_test.go @@ -386,6 +386,73 @@ func TestGetSignatureStatusesConverters(t *testing.T) { require.Equal(t, conv.ConfirmationStatusType_CONFIRMATION_STATUS_TYPE_CONFIRMED, rep2.Results[0].ConfirmationStatus) } +func TestExternalRequestProtoRoundTrip(t *testing.T) { + t.Run("GetAccountInfoRequest", func(t *testing.T) { + pk := typesolana.PublicKey{} + copy(pk[:], mkBytes(typesolana.PublicKeyLength, 0xAB)) + d := typesolana.GetAccountInfoRequest{ + Account: pk, + Opts: &typesolana.GetAccountInfoOpts{ + Encoding: typesolana.EncodingBase64, + Commitment: typesolana.CommitmentFinalized, + }, + IsExternal: true, + } + pb := conv.ConvertGetAccountInfoRequestToProto(d) + got, err := conv.ConvertGetAccountInfoRequestFromProto(pb) + require.NoError(t, err) + require.Equal(t, d, got) + }) + + t.Run("GetMultipleAccountsRequest", func(t *testing.T) { + d := &typesolana.GetMultipleAccountsRequest{ + Accounts: []typesolana.PublicKey{ + {1}, + func() (pk typesolana.PublicKey) { copy(pk[:], mkBytes(typesolana.PublicKeyLength, 0xCD)); return pk }(), + }, + Opts: &typesolana.GetMultipleAccountsOpts{ + Encoding: typesolana.EncodingJSONParsed, + Commitment: typesolana.CommitmentProcessed, + }, + IsExternal: true, + } + pb := conv.ConvertGetMultipleAccountsRequestToProto(d) + got := conv.ConvertGetMultipleAccountsRequestFromProto(pb) + require.Equal(t, d.Accounts[0], got.Accounts[0]) + require.Equal(t, d.Accounts[1], got.Accounts[1]) + require.Equal(t, d.IsExternal, got.IsExternal) + require.Equal(t, d.Opts, got.Opts) + }) + + t.Run("GetTransactionRequest", func(t *testing.T) { + var sig typesolana.Signature + copy(sig[:], mkBytes(typesolana.SignatureLength, 0xEF)) + d := typesolana.GetTransactionRequest{Signature: sig, IsExternal: true} + pb := conv.ConvertGetTransactionRequestToProto(d) + got, err := conv.ConvertGetTransactionRequestFromProto(pb) + require.NoError(t, err) + require.Equal(t, d, got) + }) + + t.Run("SimulateTXRequest", func(t *testing.T) { + var recv typesolana.PublicKey + copy(recv[:], mkBytes(typesolana.PublicKeyLength, 0x11)) + d := typesolana.SimulateTXRequest{ + Receiver: recv, + EncodedTransaction: "txdata", + Opts: &typesolana.SimulateTXOpts{ + SigVerify: true, + Commitment: typesolana.CommitmentConfirmed, + }, + IsExternal: true, + } + pb := conv.ConvertSimulateTXRequestToProto(d) + got, err := conv.ConvertSimulateTXRequestFromProto(pb) + require.NoError(t, err) + require.Equal(t, d, got) + }) +} + func TestErrorJoinBehavior_PublicKeys(t *testing.T) { in := [][]byte{ mkBytes(typesolana.PublicKeyLength-1, 0x01), diff --git a/pkg/chains/solana/solana.pb.go b/pkg/chains/solana/solana.pb.go index 86a4362d87..682b13eb62 100644 --- a/pkg/chains/solana/solana.pb.go +++ b/pkg/chains/solana/solana.pb.go @@ -648,6 +648,7 @@ type GetAccountInfoWithOptsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Account []byte `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` // 32-byte Pubkey Opts *GetAccountInfoOpts `protobuf:"bytes,2,opt,name=opts,proto3" json:"opts,omitempty"` + IsExternal bool `protobuf:"varint,3,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -696,6 +697,13 @@ func (x *GetAccountInfoWithOptsRequest) GetOpts() *GetAccountInfoOpts { return nil } +func (x *GetAccountInfoWithOptsRequest) GetIsExternal() bool { + if x != nil { + return x.IsExternal + } + return false +} + // Reply for GetBalance. type GetBalanceReply struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1237,6 +1245,7 @@ type GetMultipleAccountsWithOptsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Accounts [][]byte `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` // list of 32-byte Pubkeys Opts *GetMultipleAccountsOpts `protobuf:"bytes,2,opt,name=opts,proto3" json:"opts,omitempty"` + IsExternal bool `protobuf:"varint,3,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1285,6 +1294,13 @@ func (x *GetMultipleAccountsWithOptsRequest) GetOpts() *GetMultipleAccountsOpts return nil } +func (x *GetMultipleAccountsWithOptsRequest) GetIsExternal() bool { + if x != nil { + return x.IsExternal + } + return false +} + // Reply for GetSignatureStatuses. type GetSignatureStatusesReply struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -2416,6 +2432,7 @@ func (x *GetTransactionReply) GetMeta() *TransactionMeta { type GetTransactionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` // 64-byte signature + IsExternal bool `protobuf:"varint,2,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2457,6 +2474,13 @@ func (x *GetTransactionRequest) GetSignature() []byte { return nil } +func (x *GetTransactionRequest) GetIsExternal() bool { + if x != nil { + return x.IsExternal + } + return false +} + // RPC read context. type RPCContext struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -2646,6 +2670,7 @@ type SimulateTXRequest struct { Receiver []byte `protobuf:"bytes,1,opt,name=receiver,proto3" json:"receiver,omitempty"` // 32-byte program id (target) EncodedTransaction string `protobuf:"bytes,2,opt,name=encoded_transaction,json=encodedTransaction,proto3" json:"encoded_transaction,omitempty"` // base64/base58 tx Opts *SimulateTXOpts `protobuf:"bytes,3,opt,name=opts,proto3" json:"opts,omitempty"` + IsExternal bool `protobuf:"varint,4,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2701,6 +2726,13 @@ func (x *SimulateTXRequest) GetOpts() *SimulateTXOpts { return nil } +func (x *SimulateTXRequest) GetIsExternal() bool { + if x != nil { + return x.IsExternal + } + return false +} + // Accounts to return during simulation. type SimulateTransactionAccountsOpts struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -4083,10 +4115,12 @@ const file_solana_proto_rawDesc = "" + "\vrpc_context\x18\x01 \x01(\v2\x17.loop.solana.RPCContextR\n" + "rpcContext\x12/\n" + "\x05value\x18\x02 \x01(\v2\x14.loop.solana.AccountH\x00R\x05value\x88\x01\x01B\b\n" + - "\x06_value\"n\n" + + "\x06_value\"\x8f\x01\n" + "\x1dGetAccountInfoWithOptsRequest\x12\x18\n" + "\aaccount\x18\x01 \x01(\fR\aaccount\x123\n" + - "\x04opts\x18\x02 \x01(\v2\x1f.loop.solana.GetAccountInfoOptsR\x04opts\"'\n" + + "\x04opts\x18\x02 \x01(\v2\x1f.loop.solana.GetAccountInfoOptsR\x04opts\x12\x1f\n" + + "\vis_external\x18\x03 \x01(\bR\n" + + "isExternal\"'\n" + "\x0fGetBalanceReply\x12\x14\n" + "\x05value\x18\x01 \x01(\x04R\x05value\"d\n" + "\x11GetBalanceRequest\x12\x12\n" + @@ -4132,10 +4166,12 @@ const file_solana_proto_rawDesc = "" + " GetMultipleAccountsWithOptsReply\x128\n" + "\vrpc_context\x18\x01 \x01(\v2\x17.loop.solana.RPCContextR\n" + "rpcContext\x129\n" + - "\x05value\x18\x02 \x03(\v2#.loop.solana.OptionalAccountWrapperR\x05value\"z\n" + + "\x05value\x18\x02 \x03(\v2#.loop.solana.OptionalAccountWrapperR\x05value\"\x9b\x01\n" + "\"GetMultipleAccountsWithOptsRequest\x12\x1a\n" + "\baccounts\x18\x01 \x03(\fR\baccounts\x128\n" + - "\x04opts\x18\x02 \x01(\v2$.loop.solana.GetMultipleAccountsOptsR\x04opts\"^\n" + + "\x04opts\x18\x02 \x01(\v2$.loop.solana.GetMultipleAccountsOptsR\x04opts\x12\x1f\n" + + "\vis_external\x18\x03 \x01(\bR\n" + + "isExternal\"^\n" + "\x19GetSignatureStatusesReply\x12A\n" + "\aresults\x18\x01 \x03(\v2'.loop.solana.GetSignatureStatusesResultR\aresults\"1\n" + "\x1bGetSignatureStatusesRequest\x12\x12\n" + @@ -4225,9 +4261,11 @@ const file_solana_proto_rawDesc = "" + "\x04meta\x18\x04 \x01(\v2\x1c.loop.solana.TransactionMetaH\x02R\x04meta\x88\x01\x01B\r\n" + "\v_block_timeB\x0e\n" + "\f_transactionB\a\n" + - "\x05_meta\"5\n" + + "\x05_meta\"V\n" + "\x15GetTransactionRequest\x12\x1c\n" + - "\tsignature\x18\x01 \x01(\fR\tsignature\" \n" + + "\tsignature\x18\x01 \x01(\fR\tsignature\x12\x1f\n" + + "\vis_external\x18\x02 \x01(\bR\n" + + "isExternal\" \n" + "\n" + "RPCContext\x12\x12\n" + "\x04slot\x18\x01 \x01(\x04R\x04slot\"\xf0\x01\n" + @@ -4243,11 +4281,13 @@ const file_solana_proto_rawDesc = "" + "\x03err\x18\x01 \x01(\tR\x03err\x12\x12\n" + "\x04logs\x18\x02 \x03(\tR\x04logs\x120\n" + "\baccounts\x18\x03 \x03(\v2\x14.loop.solana.AccountR\baccounts\x12%\n" + - "\x0eunits_consumed\x18\x04 \x01(\x04R\runitsConsumed\"\x91\x01\n" + + "\x0eunits_consumed\x18\x04 \x01(\x04R\runitsConsumed\"\xb2\x01\n" + "\x11SimulateTXRequest\x12\x1a\n" + "\breceiver\x18\x01 \x01(\fR\breceiver\x12/\n" + "\x13encoded_transaction\x18\x02 \x01(\tR\x12encodedTransaction\x12/\n" + - "\x04opts\x18\x03 \x01(\v2\x1b.loop.solana.SimulateTXOptsR\x04opts\"v\n" + + "\x04opts\x18\x03 \x01(\v2\x1b.loop.solana.SimulateTXOptsR\x04opts\x12\x1f\n" + + "\vis_external\x18\x04 \x01(\bR\n" + + "isExternal\"v\n" + "\x1fSimulateTransactionAccountsOpts\x125\n" + "\bencoding\x18\x01 \x01(\x0e2\x19.loop.solana.EncodingTypeR\bencoding\x12\x1c\n" + "\taddresses\x18\x02 \x03(\fR\taddresses\"\x8e\x01\n" + diff --git a/pkg/chains/solana/solana.proto b/pkg/chains/solana/solana.proto index c629a81dad..69cd273f2d 100644 --- a/pkg/chains/solana/solana.proto +++ b/pkg/chains/solana/solana.proto @@ -107,6 +107,7 @@ message GetAccountInfoWithOptsReply { message GetAccountInfoWithOptsRequest { bytes account = 1; // 32-byte Pubkey GetAccountInfoOpts opts = 2; + bool is_external = 3; } // Reply for GetBalance. @@ -172,6 +173,7 @@ message GetMultipleAccountsWithOptsReply { message GetMultipleAccountsWithOptsRequest { repeated bytes accounts = 1; // list of 32-byte Pubkeys GetMultipleAccountsOpts opts = 2; + bool is_external = 3; } // Reply for GetSignatureStatuses. @@ -304,6 +306,7 @@ message GetTransactionReply { // GetTransaction request. message GetTransactionRequest { bytes signature = 1; // 64-byte signature + bool is_external = 2; } // RPC read context. @@ -332,6 +335,7 @@ message SimulateTXRequest { bytes receiver = 1; // 32-byte program id (target) string encoded_transaction = 2; // base64/base58 tx SimulateTXOpts opts = 3; + bool is_external = 4; } // Accounts to return during simulation. diff --git a/pkg/http/limited_transport.go b/pkg/http/limited_transport.go new file mode 100644 index 0000000000..365b6f7834 --- /dev/null +++ b/pkg/http/limited_transport.go @@ -0,0 +1,90 @@ +package http + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" +) + +type contextKey string + +const responseLimitCtxKey contextKey = "responseLimitCtxKey" + +// LimitedTransport wraps an http.RoundTripper and limits the size of the response body. Limit is set via context using WithResponseSizeLimit +type LimitedTransport struct { + // RoundTripper is the underlying http.RoundTripper to use for the actual request. + // This will typically be http.DefaultTransport or a custom *http.Transport. + RoundTripper http.RoundTripper +} + +// RoundTrip implements the http.RoundTripper interface for LimitedTransport. +func (t *LimitedTransport) RoundTrip(req *http.Request) (*http.Response, error) { + // Perform the actual HTTP request using the underlying RoundTripper. + resp, err := t.RoundTripper.RoundTrip(req) + if err != nil { + return nil, err + } + + // If the response body is not nil, wrap it with an io.limitReader. + // This will ensure that only up to MaxResponseSize bytes can be read. + respLimit := GetResponseSizeLimit(req.Context()) + if resp.Body != nil && respLimit > 0 { + resp.Body = limitReader(resp.Body, int64(respLimit)) + } + + return resp, nil +} + +// WithResponseSizeLimit - sets a limit on the size of the response body for HTTP requests made with the LimitedTransport. +func WithResponseSizeLimit(ctx context.Context, limit uint32) context.Context { + if limit > 0 { + return context.WithValue(ctx, responseLimitCtxKey, limit) + } + + return ctx +} + +func GetResponseSizeLimit(ctx context.Context) uint32 { + limit, ok := ctx.Value(responseLimitCtxKey).(uint32) + if !ok { + return 0 + } + return limit +} + +var errResponseTooLarge = errors.New("response is too large") + +// limitReader returns a Reader that reads from r +// but stops with EOF after n bytes. +// The underlying implementation is a *limitedReader. +func limitReader(r io.ReadCloser, n int64) *limitedReader { + return &limitedReader{R: r, N: n, Limit: n} +} + +// A limitedReader reads from R but limits the amount of +// data returned to just N bytes. Each call to Read +// updates N to reflect the new amount remaining. +// Read returns EOF when N <= 0 or when the underlying R returns EOF. +type limitedReader struct { + R io.ReadCloser // underlying reader + N int64 // max bytes remaining + Limit int64 // original limit for error reporting +} + +func (l *limitedReader) Read(p []byte) (n int, err error) { + if l.N <= 0 { + return 0, fmt.Errorf("reached read limit of %d bytes: %w", l.Limit, errResponseTooLarge) + } + if int64(len(p)) > l.N { + p = p[0:l.N] + } + n, err = l.R.Read(p) + l.N -= int64(n) + return +} + +func (l *limitedReader) Close() error { + return l.R.Close() +} diff --git a/pkg/loop/internal/relayer/solana.go b/pkg/loop/internal/relayer/solana.go index 0a3a1e8fae..172d946c1c 100644 --- a/pkg/loop/internal/relayer/solana.go +++ b/pkg/loop/internal/relayer/solana.go @@ -117,10 +117,7 @@ func (sc *SolClient) GetBalance(ctx context.Context, req solana.GetBalanceReques } func (sc *SolClient) GetAccountInfoWithOpts(ctx context.Context, req solana.GetAccountInfoRequest) (*solana.GetAccountInfoReply, error) { - pReq := &solpb.GetAccountInfoWithOptsRequest{ - Account: req.Account[:], - Opts: solpb.ConvertGetAccountInfoOptsToProto(req.Opts), - } + pReq := solpb.ConvertGetAccountInfoRequestToProto(req) pResp, err := sc.grpcClient.GetAccountInfoWithOpts(ctx, pReq) if err != nil { return nil, net.WrapRPCErr(err) @@ -339,14 +336,11 @@ func (s *solServer) GetBalance(ctx context.Context, req *solpb.GetBalanceRequest } func (s *solServer) GetAccountInfoWithOpts(ctx context.Context, req *solpb.GetAccountInfoWithOptsRequest) (*solpb.GetAccountInfoWithOptsReply, error) { - addr, err := solpb.ConvertPublicKeyFromProto(req.GetAccount()) + dReq, err := solpb.ConvertGetAccountInfoRequestFromProto(req) if err != nil { return nil, net.WrapRPCErr(err) } - opts := solpb.ConvertGetAccountInfoOptsFromProto(req.GetOpts()) - - dReq := solana.GetAccountInfoRequest{Account: addr, Opts: opts} dResp, err := s.impl.GetAccountInfoWithOpts(ctx, dReq) if err != nil { return nil, net.WrapRPCErr(err) diff --git a/pkg/loop/internal/relayerset/relayerset_test.go b/pkg/loop/internal/relayerset/relayerset_test.go index 5d02ec0b8b..548521e741 100644 --- a/pkg/loop/internal/relayerset/relayerset_test.go +++ b/pkg/loop/internal/relayerset/relayerset_test.go @@ -645,6 +645,7 @@ func Test_RelayerSet_SolanaService(t *testing.T) { Encoding: soltypes.EncodingJSONParsed, Commitment: soltypes.CommitmentFinalized, }, + IsExternal: true, } slot := uint64(22) lamports := uint64(33) @@ -673,6 +674,7 @@ func Test_RelayerSet_SolanaService(t *testing.T) { Encoding: soltypes.EncodingBase64, Commitment: soltypes.CommitmentProcessed, }, + IsExternal: true, } slot := uint64(22) lamports := uint64(33) @@ -736,7 +738,7 @@ func Test_RelayerSet_SolanaService(t *testing.T) { run: func(t *testing.T, sol types.SolanaService, mockSol *mocks2.SolanaService) { var sig soltypes.Signature copy(sig[:], []byte{1, 2, 3, 4}) - req := soltypes.GetTransactionRequest{Signature: sig} + req := soltypes.GetTransactionRequest{Signature: sig, IsExternal: true} expTime := soltypes.UnixTimeSeconds(11) expFee := uint64(33) expSlot := uint64(17) @@ -808,6 +810,7 @@ func Test_RelayerSet_SolanaService(t *testing.T) { Commitment: soltypes.CommitmentProcessed, ReplaceRecentBlockhash: true, }, + IsExternal: true, } mockSol.EXPECT(). SimulateTX(mock.Anything, req). diff --git a/pkg/loop/internal/relayerset/solana.go b/pkg/loop/internal/relayerset/solana.go index 06e3facc19..019e37d0f3 100644 --- a/pkg/loop/internal/relayerset/solana.go +++ b/pkg/loop/internal/relayerset/solana.go @@ -214,14 +214,11 @@ func (ss *solServer) GetAccountInfoWithOpts(ctx context.Context, req *solpb.GetA return nil, err } - addr, err := solpb.ConvertPublicKeyFromProto(req.GetAccount()) + dReq, err := solpb.ConvertGetAccountInfoRequestFromProto(req) if err != nil { return nil, net.WrapRPCErr(err) } - opts := solpb.ConvertGetAccountInfoOptsFromProto(req.GetOpts()) - - dReq := solana.GetAccountInfoRequest{Account: addr, Opts: opts} dResp, err := solService.GetAccountInfoWithOpts(ctx, dReq) if err != nil { return nil, net.WrapRPCErr(err) diff --git a/pkg/types/chains/solana/solana.go b/pkg/types/chains/solana/solana.go index c350a6ff86..2cd91ab520 100644 --- a/pkg/types/chains/solana/solana.go +++ b/pkg/types/chains/solana/solana.go @@ -350,6 +350,8 @@ type SimulateTXOpts struct { type GetAccountInfoRequest struct { Account PublicKey Opts *GetAccountInfoOpts + // If true, limits like response size limit may be applied. + IsExternal bool } type GetAccountInfoReply struct { @@ -360,6 +362,8 @@ type GetAccountInfoReply struct { type GetMultipleAccountsRequest struct { Accounts []PublicKey Opts *GetMultipleAccountsOpts + // If true, limits like response size limit may be applied. + IsExternal bool } type GetMultipleAccountsReply struct { @@ -477,6 +481,8 @@ type TransactionResultEnvelope struct { // arguments for solana-rpc GetTransaction call type GetTransactionRequest struct { Signature Signature + // If true, limits like response size limit may be applied. + IsExternal bool } // result of solana-rpc GetTransaction call @@ -517,7 +523,9 @@ type SimulateTXRequest struct { Receiver PublicKey // Encoded EncodedTransaction string - Opts *SimulateTXOpts + Opts *SimulateTXOpts + // If true, limits like response size limit may be applied. + IsExternal bool } type SimulateTXReply struct { From 9aa0c6a3be1a2ee50a1765fad59e9c94def2f7f9 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Mon, 18 May 2026 06:31:18 -0400 Subject: [PATCH 2/3] fix format --- pkg/loop/internal/relayerset/solana.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/loop/internal/relayerset/solana.go b/pkg/loop/internal/relayerset/solana.go index 019e37d0f3..e6dfbf1ca2 100644 --- a/pkg/loop/internal/relayerset/solana.go +++ b/pkg/loop/internal/relayerset/solana.go @@ -12,7 +12,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/net" "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb/relayerset" "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink-common/pkg/types/chains/solana" ) // solClient wraps the SolanaRelayerSetClient by attaching a RelayerID to SolClient requests. From 9b1d5ea6ec055747b5d3f394d2e2158989fe4599 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Mon, 18 May 2026 08:52:19 -0400 Subject: [PATCH 3/3] update comments --- pkg/chains/solana/solana.pb.go | 10 +++++----- pkg/chains/solana/solana.proto | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/chains/solana/solana.pb.go b/pkg/chains/solana/solana.pb.go index 682b13eb62..9ca5294269 100644 --- a/pkg/chains/solana/solana.pb.go +++ b/pkg/chains/solana/solana.pb.go @@ -648,7 +648,7 @@ type GetAccountInfoWithOptsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Account []byte `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` // 32-byte Pubkey Opts *GetAccountInfoOpts `protobuf:"bytes,2,opt,name=opts,proto3" json:"opts,omitempty"` - IsExternal bool `protobuf:"varint,3,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` + IsExternal bool `protobuf:"varint,3,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` // if true, limits like response size limit may be applied unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1245,7 +1245,7 @@ type GetMultipleAccountsWithOptsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Accounts [][]byte `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` // list of 32-byte Pubkeys Opts *GetMultipleAccountsOpts `protobuf:"bytes,2,opt,name=opts,proto3" json:"opts,omitempty"` - IsExternal bool `protobuf:"varint,3,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` + IsExternal bool `protobuf:"varint,3,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` // if true, limits like response size limit may be applied unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2431,8 +2431,8 @@ func (x *GetTransactionReply) GetMeta() *TransactionMeta { // GetTransaction request. type GetTransactionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` // 64-byte signature - IsExternal bool `protobuf:"varint,2,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` // 64-byte signature + IsExternal bool `protobuf:"varint,2,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` // if true, limits like response size limit may be applied unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2670,7 +2670,7 @@ type SimulateTXRequest struct { Receiver []byte `protobuf:"bytes,1,opt,name=receiver,proto3" json:"receiver,omitempty"` // 32-byte program id (target) EncodedTransaction string `protobuf:"bytes,2,opt,name=encoded_transaction,json=encodedTransaction,proto3" json:"encoded_transaction,omitempty"` // base64/base58 tx Opts *SimulateTXOpts `protobuf:"bytes,3,opt,name=opts,proto3" json:"opts,omitempty"` - IsExternal bool `protobuf:"varint,4,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` + IsExternal bool `protobuf:"varint,4,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` // if true, limits like response size limit may be applied unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } diff --git a/pkg/chains/solana/solana.proto b/pkg/chains/solana/solana.proto index 69cd273f2d..39f8f935b3 100644 --- a/pkg/chains/solana/solana.proto +++ b/pkg/chains/solana/solana.proto @@ -107,7 +107,7 @@ message GetAccountInfoWithOptsReply { message GetAccountInfoWithOptsRequest { bytes account = 1; // 32-byte Pubkey GetAccountInfoOpts opts = 2; - bool is_external = 3; + bool is_external = 3; // if true, limits like response size limit may be applied } // Reply for GetBalance. @@ -173,7 +173,7 @@ message GetMultipleAccountsWithOptsReply { message GetMultipleAccountsWithOptsRequest { repeated bytes accounts = 1; // list of 32-byte Pubkeys GetMultipleAccountsOpts opts = 2; - bool is_external = 3; + bool is_external = 3; // if true, limits like response size limit may be applied } // Reply for GetSignatureStatuses. @@ -306,7 +306,7 @@ message GetTransactionReply { // GetTransaction request. message GetTransactionRequest { bytes signature = 1; // 64-byte signature - bool is_external = 2; + bool is_external = 2; // if true, limits like response size limit may be applied } // RPC read context. @@ -335,7 +335,7 @@ message SimulateTXRequest { bytes receiver = 1; // 32-byte program id (target) string encoded_transaction = 2; // base64/base58 tx SimulateTXOpts opts = 3; - bool is_external = 4; + bool is_external = 4; // if true, limits like response size limit may be applied } // Accounts to return during simulation.