aboutsummaryrefslogtreecommitdiffstats
path: root/eth/tracers/internal/tracers/4byte_tracer.js
blob: 2629aba3cf0d21efc79607edb0c747128bda1eaa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// 4byteTracer searches for 4byte-identifiers, and collects them for post-processing.
// It collects the methods identifiers along with the size of the supplied data, so
// a reversed signature can be matched against the size of the data.
//
// Example:
//   > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
//   {
//     0x27dc297e-128: 1,
//     0x38cc4831-0: 2,
//     0x524f3889-96: 1,
//     0xadf59f99-288: 1,
//     0xc281d19e-0: 1
//   }
{
    // ids aggregates the 4byte ids found.
    ids : {},

    // callType returns 'false' for non-calls, or the peek-index for the first param
    // after 'value', i.e. meminstart.
    callType: function(opstr){
        switch(opstr){
        case "CALL": case "CALLCODE":
            // gas, addr, val, memin, meminsz, memout, memoutsz
            return 3; // stack ptr to memin

        case "DELEGATECALL": case "STATICCALL":
            // gas, addr, memin, meminsz, memout, memoutsz
            return 2; // stack ptr to memin
        }
        return false;
    },

    // store save the given indentifier and datasize.
    store: function(id, size){
        var key = "" + toHex(id) + "-" + size;
        this.ids[key] = this.ids[key] + 1 || 1;
    },

    // step is invoked for every opcode that the VM executes.
    step: function(log, db) {
        // Skip any opcodes that are not internal calls
        var ct = this.callType(log.op.toString());
        if (!ct) {
            return;
        }
        // Skip any pre-compile invocations, those are just fancy opcodes
        if (isPrecompiled(toAddress(log.stack.peek(1)))) {
            return;
        }
        // Gather internal call details
        var inSz = log.stack.peek(ct + 1).valueOf();
        if (inSz >= 4) {
            var inOff = log.stack.peek(ct).valueOf();
            this.store(log.memory.slice(inOff, inOff + 4), inSz-4);
        }
    },

    // fault is invoked when the actual execution of an opcode fails.
    fault: function(log, db) { },

    // result is invoked when all the opcodes have been iterated over and returns
    // the final result of the tracing.
    result: function(ctx) {
        // Save the outer calldata also
        if (ctx.input.length >= 4) {
            this.store(slice(ctx.input, 0, 4), ctx.input.length-4)
        }
        return this.ids;
    },
}