blockscout / blockscout

Blockchain explorer for Ethereum based network and a tool for inspecting and analyzing EVM based blockchains.
http://docs.blockscout.com
GNU General Public License v3.0
3.57k stars 2.38k forks source link

Transport IPC with geth variant broken #3670

Open DefiCake opened 3 years ago

DefiCake commented 3 years ago

The indexer breaks when using IPC transport trying to call debug_traceTransaction . Using HTTP works fine.

Environment

Steps to reproduce

Run geth normally and try to run blockscout with IPC.

Expected behaviour

Should work

Actual behaviour

Brings CPU utilization to 100% and pollutes the logs Throws:

2021-02-27T14:33:34.261 application=indexer fetcher=internal_transaction count=10 error_count=10 [error] failed to fetch internal transactions for blocks: {:timeout, {GenServer, :call, [#PID<0.433.0>, {:request, "[{\"id\":0,\"jsonrpc\":\"2.0\",\"method\":\"debug_traceTransaction\",\"params\":[\"0x07fccdf0aec16f334cc49ffc1381756e382010b90893a30464f5d29d28f3d293\",{\"tracer\":\"// tracer allows Geth's `debug_traceTransaction` to mimic the output of Parity's `trace_replayTransaction`\\n{\\n    // The call stack of the EVM execution.\\n    callStack: [{}],\\n\\n    // step is invoked for every opcode that the VM executes.\\n    step(log, db) {\\n        // Capture any errors immediately\\n        const error = log.getError();\\n\\n        if (error !== undefined) {\\n            this.fault(log, db);\\n        } else {\\n            this.success(log, db);\\n        }\\n    },\\n\\n    // fault is invoked when the actual execution of an opcode fails.\\n    fault(log, db) {\\n        // If the topmost call already reverted, don't handle the additional fault again\\n        if (this.topCall().error === undefined) {\\n            this.putError(log);\\n        }\\n    },\\n\\n    putError(log) {\\n        if (this.callStack.length > 1) {\\n            this.putErrorInTopCall(log);\\n        } else {\\n            this.putErrorInBottomCall(log);\\n        }\\n    },\\n\\n    putErrorInTopCall(log) {\\n        // Pop off the just failed call\\n        const call = this.callStack.pop();\\n        this.putErrorInCall(log, call);\\n        this.pushChildCall(call);\\n    },\\n\\n    putErrorInBottomCall(log) {\\n        const call = this.bottomCall();\\n        this.putErrorInCall(log, call);\\n    },\\n\\n    putErrorInCall(log, call) {\\n        call.error = log.getError();\\n\\n        // Consume all available gas and clean any leftovers\\n        if (call.gasBigInt !== undefined) {\\n            call.gasUsedBigInt = call.gasBigInt;\\n        }\\n\\n        delete call.outputOffset;\\n        delete call.outputLength;\\n    },\\n\\n    topCall() {\\n        return this.callStack[this.callStack.length - 1];\\n    },\\n\\n    bottomCall() {\\n        return this.callStack[0];\\n    },\\n\\n    pushChildCall(childCall) {\\n        const topCall = this.topCall();\\n\\n        if (topCall.calls === undefined) {\\n            topCall.calls = [];\\n        }\\n\\n        topCall.calls.push(childCall);\\n    },\\n\\n    pushGasToTopCall(log) {\\n        const topCall = this.topCall();\\n\\n        if (topCall.gasBigInt === undefined) {\\n            topCall.gasBigInt = log.getGas();\\n        }\\n        topCall.gasUsedBigInt = topCall.gasBigInt - log.getGas() - log.getCost();\\n    },\\n\\n    success(log, db) {\\n        const op = log.op.toString();\\n\\n        this.beforeOp(log, db);\\n\\n        switch (op) {\\n            case 'CREATE':\\n                this.createOp(log);\\n                break;\\n            case 'CREATE2':\\n                this.create2Op(log);\\n                break;\\n            case 'SELFDESTRUCT':\\n                this.selfDestructOp(log, db);\\n                break;\\n            case 'CALL':\\n            case 'CALLCODE':\\n            case 'DELEGATECALL':\\n            case 'STATICCALL':\\n                this.callOp(log, op);\\n                break;\\n            case 'REVERT':\\n                this.revertOp();\\n                break;\\n        }\\n    },\\n\\n    beforeOp(log, db) {\\n        /**\\n         * Depths\\n         * 0 - `ctx`.  Never shows up in `log.getDepth()`\\n         * 1 - first level of `log.getDepth()`\\n         *\\n         * callStack indexes\\n         *\\n         * 0 - pseudo-call stand-in for `ctx` in initializer (`callStack: [{}]`)\\n         * 1 - first callOp inside of `ctx`\\n         */\\n        const logDepth = log.getDepth();\\n        const callStackDepth = this.callStack.length;\\n\\n        if (logDepth < callStackDepth) {\\n            // Pop off the last call and get the execution results\\n            const call = this.callStack.pop();\\n\\n            const ret = log.stack.peek(0);\\n\\n            if (!ret.equals(0)) {\\n                if (call.type === 'create' || call.type === 'create2') {\\n                    call.createdContractAddressHash = toHex(toAddress(ret.toString(16)));\\n                    call.createdContractCode = toHex(db.getCode(toAddress(ret.toString(16))));\\n                } else {\\n      " <> ...}, 5000]}}
vbaranov commented 3 years ago

Probably you might want to try to decrease the batch size for the internal transactions fetcher to return it to an operational state with IPC transport https://github.com/poanetwork/blockscout/blob/master/apps/indexer/lib/indexer/fetcher/internal_transaction.ex#L23?