Coverage Summary for Class: DetailedProgramTrace (org.ethereum.vm.trace)
Class |
Class, %
|
Method, %
|
Line, %
|
DetailedProgramTrace |
0%
(0/1)
|
0%
(0/31)
|
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 org.ethereum.vm.trace;
21
22 import co.rsk.config.VmConfig;
23 import co.rsk.core.RskAddress;
24 import co.rsk.core.bc.AccountInformationProvider;
25 import co.rsk.rpc.modules.trace.ProgramSubtrace;
26 import com.fasterxml.jackson.annotation.JsonIgnore;
27 import org.ethereum.util.ByteUtil;
28 import org.ethereum.vm.DataWord;
29 import org.ethereum.vm.OpCode;
30 import org.ethereum.vm.program.Memory;
31 import org.ethereum.vm.program.Stack;
32 import org.ethereum.vm.program.Storage;
33 import org.ethereum.vm.program.invoke.ProgramInvoke;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import java.util.*;
38
39 import static java.lang.String.format;
40 import static org.ethereum.util.ByteUtil.toHexStringOrEmpty;
41 import static org.ethereum.vm.trace.Serializers.serializeFieldsOnly;
42
43 public class DetailedProgramTrace implements ProgramTrace {
44
45 private static final Logger logger = LoggerFactory.getLogger("vm");
46
47 private String contractAddress;
48 private Map<String, String> initStorage = new HashMap<>();
49
50 private List<Op> structLogs = new LinkedList<>();
51 private String result;
52 private String error;
53 private boolean reverted;
54
55 private int storageSize;
56
57 private Map<String, String> currentStorage = new HashMap<>();
58
59 @JsonIgnore
60 private boolean fullStorage;
61
62 @JsonIgnore
63 private DataWord storageKey;
64
65 @JsonIgnore
66 private final ProgramInvoke programInvoke;
67
68 @JsonIgnore
69 private final List<ProgramSubtrace> subtraces = new ArrayList<>();
70
71 public DetailedProgramTrace(VmConfig config, ProgramInvoke programInvoke) {
72 this.programInvoke = programInvoke;
73
74 if (config.vmTrace() && programInvoke != null) {
75 contractAddress = ByteUtil.toHexString(programInvoke.getOwnerAddress().getLast20Bytes());
76
77 AccountInformationProvider informationProvider = getInformationProvider(programInvoke);
78 RskAddress ownerAddress = new RskAddress(programInvoke.getOwnerAddress());
79 if (!informationProvider.isContract(ownerAddress)) {
80 storageSize = 0;
81 fullStorage = true;
82 } else {
83 storageSize = informationProvider.getStorageKeysCount(ownerAddress);
84 if (storageSize <= config.vmTraceInitStorageLimit()) {
85 fullStorage = true;
86
87 String address = toHexStringOrEmpty(programInvoke.getOwnerAddress().getLast20Bytes());
88 Iterator<DataWord> keysIterator = informationProvider.getStorageKeys(ownerAddress);
89 while (keysIterator.hasNext()) {
90 // TODO: solve NULL key/value storage problem
91 DataWord key = keysIterator.next();
92 byte[] value = informationProvider.getStorageBytes(ownerAddress, key);
93 if (key == null || value == null) {
94 logger.info("Null storage key/value: address[{}]", address);
95 continue;
96 }
97
98 initStorage.put(key.toString(), DataWord.valueOf(value).toString());
99 }
100
101 if (!initStorage.isEmpty()) {
102 logger.info("{} entries loaded to transaction's initStorage", initStorage.size());
103 }
104 }
105 }
106
107 saveCurrentStorage(initStorage);
108 }
109 }
110
111 @Override
112 public ProgramInvoke getProgramInvoke() {
113 return this.programInvoke;
114 }
115
116 private void saveCurrentStorage(Map<String, String> storage) {
117 this.currentStorage = new HashMap<>(storage);
118 }
119
120 public boolean isEmpty() {
121 return contractAddress == null;
122 }
123
124 private static AccountInformationProvider getInformationProvider(ProgramInvoke programInvoke) {
125 return programInvoke.getRepository();
126 }
127
128 public List<Op> getStructLogs() {
129 return structLogs == null ? null : Collections.unmodifiableList(structLogs);
130 }
131
132 public void setStructLogs(List<Op> structLogs) {
133 this.structLogs = structLogs == null ? null : Collections.unmodifiableList(structLogs);
134 }
135
136 public String getResult() {
137 return result;
138 }
139
140 public void setResult(String result) {
141 this.result = result;
142 }
143
144 public String getError() {
145 return error;
146 }
147
148 public void setError(String error) {
149 this.error = error;
150 }
151
152 public boolean getReverted() { return reverted; }
153
154 public boolean isFullStorage() {
155 return fullStorage;
156 }
157
158 public void setFullStorage(boolean fullStorage) {
159 this.fullStorage = fullStorage;
160 }
161
162 public int getStorageSize() {
163 return storageSize;
164 }
165
166 public void setStorageSize(int storageSize) {
167 this.storageSize = storageSize;
168 }
169
170 public Map<String, String> getInitStorage() {
171 return initStorage;
172 }
173
174 public void setInitStorage(Map<String, String> initStorage) {
175 this.initStorage = initStorage;
176 }
177
178 public String getContractAddress() {
179 return contractAddress;
180 }
181
182 public void setContractAddress(String contractAddress) {
183 this.contractAddress = contractAddress;
184 }
185
186 public ProgramTrace result(byte[] result) {
187 setResult(toHexStringOrEmpty(result));
188 return this;
189 }
190
191 public ProgramTrace error(Exception error) {
192 setError(error == null ? "" : format("%s: %s", error.getClass(), error.getMessage()));
193 return this;
194 }
195
196 @Override
197 public ProgramTrace revert(boolean reverted) {
198 this.reverted = reverted;
199 return this;
200 }
201
202 @Override
203 public void saveGasCost(long gasCost) {
204 structLogs.get(structLogs.size() - 1).setGasCost(gasCost);
205 }
206
207 @Override
208 public Op addOp(byte code, int pc, int deep, long gas, Memory memory, Stack stack, Storage storage) {
209 Op op = new Op();
210 OpCode opcode = OpCode.code(code);
211 op.setOp(opcode);
212 op.setDepth(deep);
213 op.setGas(gas);
214 op.setPc(pc);
215
216 op.setMemory(memory);
217 op.setStack(stack);
218
219 if (this.storageKey != null) {
220 RskAddress currentAddress = new RskAddress(this.contractAddress);
221 DataWord value = storage.getStorageValue(currentAddress, this.storageKey);
222
223 if (value != null) {
224 this.currentStorage = new HashMap<>(this.currentStorage);
225 this.currentStorage.put(this.storageKey.toString(), value.toString());
226 }
227 else {
228 this.currentStorage.remove(this.storageKey.toString());
229 }
230
231 this.storageSize = this.currentStorage.size();
232 this.storageKey = null;
233 }
234
235 if (opcode == OpCode.SSTORE || opcode == OpCode.SLOAD) {
236 this.storageKey = stack.peek();
237 }
238
239 op.setStorage(this.currentStorage);
240
241 structLogs.add(op);
242
243 return op;
244 }
245
246 @Override
247 public void addSubTrace(ProgramSubtrace programSubTrace) {
248 this.subtraces.add(programSubTrace);
249 }
250
251 @Override
252 public List<ProgramSubtrace> getSubtraces() {
253 return Collections.unmodifiableList(this.subtraces);
254 }
255
256 /**
257 * Used for merging sub calls execution.
258 */
259 @Override
260 public void merge(ProgramTrace programTrace) {
261 this.structLogs.addAll(((DetailedProgramTrace)programTrace).structLogs);
262 }
263
264 public String asJsonString(boolean formatted) {
265 return serializeFieldsOnly(this, formatted);
266 }
267
268 @Override
269 public String toString() {
270 return asJsonString(true);
271 }
272 }