Skip to content

Commit

Permalink
CCQ: Solana mock should support min_context_slot (#3728)
Browse files Browse the repository at this point in the history
* CCQ: Solana mock should support min_context_slot

* Minor tweaks from PR 3637
  • Loading branch information
bruce-riley authored Jan 23, 2024
1 parent 59dff67 commit 846c2e9
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 18 deletions.
2 changes: 1 addition & 1 deletion node/cmd/ccq/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func validateSolanaAccountQuery(logger *zap.Logger, permsForUser *permissionEntr
if _, exists := permsForUser.allowedCalls[callKey]; !exists {
logger.Debug("requested call not authorized", zap.String("userName", permsForUser.userName), zap.String("callKey", callKey))
invalidQueryRequestReceived.WithLabelValues("call_not_authorized").Inc()
return http.StatusBadRequest, fmt.Errorf(`call "%s" not authorized`, callKey)
return http.StatusForbidden, fmt.Errorf(`call "%s" not authorized`, callKey)
}

totalRequestedCallsByChain.WithLabelValues(chainId.String()).Inc()
Expand Down
6 changes: 3 additions & 3 deletions node/pkg/query/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const EvmContractAddressLength = 20
// SolanaAccountQueryRequestType is the type of a Solana sol_account query request.
const SolanaAccountQueryRequestType ChainSpecificQueryType = 4

// SolanaAccountQueryRequest implements ChainSpecificQuery for an EVM eth_call query request.
// SolanaAccountQueryRequest implements ChainSpecificQuery for a Solana sol_account query request.
type SolanaAccountQueryRequest struct {
// Commitment identifies the commitment level to be used in the queried. Currently it may only "finalized".
// Before we can support "confirmed", we need a way to read the account data and the block information atomically.
Expand Down Expand Up @@ -1002,7 +1002,7 @@ func (saq *SolanaAccountQueryRequest) UnmarshalFromReader(reader *bytes.Reader)

// Validate does basic validation on a Solana sol_account query.
func (saq *SolanaAccountQueryRequest) Validate() error {
if len(saq.Commitment) > math.MaxUint32 {
if len(saq.Commitment) > SolanaMaxCommitmentLength {
return fmt.Errorf("commitment too long")
}
if saq.Commitment != "finalized" {
Expand All @@ -1017,7 +1017,7 @@ func (saq *SolanaAccountQueryRequest) Validate() error {
return fmt.Errorf("does not contain any account entries")
}
if len(saq.Accounts) > SolanaMaxAccountsPerQuery {
return fmt.Errorf("too many account entries, may not be more that %d", SolanaMaxAccountsPerQuery)
return fmt.Errorf("too many account entries, may not be more than %d", SolanaMaxAccountsPerQuery)
}
for _, acct := range saq.Accounts {
// The account is fixed length, so don't need to check for nil.
Expand Down
2 changes: 1 addition & 1 deletion node/pkg/query/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ func (sar *SolanaAccountQueryResponse) Type() ChainSpecificQueryType {
}

// Marshal serializes the binary representation of a Solana sol_account response.
// This method calls Validate() and relies on it to range checks lengths, etc.
// This method calls Validate() and relies on it to range check lengths, etc.
func (sar *SolanaAccountQueryResponse) Marshal() ([]byte, error) {
if err := sar.Validate(); err != nil {
return nil, err
Expand Down
35 changes: 23 additions & 12 deletions sdk/js-query/src/mock/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ import {
} from "../query";
import { BinaryWriter } from "../query/BinaryWriter";

interface SolanaGetMultipleAccountsOpts {
commitment: string;
minSlotContext?: number;
dataSlice?: SolanaDataSlice;
}

interface SolanaDataSlice {
offset: number;
length: number;
}

type SolanaAccountData = {
data: [string, string];
executable: boolean;
Expand Down Expand Up @@ -399,18 +410,18 @@ export class QueryProxyMock {
accounts.push(base58.encode(acct));
});

let opts =
query.dataSliceOffset === BigInt(0)
? {
commitment: query.commitment,
}
: {
commitment: query.commitment,
dataSlice: {
offset: Number(query.dataSliceOffset),
length: Number(query.dataSliceLength),
},
};
let opts: SolanaGetMultipleAccountsOpts = {
commitment: query.commitment,
};
if (query.minContextSlot != BigInt(0)) {
opts.minSlotContext = Number(query.minContextSlot);
}
if (query.dataSliceOffset !== BigInt(0)) {
opts.dataSlice = {
offset: Number(query.dataSliceOffset),
length: Number(query.dataSliceLength),
};
}

const response = await axios.post<SolanaGetMultipleAccountsResponse>(
rpc,
Expand Down
52 changes: 52 additions & 0 deletions sdk/js-query/src/mock/mock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,29 @@ describe.skip("mocks match testnet", () => {
"01000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"
);
});
test("SolAccount to devnet with min context slot", async () => {
const accounts = [
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ", // Example token in devnet
"BVxyYhm498L79r4HMQ9sxZ5bi41DmJmeWZ7SCS7Cyvna", // Example NFT in devnet
];

const query = new QueryRequest(42, [
new PerChainQueryRequest(
1,
new SolanaAccountQueryRequest("finalized", accounts, BigInt(7))
),
]);
const resp = await mock.mock(query);
const queryResponse = QueryResponse.from(resp.bytes);
const sar = queryResponse.responses[0]
.response as SolanaAccountQueryResponse;
expect(Buffer.from(sar.results[0].data).toString("hex")).toEqual(
"01000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a0901000000000000000000000000000000000000000000000000000000000000000000000000"
);
expect(Buffer.from(sar.results[1].data).toString("hex")).toEqual(
"01000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"
);
});
test("SolAccount to devnet with data slice", async () => {
const accounts = [
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ", // Example token in devnet
Expand Down Expand Up @@ -289,4 +312,33 @@ describe.skip("mocks match testnet", () => {
"000000574108aed69daf"
);
});
test("SolAccount to devnet with min context slot and data slice", async () => {
const accounts = [
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ", // Example token in devnet
"BVxyYhm498L79r4HMQ9sxZ5bi41DmJmeWZ7SCS7Cyvna", // Example NFT in devnet
];

const query = new QueryRequest(42, [
new PerChainQueryRequest(
1,
new SolanaAccountQueryRequest(
"finalized",
accounts,
BigInt(7),
BigInt(1),
BigInt(10)
)
),
]);
const resp = await mock.mock(query);
const queryResponse = QueryResponse.from(resp.bytes);
const sar = queryResponse.responses[0]
.response as SolanaAccountQueryResponse;
expect(Buffer.from(sar.results[0].data).toString("hex")).toEqual(
"000000574108aed69daf"
);
expect(Buffer.from(sar.results[1].data).toString("hex")).toEqual(
"000000574108aed69daf"
);
});
});
2 changes: 1 addition & 1 deletion whitepapers/0013_ccq.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ uint32 response_len
- The `slot_number` is the slot number returned by the query.
- The `block_time_us` is the timestamp of the block associated with the slot.
- The `block_hash` is the block hash associated with the slot.
- The array of results array returns the data for each account queried.
- The `results` array returns the data for each account queried

```go
u64 lamports
Expand Down

0 comments on commit 846c2e9

Please sign in to comment.