|
|
@ -18,12 +18,16 @@ package tracers |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"encoding/json" |
|
|
|
|
|
|
|
"errors" |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
"math/big" |
|
|
|
"math/big" |
|
|
|
|
|
|
|
"strconv" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/rlp" |
|
|
|
"github.com/ethereum/go-ethereum/rlp" |
|
|
|
|
|
|
|
"github.com/harmony-one/harmony/core/vm" |
|
|
|
"github.com/harmony-one/harmony/crypto/hash" |
|
|
|
"github.com/harmony-one/harmony/crypto/hash" |
|
|
|
|
|
|
|
"github.com/harmony-one/harmony/internal/utils" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
type ActionStorage struct { |
|
|
|
type ActionStorage struct { |
|
|
@ -109,27 +113,46 @@ func (ts *TraceBlockStorage) indexAddress(address common.Address) int { |
|
|
|
return index |
|
|
|
return index |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (ts *TraceBlockStorage) toDB(write func([]byte, []byte)) { |
|
|
|
func (ts *TraceBlockStorage) KeyDB() []byte { |
|
|
|
|
|
|
|
return ts.Hash[:] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (ts *TraceBlockStorage) ToDB(write func([]byte, []byte)) { |
|
|
|
for index, key := range ts.DataKeyTable { |
|
|
|
for index, key := range ts.DataKeyTable { |
|
|
|
write(key[:], ts.dataValueTable[index]) |
|
|
|
write(key[:], ts.dataValueTable[index]) |
|
|
|
} |
|
|
|
} |
|
|
|
bytes, _ := rlp.EncodeToBytes(ts) |
|
|
|
bytes, _ := rlp.EncodeToBytes(ts) |
|
|
|
write(ts.Hash[:], bytes) |
|
|
|
write(ts.KeyDB(), bytes) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (ts *TraceBlockStorage) fromDB(read func([]byte) []byte, hash common.Hash) { |
|
|
|
func (ts *TraceBlockStorage) FromDB(read func([]byte) ([]byte, error)) error { |
|
|
|
bytes := read(hash[:]) |
|
|
|
bytes, err := read(ts.KeyDB()) |
|
|
|
rlp.DecodeBytes(bytes, ts) |
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = rlp.DecodeBytes(bytes, ts) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
for _, key := range ts.DataKeyTable { |
|
|
|
for _, key := range ts.DataKeyTable { |
|
|
|
ts.dataValueTable = append(ts.dataValueTable, read(key[:])) |
|
|
|
data, err := read(key[:]) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ts.dataValueTable = append(ts.dataValueTable, data) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (ts *TraceBlockStorage) txJson(index int) []json.RawMessage { |
|
|
|
func (ts *TraceBlockStorage) TxJson(index int) ([]json.RawMessage, error) { |
|
|
|
var results []json.RawMessage |
|
|
|
var results []json.RawMessage |
|
|
|
var txStorage TxStorage |
|
|
|
var txStorage TxStorage |
|
|
|
|
|
|
|
var err error |
|
|
|
b := ts.TraceStorages[index] |
|
|
|
b := ts.TraceStorages[index] |
|
|
|
rlp.DecodeBytes(b, &txStorage) |
|
|
|
err = rlp.DecodeBytes(b, &txStorage) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
headPiece := fmt.Sprintf( |
|
|
|
headPiece := fmt.Sprintf( |
|
|
|
`"blockNumber":%d,"blockHash":"%s","transactionHash":"%s","transactionPosition":%d`, |
|
|
|
`"blockNumber":%d,"blockHash":"%s","transactionHash":"%s","transactionPosition":%d`, |
|
|
@ -142,8 +165,8 @@ func (ts *TraceBlockStorage) txJson(index int) []json.RawMessage { |
|
|
|
|
|
|
|
|
|
|
|
typStr, acStr, outStr := ac.toJsonStr() |
|
|
|
typStr, acStr, outStr := ac.toJsonStr() |
|
|
|
if acStr == nil { |
|
|
|
if acStr == nil { |
|
|
|
//err = errors.New("tracer internal failure")
|
|
|
|
err = errors.New("tracer internal failure") |
|
|
|
return nil |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
traceStr, _ := json.Marshal(acStorage.TraceAddress) |
|
|
|
traceStr, _ := json.Marshal(acStorage.TraceAddress) |
|
|
|
bodyPiece := fmt.Sprintf( |
|
|
|
bodyPiece := fmt.Sprintf( |
|
|
@ -161,13 +184,167 @@ func (ts *TraceBlockStorage) txJson(index int) []json.RawMessage { |
|
|
|
jstr := "{" + headPiece + bodyPiece + resultPiece + "}" |
|
|
|
jstr := "{" + headPiece + bodyPiece + resultPiece + "}" |
|
|
|
results = append(results, json.RawMessage(jstr)) |
|
|
|
results = append(results, json.RawMessage(jstr)) |
|
|
|
} |
|
|
|
} |
|
|
|
return results |
|
|
|
return results, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (ts *TraceBlockStorage) toJson() []json.RawMessage { |
|
|
|
func (ts *TraceBlockStorage) ToJson() (json.RawMessage, error) { |
|
|
|
var results []json.RawMessage |
|
|
|
var results []json.RawMessage |
|
|
|
for i := range ts.TraceStorages { |
|
|
|
for i := range ts.TraceStorages { |
|
|
|
results = append(results, ts.txJson(i)...) |
|
|
|
tx, err := ts.TxJson(i) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
results = append(results, tx...) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return json.Marshal(results) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type JsonCallAction struct { |
|
|
|
|
|
|
|
CallType string `json:"callType"` |
|
|
|
|
|
|
|
Value string `json:"value"` |
|
|
|
|
|
|
|
From common.Address `json:"from"` |
|
|
|
|
|
|
|
To common.Address `json:"to"` |
|
|
|
|
|
|
|
Gas string `json:"gas"` |
|
|
|
|
|
|
|
Input string `json:"input"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type JsonCreateAction struct { |
|
|
|
|
|
|
|
From common.Address `json:"from"` |
|
|
|
|
|
|
|
Value string `json:"value"` |
|
|
|
|
|
|
|
Gas string `json:"gas"` |
|
|
|
|
|
|
|
Init string `json:"init"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type JsonSuicideAction struct { |
|
|
|
|
|
|
|
RefundAddress common.Address `json:"refundAddress"` |
|
|
|
|
|
|
|
Balance string `json:"balance"` |
|
|
|
|
|
|
|
Address common.Address `json:"address"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type JsonCallOutput struct { |
|
|
|
|
|
|
|
Output string `json:"output"` |
|
|
|
|
|
|
|
GasUsed string `json:"gasUsed"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type JsonCreateOutput struct { |
|
|
|
|
|
|
|
Address common.Address `json:"address"` |
|
|
|
|
|
|
|
Code string `json:"code"` |
|
|
|
|
|
|
|
GasUsed string `json:"gasUsed"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type JsonTrace struct { |
|
|
|
|
|
|
|
BlockNumber uint64 `json:"blockNumber"` |
|
|
|
|
|
|
|
BlockHash common.Hash `json:"blockHash"` |
|
|
|
|
|
|
|
TransactionHash common.Hash `json:"transactionHash"` |
|
|
|
|
|
|
|
TransactionPosition uint `json:"transactionPosition"` |
|
|
|
|
|
|
|
Subtraces uint `json:"subtraces"` |
|
|
|
|
|
|
|
TraceAddress []uint `json:"traceAddress"` |
|
|
|
|
|
|
|
Typ string `json:"type"` |
|
|
|
|
|
|
|
Action json.RawMessage `json:"action"` |
|
|
|
|
|
|
|
Result json.RawMessage `json:"result"` |
|
|
|
|
|
|
|
Err string `json:"error"` |
|
|
|
|
|
|
|
Revert string `json:"revert"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (ts *TraceBlockStorage) fromJson(bytes []byte) { |
|
|
|
|
|
|
|
var actionObjs []JsonTrace |
|
|
|
|
|
|
|
var txs []*TxStorage |
|
|
|
|
|
|
|
var tx *TxStorage |
|
|
|
|
|
|
|
json.Unmarshal(bytes, &actionObjs) |
|
|
|
|
|
|
|
for _, obj := range actionObjs { |
|
|
|
|
|
|
|
ac := action{} |
|
|
|
|
|
|
|
if len(obj.Err) > 0 { |
|
|
|
|
|
|
|
ac.err = errors.New(obj.Err) |
|
|
|
|
|
|
|
ac.revert = utils.FromHex(obj.Revert) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if obj.Typ == "call" { |
|
|
|
|
|
|
|
callAc := &JsonCallAction{} |
|
|
|
|
|
|
|
err := json.Unmarshal(obj.Action, callAc) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
panic(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
switch callAc.CallType { |
|
|
|
|
|
|
|
case "staticcall": |
|
|
|
|
|
|
|
ac.op = vm.STATICCALL |
|
|
|
|
|
|
|
case "call": |
|
|
|
|
|
|
|
ac.op = vm.CALL |
|
|
|
|
|
|
|
case "callcode": |
|
|
|
|
|
|
|
ac.op = vm.CALLCODE |
|
|
|
|
|
|
|
case "delegatecall": |
|
|
|
|
|
|
|
ac.op = vm.DELEGATECALL |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ac.from = callAc.From |
|
|
|
|
|
|
|
ac.to = callAc.To |
|
|
|
|
|
|
|
ac.value, _ = new(big.Int).SetString(callAc.Value[2:], 16) |
|
|
|
|
|
|
|
ac.gas, _ = strconv.ParseUint(callAc.Gas, 0, 64) |
|
|
|
|
|
|
|
ac.input = utils.FromHex(callAc.Input) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ac.err == nil { |
|
|
|
|
|
|
|
callOutput := &JsonCallOutput{} |
|
|
|
|
|
|
|
err = json.Unmarshal(obj.Result, callOutput) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
panic(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ac.output = utils.FromHex(callOutput.Output) |
|
|
|
|
|
|
|
ac.gasUsed, err = strconv.ParseUint(callOutput.GasUsed, 0, 64) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
panic(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if obj.Typ == "create" { |
|
|
|
|
|
|
|
ac.op = vm.CREATE |
|
|
|
|
|
|
|
callAc := &JsonCreateAction{} |
|
|
|
|
|
|
|
err := json.Unmarshal(obj.Action, callAc) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
panic(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
callOutput := &JsonCreateOutput{} |
|
|
|
|
|
|
|
err = json.Unmarshal(obj.Result, callOutput) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
panic(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ac.from = callAc.From |
|
|
|
|
|
|
|
ac.value, _ = new(big.Int).SetString(callAc.Value[2:], 16) |
|
|
|
|
|
|
|
ac.gas, _ = strconv.ParseUint(callAc.Gas, 0, 64) |
|
|
|
|
|
|
|
ac.input = utils.FromHex(callAc.Init) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ac.err == nil { |
|
|
|
|
|
|
|
ac.to = callOutput.Address |
|
|
|
|
|
|
|
ac.output = utils.FromHex(callOutput.Code) |
|
|
|
|
|
|
|
ac.gasUsed, _ = strconv.ParseUint(callOutput.GasUsed, 0, 64) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if obj.Typ == "suicide" { |
|
|
|
|
|
|
|
ac.op = vm.SELFDESTRUCT |
|
|
|
|
|
|
|
callAc := &JsonSuicideAction{} |
|
|
|
|
|
|
|
err := json.Unmarshal(obj.Action, callAc) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
panic(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ac.from = callAc.Address |
|
|
|
|
|
|
|
ac.to = callAc.RefundAddress |
|
|
|
|
|
|
|
ac.value, _ = new(big.Int).SetString(callAc.Balance[2:], 16) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ts.Hash = obj.BlockHash |
|
|
|
|
|
|
|
ts.Number = obj.BlockNumber |
|
|
|
|
|
|
|
if tx == nil || tx.Hash != obj.TransactionHash { |
|
|
|
|
|
|
|
tx = &TxStorage{ |
|
|
|
|
|
|
|
Hash: obj.TransactionHash, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
txs = append(txs, tx) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
acStorage := ac.toStorage(ts) |
|
|
|
|
|
|
|
acStorage.Subtraces = obj.Subtraces |
|
|
|
|
|
|
|
acStorage.TraceAddress = obj.TraceAddress |
|
|
|
|
|
|
|
tx.Storages = append(tx.Storages, acStorage) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for _, tx := range txs { |
|
|
|
|
|
|
|
b, err := rlp.EncodeToBytes(tx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
panic(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ts.TraceStorages = append(ts.TraceStorages, b) |
|
|
|
} |
|
|
|
} |
|
|
|
return results |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|