package tracers import ( "bytes" "encoding/json" "errors" "math/big" "strconv" "strings" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" "github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/internal/utils" ) var TestJsonsMock = []byte(`{"14054302":[{"blockNumber":14054302,"blockHash":"0x04d7a0d62d3211151db0dadcaebcb1686c4a3df0e551a00c023c651546293975","transactionHash":"0xce49e42e0fbd37a0cfd08c2da3f1acc371ddbc02c428afa123a43663e57953d7","transactionPosition":0,"subtraces":0,"traceAddress":[0],"type":"suicide","action":{"refundAddress":"0x12e49d93588e0056bd25530c3b1e8aac68f4b70a","balance":"0x0","address":"0x7006c42d6fa41844baa53b0388f9542e634cf55a"},"result":null}],"14833359":[{"blockNumber":14833359,"blockHash":"0x6d6660f3d042a145c7f95c408f28cbf036a18eaf603161c2c00ca3f6041d8b52","transactionHash":"0x9fd0daef346c72d51f7482ddc9a466caf52fa6a116ed13ee0c003e57e632b7c0","transactionPosition":0,"subtraces":0,"traceAddress":[],"type":"create","action":{"from":"0x8520021f89450394244cd4abda4cfe2f1b0ef61c","gas":"0x1017d","init":"0x608060405234801561001057600080fd5b50610149806100206000396000f3fe6080604052600436106100295760003560e01c80630c2ad69c1461002e57806315d55b281461007a575b600080fd5b6100646004803603604081101561004457600080fd5b810190808035906020019092919080359060200190929190505050610091565b6040518082815260200191505060405180910390f35b34801561008657600080fd5b5061008f6100a5565b005b600081838161009c57fe5b04905092915050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f68656c6c6f00000000000000000000000000000000000000000000000000000081525060200191505060405180910390fdfea26469706673582212202f9958b958267c4ed653e54dc0161cfb9b772209cbe086f4a9ac3d967f22f09564736f6c634300060c0033","value":"0x0"},"result":{"address":"0xf29fcf3a375ce5dd1c58f0e8a584ab5d782cc12b","code":"0x6080604052600436106100295760003560e01c80630c2ad69c1461002e57806315d55b281461007a575b600080fd5b6100646004803603604081101561004457600080fd5b810190808035906020019092919080359060200190929190505050610091565b6040518082815260200191505060405180910390f35b34801561008657600080fd5b5061008f6100a5565b005b600081838161009c57fe5b04905092915050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f68656c6c6f00000000000000000000000000000000000000000000000000000081525060200191505060405180910390fdfea26469706673582212202f9958b958267c4ed653e54dc0161cfb9b772209cbe086f4a9ac3d967f22f09564736f6c634300060c0033","gasUsed":"0x1017d"}},{"blockNumber":14833359,"blockHash":"0x6d6660f3d042a145c7f95c408f28cbf036a18eaf603161c2c00ca3f6041d8b52","transactionHash":"0xc3b81fa2f6786ffd11a588b9d951a39adb46b6e29abad819b0cb09ee32ea7072","transactionPosition":1,"subtraces":2,"traceAddress":[],"type":"call","action":{"callType":"call","value":"0x0","to":"0x4596817192fbbf0142c576ed3e7cfc0e8f40bbbe","gas":"0x2b71c","from":"0x87946ddc76a4c0a75c8ca1f63dffd0612ae6458c","input":"0x1801fbe5aebcf6e3d785238603dd88bb43cbdfcfeb51c95b570113ee65d2f9271d3b59510000000dcdf493a5e1610e23c037bc4c4e04ab9a6d8fe9d0d462ecd8d45643ac"},"result":{"output":"0x0000000000000000000000000000000000000000000000000000000000000001","gasUsed":"0x13c58"}}]}`) 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"` } // this function only used by test func initFromJson(ts *TraceBlockStorage, 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) } } func TestStorage(t *testing.T) { testJsons := make(map[string]json.RawMessage) var sizeDB []byte var memDB [][2][]byte //TestJsonsMock, _ := os.ReadFile("/tmp/out.json") json.Unmarshal(TestJsonsMock, &testJsons) for _, testJson := range testJsons { block := &TraceBlockStorage{ addressIndex: make(map[common.Address]int), dataIndex: make(map[common.Hash]int), } initFromJson(block, testJson) jsonRaw, err := block.ToJson() if err != nil { t.Error(err) } if !bytes.Equal(jsonRaw, testJson) { t.Fatal("restroe failed!") } block.ToDB(func(key, data []byte) { for _, kv := range memDB { if bytes.Equal(kv[0], key) { return } } sizeDB = append(sizeDB, key...) newKey := sizeDB[len(sizeDB)-len(key):] sizeDB = append(sizeDB, data...) newData := sizeDB[len(sizeDB)-len(data):] memDB = append(memDB, [2][]byte{newKey, newData}) }) newBlock := &TraceBlockStorage{ Hash: block.Hash, addressIndex: make(map[common.Address]int), dataIndex: make(map[common.Hash]int), } newBlock.FromDB(func(key []byte) ([]byte, error) { for _, kv := range memDB { k, v := kv[0], kv[1] if bytes.Equal(k, key) { return v, nil } } t.Fatalf("key not exist: %x", key) return nil, nil }) jsonRaw, err = newBlock.ToJson() if err != nil { t.Error(err) } if !bytes.Equal(jsonRaw, testJson) { t.Fatal("restroe failed!") } //os.WriteFile("/tmp/trace.raw", sizeDB, os.ModePerm) } } var TestActions = []string{ `{"callType":"call","value":"0x9f2d9ea5d38f03446","to":"0xf012702a5f0e54015362cbca26a26fc90aa832a3","gas":"0x177d97","from":"0x5a42560d64136caef1a92d4c0829c21385f9f182","input":"0xfb3bdb41000000000000000000000000000000000000000000000001a8a909dfcef4000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000005a42560d64136caef1a92d4c0829c21385f9f1820000000000000000000000000000000000000000000000000000000061cecc5c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cf664087a5bb0237a0bad6742852ec6c8d69a27a000000000000000000000000a4e24f10712cec820dd04e35d52f8087a0699d19"}`, `{"callType":"staticcall","to":"0x3fe7910191e07503a94237303c8c0497549b0b20","gas":"0x17120d","from":"0xf012702a5f0e54015362cbca26a26fc90aa832a3","input":"0x0902f1ac"}`, `{"callType":"delegatecall","to":"0x4a5dca217eea6ec5f3a2f81859f0698aaf7668b9","gas":"0x215170","from":"0x5f753dcdf9b1ad9aabc1346614d1f4746fd6ce5c","input":"0x6352211e000000000000000000000000000000000000000000000000000000000000bd5e"}`, `{"from":"0x84eaa517a51445e35a368885da3d30cd9ab2ead4","gas":"0x659087","init":"0x608060405234801561001057600080fd5b5060405161033a38038061033a8339818101604052602081101561003357600080fd5b810190808051906020019092919050505080600081905550506102df8061005b6000396000f3fe6080604052600436106100295760003560e01c80638100626b1461002e5780639eb7a67f1461007c575b600080fd5b61007a6004803603604081101561004457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506100d7565b005b34801561008857600080fd5b506100d56004803603604081101561009f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061020a565b005b60008114156100e557610206565b8173ffffffffffffffffffffffffffffffffffffffff16638100626b6002348161010b57fe5b0430600185036040518463ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506000604051808303818588803b15801561016357600080fd5b505af1158015610177573d6000803e3d6000fd5b50505050508173ffffffffffffffffffffffffffffffffffffffff16639eb7a67f30836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b1580156101ed57600080fd5b505af1158015610201573d6000803e3d6000fd5b505050505b5050565b6001811115610218576102a5565b8173ffffffffffffffffffffffffffffffffffffffff16639eb7a67f83600184016040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b15801561028c57600080fd5b505af11580156102a0573d6000803e3d6000fd5b505050505b505056fea2646970667358221220eada8b07d177afc51b40b49ee90fe29274e3a002f2138a9023a11463dd82d4f164736f6c634300060c003300000000000000000000000000000000000000000000000000000000000001c8","value":"0x0"}`, `{"refundAddress":"0xf012702a5f0e54015362cbca26a26fc90aa832a3","balance":"0x9f2d9ea5d38f03446","address":"0xf012702a5f0e54015362cbca26a26fc90aa832a3"}`, } func unmarshalAction(jsonstr string) (*action, error) { actionInterface := make(map[string]string) if err := json.Unmarshal([]byte(jsonstr), &actionInterface); err != nil { return nil, err } var ac action if callType, exist := actionInterface["callType"]; exist { ac.from = common.HexToAddress(actionInterface["from"]) ac.to = common.HexToAddress(actionInterface["to"]) ac.gas, _ = strconv.ParseUint(actionInterface["gas"], 0, 64) ac.input = utils.FromHex(actionInterface["input"]) ac.value = big.NewInt(0) ac.value.UnmarshalText([]byte(actionInterface["value"])) switch strings.ToUpper(callType) { case "CALL": ac.op = vm.CALL case "CALLCODE": ac.op = vm.CALLCODE case "DELEGATECALL": ac.op = vm.DELEGATECALL case "STATICCALL": ac.op = vm.STATICCALL } return &ac, nil } if initCode, exist := actionInterface["init"]; exist { ac.op = vm.CREATE ac.from = common.HexToAddress(actionInterface["from"]) ac.value = big.NewInt(0) ac.value.UnmarshalText([]byte(actionInterface["value"])) ac.gas, _ = strconv.ParseUint(actionInterface["gas"], 0, 64) ac.input = utils.FromHex(initCode) return &ac, nil } if refundAddress, exist := actionInterface["refundAddress"]; exist { ac.op = vm.SELFDESTRUCT ac.from = common.HexToAddress(actionInterface["address"]) ac.to = common.HexToAddress(refundAddress) ac.value = big.NewInt(0) ac.value.UnmarshalText([]byte(actionInterface["balance"])) return &ac, nil } return nil, errors.New("invalid action string") } func TestActionMarshal(t *testing.T) { for _, jsonStr := range TestActions { ac, err := unmarshalAction(jsonStr) if err != nil { t.Error(err) } _, acJsonStr, _ := ac.toJsonStr() if jsonStr != *acJsonStr { t.Errorf("expected %s got %s\n", jsonStr, *acJsonStr) } } }