Coverage Summary for Class: TraceTransformer (co.rsk.rpc.modules.trace)
Class |
Class, %
|
Method, %
|
Line, %
|
TraceTransformer |
0%
(0/1)
|
0%
(0/7)
|
0%
(0/83)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package co.rsk.rpc.modules.trace;
21
22 import co.rsk.core.RskAddress;
23 import org.bouncycastle.util.encoders.Hex;
24 import org.ethereum.db.TransactionInfo;
25 import org.ethereum.rpc.TypeConverter;
26 import org.ethereum.vm.DataWord;
27 import org.ethereum.vm.program.ProgramResult;
28 import org.ethereum.vm.program.invoke.InvokeData;
29 import org.ethereum.vm.trace.SummarizedProgramTrace;
30
31 import java.math.BigInteger;
32 import java.util.ArrayList;
33 import java.util.List;
34
35 public class TraceTransformer {
36 private TraceTransformer() {
37
38 }
39
40 public static List<TransactionTrace> toTraces(SummarizedProgramTrace trace, TransactionInfo txInfo, long blockNumber) {
41 List<TransactionTrace> traces = new ArrayList<>();
42
43 addTrace(traces, trace, txInfo, blockNumber, new TraceAddress());
44
45 return traces;
46 }
47
48 private static void addTrace(List<TransactionTrace> traces, SummarizedProgramTrace trace, TransactionInfo txInfo, long blockNumber, TraceAddress traceAddress) {
49 boolean isContractCreation = txInfo.getReceipt().getTransaction().isContractCreation();
50 CallType callType = isContractCreation ? CallType.NONE : CallType.CALL;
51 byte[] creationInput = isContractCreation ? txInfo.getReceipt().getTransaction().getData() : null;
52
53 ProgramResult programResult = ProgramResult.empty();
54 programResult.spendGas(new BigInteger(1, txInfo.getReceipt().getGasUsed()).longValue());
55
56 if (trace.getReverted()) {
57 programResult.setRevert();
58 }
59
60 CreationData creationData = null;
61 TraceType traceType = TraceType.CALL;
62
63 if (isContractCreation) {
64 String outputText = trace.getResult();
65 byte[] createdCode = outputText == null ? new byte[0] : Hex.decode(outputText);
66 RskAddress createdAddress = txInfo.getReceipt().getTransaction().getContractAddress();
67 creationData = new CreationData(creationInput, createdCode, createdAddress);
68 traceType = TraceType.CREATE;
69 }
70
71 int nsubtraces = trace.getSubtraces().size();
72
73 traces.add(toTrace(traceType, trace.getInvokeData(), programResult, txInfo, blockNumber, traceAddress, callType, creationData, null, trace.getError(), nsubtraces, null));
74
75 for (int k = 0; k < nsubtraces; k++) {
76 addTrace(traces, trace.getSubtraces().get(k), txInfo, blockNumber, new TraceAddress(traceAddress, k));
77 }
78 }
79
80 private static void addTrace(List<TransactionTrace> traces, ProgramSubtrace subtrace, TransactionInfo txInfo, long blockNumber, TraceAddress traceAddress) {
81 traces.add(toTrace(subtrace.getTraceType(), subtrace.getInvokeData(), subtrace.getProgramResult(), txInfo, blockNumber, traceAddress, subtrace.getCallType(), subtrace.getCreationData(), subtrace.getCreationMethod(), null, subtrace.getSubtraces().size(), subtrace.getCodeAddress()));
82
83 int nsubtraces = subtrace.getSubtraces().size();
84
85 for (int k = 0; k < nsubtraces; k++) {
86 addTrace(traces, subtrace.getSubtraces().get(k), txInfo, blockNumber, new TraceAddress(traceAddress, k));
87 }
88 }
89
90 public static TransactionTrace toTrace(TraceType traceType, InvokeData invoke, ProgramResult programResult, TransactionInfo txInfo, long blockNumber, TraceAddress traceAddress, CallType callType, CreationData creationData, String creationMethod, String err, int nsubtraces, DataWord codeAddress) {
91 TraceAction action = toAction(traceType, invoke, callType, creationData == null ? null : creationData.getCreationInput(), creationMethod, codeAddress);
92 TraceResult result = null;
93 String error = null;
94
95 if (traceType != TraceType.SUICIDE) {
96 result = toResult(programResult, creationData == null ? null : creationData.getCreatedCode(), creationData == null ? null : creationData.getCreatedAddress());
97
98 error = err != null && err.isEmpty() ? null : err;
99
100 if (programResult.getException() != null) {
101 error = programResult.getException().toString();
102 }
103 else if (programResult.isRevert()) {
104 error = "Reverted";
105 }
106
107 if (error != null) {
108 result = null;
109 }
110 }
111
112 String blockHash = TypeConverter.toUnformattedJsonHex(txInfo.getBlockHash());
113 String transactionHash = txInfo.getReceipt().getTransaction().getHash().toJsonString();
114 int transactionPosition = txInfo.getIndex();
115 String type = traceType.name().toLowerCase();
116
117 return new TransactionTrace(
118 action,
119 blockHash,
120 blockNumber,
121 transactionHash,
122 transactionPosition,
123 type,
124 nsubtraces,
125 traceAddress,
126 result,
127 error
128 );
129 }
130
131 public static TraceResult toResult(ProgramResult programResult, byte[] createdCode, RskAddress createdAddress) {
132 String gasUsed = TypeConverter.toQuantityJsonHex(programResult.getGasUsed());
133 String output = null;
134 String address = null;
135 String code = null;
136
137 if (createdCode != null && createdAddress != null) {
138 code = TypeConverter.toUnformattedJsonHex(createdCode);
139 address = createdAddress.toJsonString();
140 }
141 else {
142 output = TypeConverter.toUnformattedJsonHex(programResult.getHReturn());
143 }
144
145 return new TraceResult(gasUsed, output, code, address);
146 }
147
148 public static TraceAction toAction(TraceType traceType, InvokeData invoke, CallType callType, byte[] creationInput, String creationMethod, DataWord codeAddress) {
149 String from;
150
151 if (callType == CallType.DELEGATECALL) {
152 from = new RskAddress(invoke.getOwnerAddress().getLast20Bytes()).toJsonString();
153 }
154 else {
155 from = new RskAddress(invoke.getCallerAddress().getLast20Bytes()).toJsonString();
156 }
157
158 String to = null;
159 String gas = null;
160
161 DataWord callValue = invoke.getCallValue();
162
163 String input = null;
164 String value = null;
165 String address = null;
166 String refundAddress = null;
167 String balance = null;
168 String init = null;
169
170 if (traceType == TraceType.CREATE) {
171 init = TypeConverter.toUnformattedJsonHex(creationInput);
172 value = TypeConverter.toQuantityJsonHex(callValue.getData());
173 gas = TypeConverter.toQuantityJsonHex(invoke.getGas());
174 }
175
176 if (traceType == TraceType.CALL) {
177 input = TypeConverter.toUnformattedJsonHex(invoke.getDataCopy(DataWord.ZERO, invoke.getDataSize()));;
178 value = TypeConverter.toQuantityJsonHex(callValue.getData());
179
180 if (callType == CallType.DELEGATECALL) {
181 // The code address should not be null in a DELEGATECALL case
182 // but handling the case here
183 if (codeAddress != null) {
184 to = new RskAddress(codeAddress.getLast20Bytes()).toJsonString();
185 }
186 }
187 else {
188 to = new RskAddress(invoke.getOwnerAddress().getLast20Bytes()).toJsonString();
189 }
190
191 gas = TypeConverter.toQuantityJsonHex(invoke.getGas());
192 }
193
194 if (traceType == TraceType.SUICIDE) {
195 address = from;
196 from = null;
197 balance = TypeConverter.toQuantityJsonHex(callValue.getData());
198 refundAddress = new RskAddress(invoke.getOwnerAddress().getLast20Bytes()).toJsonString();
199 }
200
201 return new TraceAction(
202 callType,
203 from,
204 to,
205 gas,
206 input,
207 init,
208 creationMethod,
209 value,
210 address,
211 refundAddress,
212 balance
213 );
214 }
215 }