-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract runtime into separate module
- Loading branch information
Showing
2 changed files
with
205 additions
and
198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
const { createHash } = require('crypto'); | ||
|
||
const { dataKey } = require('../storage-keys'); | ||
const prettyBuffer = require('../utils/pretty-buffer'); | ||
const { FastNEARError } = require('../error'); | ||
|
||
const MAX_U64 = 18446744073709551615n; | ||
|
||
const notImplemented = (name) => (...args) => { | ||
debug('notImplemented', name, 'args', args); | ||
throw new FastNEARError('notImplemented', 'method not implemented: ' + name, { methodName: name }); | ||
}; | ||
|
||
const prohibitedInView = (name) => (...args) => { | ||
debug('prohibitedInView', name, 'args', args); | ||
// TODO: Shouldn't this use unique code which is not resulting in proxyJson? | ||
throw new FastNEARError('notImplemented', 'method not available for view calls: ' + name, { methodName: name }); | ||
}; | ||
|
||
const imports = (ctx) => { | ||
const debug = require('debug')(`worker:runtime:${ctx.threadId}`); | ||
const registers = {}; | ||
|
||
function readUTF16CStr(ptr) { | ||
let arr = []; | ||
const mem = new Uint16Array(ctx.memory.buffer); | ||
ptr = Number(ptr) / 2; | ||
while (mem[ptr] != 0) { | ||
arr.push(mem[ptr]); | ||
ptr++; | ||
} | ||
return Buffer.from(Uint16Array.from(arr).buffer).toString('ucs2'); | ||
} | ||
|
||
function readUTF8CStr(len, ptr) { | ||
let arr = []; | ||
const mem = new Uint8Array(ctx.memory.buffer); | ||
ptr = Number(ptr); | ||
for (let i = 0; i < len && mem[ptr] != 0; i++) { | ||
arr.push(mem[ptr]); | ||
ptr++; | ||
} | ||
return Buffer.from(arr).toString('utf8'); | ||
} | ||
|
||
function storageRead(key_len, key_ptr) { | ||
const storageKey = Buffer.from(new Uint8Array(ctx.memory.buffer, Number(key_ptr), Number(key_len))); | ||
const compKey = dataKey(ctx.contractId, storageKey); | ||
debug('storage_read', ctx.contractId, prettyBuffer(storageKey)); | ||
|
||
ctx.parentPort.postMessage({ | ||
methodName: 'storage_read', | ||
compKey | ||
}); | ||
|
||
let resultMessage | ||
do { | ||
resultMessage = ctx.receiveMessageOnPort(ctx.parentPort); | ||
} while (!resultMessage); | ||
return resultMessage.message; | ||
} | ||
|
||
|
||
return { | ||
// NOTE: See https://github.com/near/nearcore/blob/master/runtime/near-vm-runner/src/logic/logic.rs | ||
register_len: (register_id) => { | ||
debug('register_len', register_id); | ||
debug('registers[register_id].length', registers[register_id].length); | ||
return BigInt(registers[register_id] ? registers[register_id].length : MAX_U64); | ||
}, | ||
read_register: (register_id, ptr) => { | ||
debug('read_register', register_id, ptr); | ||
debug('registers[register_id]', registers[register_id]); | ||
const mem = new Uint8Array(ctx.memory.buffer) | ||
mem.set(registers[register_id] || Buffer.from([]), Number(ptr)); | ||
}, | ||
current_account_id: (register_id) => { | ||
registers[register_id] = Buffer.from(ctx.contractId); | ||
}, | ||
signer_account_id: prohibitedInView('signer_account_id'), | ||
signer_account_pk: prohibitedInView('signer_account_pk'), | ||
predecessor_account_id: prohibitedInView('predecessor_account_id'), | ||
input: (register_id) => { | ||
debug('input', register_id); | ||
registers[register_id] = Buffer.from(ctx.methodArgs); | ||
}, | ||
block_index: () => { | ||
return BigInt(ctx.blockHeight); | ||
}, | ||
block_timestamp: () => { | ||
return BigInt(ctx.blockTimestamp); | ||
}, | ||
epoch_height: notImplemented('epoch_height'), | ||
storage_usage: notImplemented('storage_usage'), | ||
|
||
account_balance: notImplemented('account_balance'), // TODO: Implement as needed for IDO usage | ||
account_locked_balance: notImplemented('account_locked_balance'), | ||
attached_deposit: prohibitedInView('attached_deposit'), | ||
prepaid_gas: prohibitedInView('prepaid_gas'), | ||
used_gas: prohibitedInView('used_gas'), | ||
|
||
random_seed: notImplemented('random_seed'), | ||
sha256: (value_len, value_ptr, register_id) => { | ||
const value = new Uint8Array(ctx.memory.buffer, Number(value_ptr), Number(value_len)); | ||
const hash = createHash('sha256'); | ||
hash.update(value); | ||
registers[register_id] = hash.digest(); | ||
}, | ||
keccak256: notImplemented('keccak256'), | ||
keccak512: notImplemented('keccak512'), | ||
ripemd160: notImplemented('ripemd160'), | ||
ecrecover: notImplemented('ecrecover'), | ||
ed25519_verify: notImplemented('ed25519_verify'), | ||
|
||
value_return: (value_len, value_ptr) => { | ||
debug('value_return', value_len, value_ptr); | ||
ctx.result = Buffer.from(new Uint8Array(ctx.memory.buffer, Number(value_ptr), Number(value_len))); | ||
}, | ||
panic: () => { | ||
const message = `explicit guest panic` | ||
debug('panic', message); | ||
throw new FastNEARError('panic', message); | ||
}, | ||
panic_utf8: (len, ptr) => { | ||
const message = readUTF8CStr(len, ptr); | ||
debug('panic', message); | ||
throw new FastNEARError('panic', message); | ||
}, | ||
abort: (msg_ptr, filename_ptr, line, col) => { | ||
const msg = readUTF16CStr(msg_ptr); | ||
const filename = readUTF16CStr(filename_ptr); | ||
const message = `${msg} ${filename}:${line}:${col}` | ||
debug('abort', msg_ptr, filename_ptr, line, col, message); | ||
if (!msg || !filename) { | ||
// TODO: Check when exactly this gets thrown in nearcore, as it's weird choice for empty C string | ||
// TODO: Check what happens with actual invalid UTF-16 | ||
throw new FastNEARError('abort', 'String encoding is bad UTF-16 sequence.'); | ||
} | ||
throw new FastNEARError('abort', message); | ||
}, | ||
log_utf8: (len, ptr) => { | ||
const message = readUTF8CStr(len, ptr); | ||
debug(`log: ${message}`); | ||
ctx.logs.push(message); | ||
}, | ||
log_utf16: (len, ptr) => { | ||
const message = readUTF8CStr(len, ptr); | ||
debug(`log: ${message}`); | ||
ctx.logs.push(message); | ||
}, | ||
|
||
promise_create: prohibitedInView('promise_create'), | ||
promise_then: prohibitedInView('promise_then'), | ||
promise_and: prohibitedInView('promise_and'), | ||
promise_batch_create: prohibitedInView('promise_batch_create'), | ||
promise_batch_then: prohibitedInView('promise_batch_then'), | ||
promise_batch_action_create_account: prohibitedInView('promise_batch_action_create_account'), | ||
promise_batch_action_deploy_contract: prohibitedInView('promise_batch_action_deploy_contract'), | ||
promise_batch_action_function_call: prohibitedInView('promise_batch_action_function_call'), | ||
promise_batch_action_function_call_weight: prohibitedInView('promise_batch_action_function_call_weight'), | ||
promise_batch_action_transfer: prohibitedInView('promise_batch_action_transfer'), | ||
promise_batch_action_stake: prohibitedInView('promise_batch_action_stake'), | ||
promise_batch_action_add_key_with_full_access: prohibitedInView('promise_batch_action_add_key_with_full_access'), | ||
promise_batch_action_add_key_with_function_call: prohibitedInView('promise_batch_action_add_key_with_function_call'), | ||
promise_batch_action_delete_key: prohibitedInView('promise_batch_action_delete_key'), | ||
promise_batch_action_delete_account: prohibitedInView('promise_batch_action_delete_account'), | ||
promise_results_count: prohibitedInView('promise_results_count'), | ||
promise_result: prohibitedInView('promise_result'), | ||
promise_return: prohibitedInView('promise_return'), | ||
|
||
storage_write: prohibitedInView('storage_write'), | ||
storage_read: (key_len, key_ptr, register_id) => { | ||
const result = storageRead(key_len, key_ptr); | ||
|
||
if (result == null) { | ||
debug('storage_read result: none'); | ||
return 0n; | ||
} | ||
|
||
registers[register_id] = result; | ||
debug('storage_read result', prettyBuffer(Buffer.from(result))); | ||
return 1n; | ||
}, | ||
storage_remove: prohibitedInView('storage_remove'), | ||
storage_has_key: (key_len, key_ptr) => { | ||
const result = storageRead(key_len, key_ptr); | ||
|
||
if (result == null) { | ||
debug('storage_has_key: false'); | ||
return 0n; | ||
} | ||
|
||
debug('storage_has_key: true'); | ||
return 1n; | ||
}, | ||
validator_stake: notImplemented('validator_stake'), | ||
validator_total_stake: notImplemented('validator_total_stake'), | ||
} | ||
}; | ||
|
||
module.exports = imports; |
Oops, something went wrong.