Skip to content

Commit

Permalink
Refactor JSON-RPC, improve error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
vgrichina committed Jan 2, 2024
1 parent 4125b11 commit 74d8c92
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 46 deletions.
65 changes: 31 additions & 34 deletions json-rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ const legacyError = ({ id, message }) => {

const ALWAYS_PROXY = ['yes', 'true'].includes((process.env.FAST_NEAR_ALWAYS_PROXY || 'no').trim().toLowerCase());

const handleError = async ({ ctx, accountId, blockHeight, error }) => {
const handleError = async ({ ctx, blockHeight, error }) => {
console.log('handleError', error);
const { body } = ctx.request;
const accountId = error.accountId;

// TODO: Match error handling? Structured errors? https://docs.near.org/docs/api/rpc/contracts#what-could-go-wrong-6
const message = error.toString();
Expand Down Expand Up @@ -128,7 +129,6 @@ const handleJsonRpc = async ctx => {
const { body } = ctx.request;

if (body?.method == 'query') {
let accountId;
const { finality, block_id } = body.params;
// TODO: Determine proper way to handle finality. Depending on what indexer can do maybe just redirect to nearcore if not final

Expand All @@ -142,45 +142,42 @@ const handleJsonRpc = async ctx => {
debug('blockHeight', blockHeight);

try {
if (body?.params?.request_type == 'call_function') {
const { account_id, method_name: methodName, args_base64 } = body.params;
accountId = account_id;
rpcResult(ctx, await callViewFunction({ blockHeight, accountId, methodName, args: Buffer.from(args_base64, 'base64') }));
return;
}

if (body?.params?.request_type == 'view_account') {
const { account_id } = body.params;
accountId = account_id;
rpcResult(ctx, await viewAccount({ blockHeight, accountId }));
return;
}

if (body?.params?.length) {
const query = body.params[0];
if (query?.startsWith('account/')) {
[, accountId] = query.split('/');
rpcResult(ctx, await viewAccount({ blockHeight, accountId }));
return;
}

if (query?.startsWith('call/')) {
let methodName;
[, accountId, methodName] = query.split('/');
const args = bs58.decode(body.params[1], 'base64');
rpcResult(ctx, await callViewFunction({ blockHeight, accountId, methodName, args }));
return;
}
}
return rpcResult(ctx, await handleQuery({ blockHeight, body }));
} catch (error) {
await handleError({ ctx, accountId, blockHeight, error });
await handleError({ ctx, blockHeight, error });
return;
}
}

await proxyJson(ctx);
};

async function handleQuery({ blockHeight, body }) {
if (body?.params?.request_type == 'call_function') {
const { account_id, method_name: methodName, args_base64 } = body.params;
return await callViewFunction({ blockHeight, accountId: account_id, methodName, args: Buffer.from(args_base64, 'base64') });
}

if (body?.params?.request_type == 'view_account') {
const { account_id } = body.params;
return await viewAccount({ blockHeight, accountId: account_id });
}

if (body?.params?.length) {
const query = body.params[0];
if (query?.startsWith('account/')) {
const [, accountId] = query.split('/');
return await viewAccount({ blockHeight, accountId });
}

if (query?.startsWith('call/')) {
const [, accountId, methodName] = query.split('/');
const args = bs58.decode(body.params[1], 'base64');
return await callViewFunction({ blockHeight, accountId, methodName, args });
}
}
}

const callViewFunction = async ({ blockHeight, accountId, methodName, args }) => {
const { result, logs, blockHeight: resolvedBlockHeight } = await runContract(accountId, methodName, args, blockHeight);
const resultBuffer = Buffer.from(result);
Expand All @@ -198,7 +195,7 @@ const viewAccount = async ({ blockHeight, accountId }) => {
const accountData = await storage.getLatestData(compKey, blockHeight);
debug('account data loaded', accountId);
if (!accountData) {
throw new FastNEARError('accountNotFound', `Account not found: ${accountId} at ${blockHeight} block height`);
throw new FastNEARError('accountNotFound', `Account not found: ${accountId} at ${blockHeight} block height`, { accountId, blockHeight });
}

const { amount, locked, code_hash, storage_usage } = deserialize(BORSH_SCHEMA, Account, accountData);
Expand Down
4 changes: 2 additions & 2 deletions resolve-block-height.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ async function resolveBlockHeight(blockHeight) {
const latestBlockHeight = await storage.getLatestBlockHeight();
blockHeight = blockHeight || latestBlockHeight;
if (parseInt(blockHeight, 10) > parseInt(latestBlockHeight, 10)) {
throw new FastNEARError('blockHeightTooHigh', `Block height not found: ${blockHeight}`);
throw new FastNEARError('blockHeightTooHigh', `Block height not found: ${blockHeight}`, { blockHeight, latestBlockHeight });
}

if (parseInt(blockHeight, 10) < parseInt(START_BLOCK_HEIGHT, 10)) {
throw new FastNEARError('blockHeightTooLow', `Block height not found: ${blockHeight}`);
throw new FastNEARError('blockHeightTooLow', `Block height not found: ${blockHeight}`, { blockHeight, startBlockHeight: START_BLOCK_HEIGHT });
}

return blockHeight;
Expand Down
14 changes: 7 additions & 7 deletions run-contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ let contractCache = new LRU({

let workerPool;

async function getWasmModule(contractId, blockHeight) {
const debug = require('debug')(`host:${contractId}`);
debug('getWasmModule', contractId, blockHeight);
async function getWasmModule(accountId, blockHeight) {
const debug = require('debug')(`host:${accountId}`);
debug('getWasmModule', accountId, blockHeight);

const accountDataKey = accountKey(contractId);
const accountDataKey = accountKey(accountId);

debug('load account data')
const accountData = await storage.getLatestData(accountDataKey, blockHeight);
debug('accountData', accountData);
if (!accountData) {
throw new FastNEARError('accountNotFound', `Account not found: ${contractId} at ${blockHeight} block height`);
throw new FastNEARError('accountNotFound', `Account not found: ${accountId} at ${blockHeight} block height`, { accountId, blockHeight });
}

const { code_hash } = deserialize(BORSH_SCHEMA, Account, accountData);
const codeHash = Buffer.from(code_hash);
const codeHashStr = bs58.encode(codeHash);

if (NO_CODE_HASH.equals(codeHash)) {
throw new FastNEARError('codeNotFound', `Cannot find contract code: ${contractId}, block height: ${blockHeight}, code hash: ${codeHashStr}`);
throw new FastNEARError('codeNotFound', `Cannot find contract code: ${accountId}, block height: ${blockHeight}, code hash: ${codeHashStr}`, { accountId, blockHeight, codeHashStr });
}

const cacheKey = codeHashStr;
Expand All @@ -48,7 +48,7 @@ async function getWasmModule(contractId, blockHeight) {
const wasmData = await storage.getBlob(codeHash);
if (!wasmData) {
// TODO: Should this be fatal error because shoudn't happen with consistent data?
throw new FastNEARError('codeNotFound', `Cannot find contract code: ${contractId}, block height: ${blockHeight}, code hash: ${codeHashStr}`);
throw new FastNEARError('codeNotFound', `Cannot find contract code: ${accountId}, block height: ${blockHeight}, code hash: ${codeHashStr}`, { accountId, blockHeight, codeHashStr });
}
debug('wasmData.length', wasmData.length);

Expand Down
2 changes: 1 addition & 1 deletion worker-pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class WorkerPool extends EventEmitter {
if (worker[kTaskInfo]) {
const { contractId, methodName, didTimeout, reject } = worker[kTaskInfo]
if (didTimeout) {
err = new FastNEARError('executionTimedOut', `${contractId}.${methodName} execution timed out`);
err = new FastNEARError('executionTimedOut', `${contractId}.${methodName} execution timed out`, { accountId: contractId, methodName });
}
reject(err)
} else {
Expand Down
4 changes: 2 additions & 2 deletions worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ const MAX_U64 = 18446744073709551615n;

const notImplemented = (name) => (...args) => {
debug('notImplemented', name, 'args', args);
throw new FastNEARError('notImplemented', 'method not implemented: ' + name);
throw new FastNEARError('notImplemented', 'method not implemented: ' + name, { methodName: name });
};

const prohibitedInView = (name) => (...args) => {
debug('prohibitedInView', name, 'args', args);
throw new FastNEARError('notImplemented', 'method not available for view calls: ' + name);
throw new FastNEARError('notImplemented', 'method not available for view calls: ' + name, { methodName: name });
};

const imports = (ctx) => {
Expand Down

0 comments on commit 74d8c92

Please sign in to comment.