Change the way we access the stack in traces

This commit is contained in:
Leonid Logvinov
2019-02-28 20:19:05 -08:00
parent bb346537ba
commit 46bc5463ca
6 changed files with 26 additions and 19 deletions

View File

@@ -1,4 +1,4 @@
import { TraceInfo } from '@0x/sol-tracing-utils';
import { TraceInfo, constants } from '@0x/sol-tracing-utils';
import { logUtils } from '@0x/utils';
import { OpCode } from 'ethereum-types';
import { stripHexPrefix } from 'ethereumjs-util';
@@ -47,11 +47,17 @@ export const costUtils = {
);
const memoryLocationsAccessed = _.map(memoryLogs, structLog => {
if (_.includes(CALL_DATA_OPCODES, structLog.op)) {
const memOffset = parseInt(structLog.stack[0], HEX_BASE);
const length = parseInt(structLog.stack[2], HEX_BASE);
const memoryOffsetStackOffset = constants.opCodeToParamToStackOffset[structLog.op as any].memoryOffset;
const lengthStackOffset = constants.opCodeToParamToStackOffset[structLog.op as any].length;
const memOffset = parseInt(
structLog.stack[structLog.stack.length - memoryOffsetStackOffset - 1],
HEX_BASE,
);
const length = parseInt(structLog.stack[structLog.stack.length - lengthStackOffset - 1], HEX_BASE);
return memOffset + length;
} else {
return parseInt(structLog.stack[structLog.stack.length - 1], HEX_BASE);
const memoryLocationStackOffset = constants.opCodeToParamToStackOffset[structLog.op].offset;
return parseInt(structLog.stack[structLog.stack.length - memoryLocationStackOffset - 1], HEX_BASE);
}
});
const highestMemoryLocationAccessed = _.max(memoryLocationsAccessed);
@@ -62,7 +68,8 @@ export const costUtils = {
const COPY_OPCODES = [OpCode.CallDataCopy];
const copyLogs = _.filter(structLogs, structLog => _.includes(COPY_OPCODES, structLog.op));
const copyCosts = _.map(copyLogs, structLog => {
const length = parseInt(structLog.stack[2], HEX_BASE);
const lengthStackOffset = constants.opCodeToParamToStackOffset[structLog.op as any].length;
const length = parseInt(structLog.stack[structLog.stack.length - lengthStackOffset - 1], HEX_BASE);
return Math.ceil(length / WORD_SIZE) * G_COPY;
});
return _.sum(copyCosts);

View File

@@ -65,7 +65,7 @@ export class ProfilerSubprovider extends TraceInfoSubprovider {
const transactionBaseCost = BASE_COST;
let totalCost = callDataCost + opcodesCost + BASE_COST;
logUtils.header('Final breakdown', '-');
if (!_.isNull(receipt.contractAddress)) {
if (_.isString(receipt.contractAddress)) {
const code = await this._web3Wrapper.getContractCodeAsync(receipt.contractAddress);
const codeBuff = Buffer.from(stripHexPrefix(code), 'hex');
const codeLength = codeBuff.length;

View File

@@ -3,6 +3,7 @@ import { OpCode, StructLog } from 'ethereum-types';
import * as _ from 'lodash';
import { constants } from './constants';
import { EvmCallStack } from './types';
import { utils } from './utils';
@@ -29,9 +30,8 @@ export function getRevertTrace(structLogs: StructLog[], startAddress: string): E
if (utils.isCallLike(structLog.op)) {
const currentAddress = _.last(addressStack) as string;
const jumpAddressOffset = 1;
const newAddress = utils.getAddressFromStackEntry(
structLog.stack[structLog.stack.length - jumpAddressOffset - 1],
structLog.stack[structLog.stack.length - constants.opCodeToParamToStackOffset[OpCode.Call].to - 1],
);
// Sometimes calls don't change the execution context (current address). When we do a transfer to an

View File

@@ -3,6 +3,7 @@ import { OpCode, StructLog } from 'ethereum-types';
import * as _ from 'lodash';
import { utils } from './utils';
import { constants } from './constants';
export interface ContractAddressToTraces {
[contractAddress: string]: StructLog[];
@@ -33,9 +34,8 @@ export function getContractAddressToTraces(structLogs: StructLog[], startAddress
if (utils.isCallLike(structLog.op)) {
const currentAddress = _.last(addressStack) as string;
const jumpAddressOffset = 1;
const newAddress = utils.getAddressFromStackEntry(
structLog.stack[structLog.stack.length - jumpAddressOffset - 1],
structLog.stack[structLog.stack.length - constants.opCodeToParamToStackOffset[OpCode.Call].to - 1],
);
// Sometimes calls don't change the execution context (current address). When we do a transfer to an

View File

@@ -52,11 +52,11 @@ export abstract class TraceInfoSubprovider extends TraceCollectionSubprovider {
const isCallDataAccess = opn == 0x37;
var stack;
if (isCall) {
stack = [null, '0x'+log.stack.peek(1).toString(16)];
stack = ['0x'+log.stack.peek(1).toString(16), null];
} else if (isMemoryAccess) {
stack = ['0x'+log.stack.peek(0).toString(16)];
} else if (isCallDataAccess) {
stack = ['0x'+log.stack.peek(0).toString(16), '0x'+log.stack.peek(1).toString(16), '0x'+log.stack.peek(2).toString(16)];
stack = ['0x'+log.stack.peek(2).toString(16), '0x'+log.stack.peek(1).toString(16), '0x'+log.stack.peek(0).toString(16)];
}
this.data.push({ pc, gasCost, depth, op, stack, gas });
},

View File

@@ -3,11 +3,9 @@ import { OpCode, StructLog } from 'ethereum-types';
import { addHexPrefix } from 'ethereumjs-util';
import * as _ from 'lodash';
import { constants } from './constants';
import { ContractData, LineColumn, SingleFileSourceRange } from './types';
const STATICCALL_GAS_COST = 40;
const CALL_GAS_COST = 700;
const bytecodeToContractDataIfExists: { [bytecode: string]: ContractData | undefined } = {};
export const utils = {
@@ -95,14 +93,16 @@ export const utils = {
structLog.op === OpCode.StaticCall
? {
...structLog,
gasCost: STATICCALL_GAS_COST,
gasCost: constants.opCodeToGasCost[structLog.op],
}
: structLog;
// HACK(leo): Geth traces sometimes returns those gas costs incorrectly as very big numbers so we manually fix them.
const normalizeCallCost = (structLog: StructLog, index: number) => {
if (structLog.op === OpCode.Call) {
const HEX_BASE = 16;
const callAddress = parseInt(structLog.stack[0], HEX_BASE);
const callAddress = parseInt(
structLog.stack[structLog.stack.length - constants.opCodeToParamToStackOffset[OpCode.Call].to - 1],
constants.HEX_BASE,
);
const MAX_REASONABLE_PRECOMPILE_ADDRESS = 100;
if (callAddress < MAX_REASONABLE_PRECOMPILE_ADDRESS) {
const nextStructLog = normalizedStructLogs[index + 1];
@@ -114,7 +114,7 @@ export const utils = {
} else {
return {
...structLog,
gasCost: CALL_GAS_COST,
gasCost: constants.opCodeToGasCost[structLog.op],
};
}
} else {