incubed
Version:
Typescript-version of the incubed client
120 lines • 6.7 kB
JavaScript
;
/***********************************************************
* This file is part of the Slock.it IoT Layer. *
* The Slock.it IoT Layer contains: *
* - USN (Universal Sharing Network) *
* - INCUBED (Trustless INcentivized remote Node Network) *
************************************************************
* Copyright (C) 2016 - 2018 Slock.it GmbH *
* All Rights Reserved. *
************************************************************
* You may use, distribute and modify this code under the *
* terms of the license contract you have concluded with *
* Slock.it GmbH. *
* For information about liability, maintenance etc. also *
* refer to the contract concluded with Slock.it GmbH. *
************************************************************
* For more information, please refer to https://slock.it *
* For questions, please contact info@slock.it *
***********************************************************/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const verify_1 = require("./verify");
const VM = require("ethereumjs-vm");
const Account = require("ethereumjs-account");
const Block = require("ethereumjs-block");
const Trie = require("merkle-patricia-tree");
const ethereumjs_util_1 = require("ethereumjs-util");
const serialize_1 = require("./serialize");
const util_1 = require("../../util/util");
/** executes a transaction-call to a smart contract */
function executeCall(args, accounts, block) {
return __awaiter(this, void 0, void 0, function* () {
// fix account-keys, so all the addresses are formated the same way
Object.keys(accounts).forEach(a => accounts[util_1.toHex(a, 20).toLowerCase()] = accounts[a]);
// create new state for a vm
const state = new Trie();
const vm = new VM({ state });
// set all storage values from the proof in the state
yield setStorageFromProof(state, accounts);
// create a transaction-object
const tx = serialize_1.createTx(Object.assign({ gas: '0x5b8d80', gasLimit: '0x5b8d80', from: '0x0000000000000000000000000000000000000000' }, args));
// keep track of each opcode in order to make sure, all storage-values are provided!
let missingDataError = null;
vm.on('step', ev => {
// TODO als check the following opcodes:
// - BLOCKHASH
// - COINBASE ( since we are currently not using a real block!)
// and we need to check if the target contract exists (even though it would most likely fail if not)
// - STATIONCALL
switch (ev.opcode.name) {
case 'BALANCE':
case 'EXTCODESIZE':
case 'EXTCODECOPY':
const balanceContract = util_1.toHex('0x' + ev.stack[ev.stack.length - 1].toString(16), 20);
if (!(accounts[balanceContract]))
missingDataError = new Error('The contract ' + balanceContract + ' is used to get the balance but is missing in the proof!');
break;
case 'CALL':
case 'CALLCODE':
case 'DELEGATECALL':
case 'STATICCALL':
const callContract = util_1.toHex('0x' + ev.stack[ev.stack.length - 2].toString(16), 20);
if (!(accounts[callContract]))
missingDataError = new Error('The contract ' + callContract + ' is used to get the balance but is missing in the proof!');
break;
case 'SLOAD':
const contract = util_1.toHex(ev.address, 20);
const key = serialize_1.bytes32(ev.stack[ev.stack.length - 1]);
const ac = accounts[contract];
// check if this key is part of the acountProof, if not the result can not be trusted
if (!ac)
missingDataError = new Error('The contract ' + contract + ' is used but is missing in the proof! proof=' + JSON.stringify(accounts, null, 2));
else if (!verify_1.getStorageValue(ac, key))
missingDataError = new Error('The storage value ' + key + ' in ' + contract + ' is used but is missing in the proof!');
break;
default:
return;
}
// console.log('step ' + counter + ' : ' + ev.opcode.name + ' pc:' + ev.pc + 'stack: ' + ev.stack.map(_ => _.toString(16)))
});
// run the tx
const result = yield util_1.promisify(vm, vm.runTx, { tx, block: new Block([block, [], []]) });
// return the returnValue
if (missingDataError)
throw missingDataError;
return result.vm.return;
});
}
exports.executeCall = executeCall;
function setStorageFromProof(trie, accounts) {
return __awaiter(this, void 0, void 0, function* () {
for (const adr of Object.keys(accounts)) {
const ac = accounts[adr];
// create an account-object
const account = new Account();
if (ac.balance)
account.balance = ac.balance;
if (ac.nonce)
account.nonce = ac.nonce;
if (ac.codeHash)
account.codeHash = ac.codeHash;
// if we have a code, we will set the code
if (ac.code)
yield util_1.promisify(account, account.setCode, trie, util_1.toBuffer(ac.code));
// set all storage-values
for (const s of ac.storageProof)
yield util_1.promisify(account, account.setStorage, trie, util_1.toBuffer(s.key, 32), ethereumjs_util_1.rlp.encode(util_1.toBuffer(s.value, 32)));
// set the account data
yield util_1.promisify(trie, trie.put, util_1.toBuffer(adr, 20), account.serialize());
}
});
}
//# sourceMappingURL=call.js.map