Coverage Summary for Class: Program (org.ethereum.vm.program)
Class |
Method, %
|
Line, %
|
Program |
32.8%
(38/116)
|
16.9%
(111/656)
|
Program$AddressCollisionException |
0%
(0/1)
|
0%
(0/2)
|
Program$BadJumpDestinationException |
0%
(0/1)
|
0%
(0/2)
|
Program$ExceptionHelper |
0%
(0/14)
|
0%
(0/15)
|
Program$IllegalOperationException |
0%
(0/1)
|
0%
(0/2)
|
Program$OutOfGasException |
0%
(0/1)
|
0%
(0/2)
|
Program$StackTooLargeException |
0%
(0/1)
|
0%
(0/3)
|
Program$StackTooSmallException |
0%
(0/1)
|
0%
(0/2)
|
Program$StaticCallModificationException |
0%
(0/1)
|
0%
(0/2)
|
Total |
27.7%
(38/137)
|
16.2%
(111/686)
|
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.program;
21
22 import co.rsk.config.VmConfig;
23 import co.rsk.core.Coin;
24 import co.rsk.core.RskAddress;
25 import co.rsk.crypto.Keccak256;
26 import co.rsk.pcc.NativeContract;
27 import co.rsk.peg.Bridge;
28 import co.rsk.remasc.RemascContract;
29 import co.rsk.rpc.modules.trace.CallType;
30 import co.rsk.rpc.modules.trace.CreationData;
31 import co.rsk.rpc.modules.trace.ProgramSubtrace;
32 import co.rsk.vm.BitSet;
33 import com.google.common.annotations.VisibleForTesting;
34 import org.ethereum.config.Constants;
35 import org.ethereum.config.blockchain.upgrades.ActivationConfig;
36 import org.ethereum.config.blockchain.upgrades.ConsensusRule;
37 import org.ethereum.core.Block;
38 import org.ethereum.core.BlockFactory;
39 import org.ethereum.core.Repository;
40 import org.ethereum.core.Transaction;
41 import org.ethereum.crypto.HashUtil;
42 import org.ethereum.util.ByteUtil;
43 import org.ethereum.util.FastByteComparisons;
44 import org.ethereum.vm.*;
45 import org.ethereum.vm.MessageCall.MsgType;
46 import org.ethereum.vm.PrecompiledContracts.PrecompiledContract;
47 import org.ethereum.vm.exception.VMException;
48 import org.ethereum.vm.program.invoke.*;
49 import org.ethereum.vm.program.listener.CompositeProgramListener;
50 import org.ethereum.vm.program.listener.ProgramListenerAware;
51 import org.ethereum.vm.trace.*;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 import javax.annotation.Nonnull;
56 import java.math.BigInteger;
57 import java.util.*;
58
59 import static co.rsk.util.ListArrayUtil.*;
60 import static java.lang.String.format;
61 import static org.ethereum.util.BIUtil.*;
62 import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
63
64 /**
65 * @author Roman Mandeleil
66 * @since 01.06.2014
67 */
68 public class Program {
69 // These logs should never be in Info mode in production
70 private static final Logger logger = LoggerFactory.getLogger("VM");
71 private static final Logger gasLogger = LoggerFactory.getLogger("gas");
72
73 public static final long MAX_MEMORY = (1<<30);
74
75 //Max size for stack checks
76 private static final int MAX_STACKSIZE = 1024;
77 private static final String CALL_PRECOMPILED_CAUSE = "call pre-compiled";
78
79 private final ActivationConfig.ForBlock activations;
80 private final Transaction transaction;
81
82 private final ProgramInvoke invoke;
83 private final ProgramInvokeFactory programInvokeFactory = new ProgramInvokeFactoryImpl();
84
85 private ProgramOutListener listener;
86 private final ProgramTraceListener traceListener;
87 private final CompositeProgramListener programListener = new CompositeProgramListener();
88
89 private final Stack stack;
90 private final Memory memory;
91 private final Storage storage;
92 private byte[] returnDataBuffer;
93
94 private final ProgramResult result = new ProgramResult();
95 private final ProgramTrace trace;
96
97 private final byte[] ops;
98 private int pc;
99 private byte lastOp;
100 private boolean stopped;
101 private byte exeVersion; // currently limited to 0..127
102 private byte scriptVersion; // currently limited to 0..127
103 private int startAddr;
104
105 private BitSet jumpdestSet;
106
107 private final VmConfig config;
108 private final PrecompiledContracts precompiledContracts;
109 private final BlockFactory blockFactory;
110
111 private boolean isLogEnabled;
112 private boolean isGasLogEnabled;
113
114 private RskAddress rskOwnerAddress;
115
116 private final Set<DataWord> deletedAccountsInBlock;
117
118 public Program(
119 VmConfig config,
120 PrecompiledContracts precompiledContracts,
121 BlockFactory blockFactory,
122 ActivationConfig.ForBlock activations,
123 byte[] ops,
124 ProgramInvoke programInvoke,
125 Transaction transaction,
126 Set<DataWord> deletedAccounts) {
127 this.config = config;
128 this.precompiledContracts = precompiledContracts;
129 this.blockFactory = blockFactory;
130 this.activations = activations;
131 this.transaction = transaction;
132 isLogEnabled = logger.isInfoEnabled();
133 isGasLogEnabled = gasLogger.isInfoEnabled();
134
135 if (isLogEnabled ) {
136 logger.warn("WARNING! VM logging is enabled. This will make the VM 200 times slower. Do not use in production.");
137 }
138
139 if (isGasLogEnabled) {
140 gasLogger.warn("WARNING! Gas logging is enabled. This will the make VM 200 times slower. Do not use in production.");
141 }
142
143 this.invoke = programInvoke;
144
145 this.ops = nullToEmpty(ops);
146
147 this.trace = createProgramTrace(config, programInvoke);
148 this.memory = setupProgramListener(new Memory());
149 this.stack = setupProgramListener(new Stack());
150 this.stack.ensureCapacity(1024); // faster?
151 this.storage = setupProgramListener(new Storage(programInvoke));
152 this.deletedAccountsInBlock = new HashSet<>(deletedAccounts);
153
154 precompile();
155 traceListener = new ProgramTraceListener(config);
156 }
157
158 private static ProgramTrace createProgramTrace(VmConfig config, ProgramInvoke programInvoke) {
159 if (!config.vmTrace()) {
160 return new EmptyProgramTrace();
161 }
162
163 if ((config.vmTraceOptions() & VmConfig.LIGHT_TRACE) != 0) {
164 return new SummarizedProgramTrace(programInvoke);
165 }
166
167 return new DetailedProgramTrace(config, programInvoke);
168 }
169
170 /**
171 * Defines the depth of the call stack inside the EVM.
172 * Changed to a value more similar to Ethereum's with EIP150
173 * since RSKIP150.
174 */
175 public int getMaxDepth() {
176 if (activations.isActive(ConsensusRule.RSKIP150)) {
177 return 400;
178 }
179 return 1024;
180 }
181
182 public int getCallDeep() {
183 return invoke.getCallDeep();
184 }
185
186 private InternalTransaction addInternalTx(byte[] nonce, DataWord gasLimit, RskAddress senderAddress, RskAddress receiveAddress,
187 Coin value, byte[] data, String note) {
188 if (transaction == null) {
189 return null;
190 }
191
192 byte[] senderNonce = isEmpty(nonce) ? getStorage().getNonce(senderAddress).toByteArray() : nonce;
193
194 return getResult().addInternalTransaction(
195 transaction,
196 getCallDeep(),
197 senderNonce,
198 getGasPrice(),
199 gasLimit,
200 senderAddress.getBytes(),
201 receiveAddress.getBytes(),
202 value.getBytes(),
203 data,
204 note);
205 }
206
207 private <T extends ProgramListenerAware> T setupProgramListener(T traceListenerAware) {
208 if (programListener.isEmpty()) {
209 programListener.addListener(traceListener);
210 }
211
212 traceListenerAware.setTraceListener(traceListener);
213 return traceListenerAware;
214 }
215
216 public byte getOp(int pc) {
217 return (getLength(ops) <= pc) ? 0 : ops[pc];
218 }
219
220 public byte getCurrentOp() {
221 return isEmpty(ops) ? 0 : ops[pc];
222 }
223
224 /**
225 * Last Op can only be set publicly (no getLastOp method), is used for logging.
226 */
227 public void setLastOp(byte op) {
228 this.lastOp = op;
229 }
230
231 private void stackPushZero() {
232 stackPush(DataWord.ZERO);
233 }
234
235 private void stackPushOne() {
236 stackPush(DataWord.ONE);
237 }
238
239 private void stackClear(){
240 stack.clear();
241 }
242
243 public void stackPush(DataWord stackWord) {
244 verifyStackOverflow(0, 1); //Sanity Check
245 stack.push(stackWord);
246 }
247
248 public Stack getStack() {
249 return this.stack;
250 }
251
252 public int getPC() {
253 return pc;
254 }
255
256 public void setPC(DataWord pc) {
257 this.setPC(pc.intValue());
258 }
259
260 public void setPC(int pc) {
261 this.pc = pc;
262
263 if (this.pc >= ops.length) {
264 stop();
265 }
266 }
267
268 public boolean isStopped() {
269 return stopped;
270 }
271
272 public void stop() {
273 stopped = true;
274 }
275
276 public void setHReturn(byte[] buff) {
277 getResult().setHReturn(buff);
278 }
279
280 public void step() {
281 setPC(pc + 1);
282 }
283
284
285 public DataWord sweepGetDataWord(int n) {
286 if (pc + n > ops.length) {
287 stop();
288 // In this case partial data is copied. At least Ethereumj does this
289 // Asummes LSBs are zero. assignDataRange undestands this semantics.
290 }
291
292 DataWord dw = DataWord.valueOf(ops, pc, n);
293 pc += n;
294 if (pc >= ops.length) {
295 stop();
296 }
297
298 return dw;
299 }
300
301 public DataWord stackPop() {
302 return stack.pop();
303 }
304
305 /**
306 * Verifies that the stack is at least <code>stackSize</code>
307 *
308 * @param stackSize int
309 * @throws StackTooSmallException If the stack is
310 * smaller than <code>stackSize</code>
311 */
312 public void verifyStackSize(int stackSize) {
313 if (stackSize < 0 || stack.size() < stackSize) {
314 throw ExceptionHelper.tooSmallStack(this, stackSize, stack.size());
315 }
316 }
317
318 public void verifyStackOverflow(int argsReqs, int returnReqs) {
319 if ((stack.size() - argsReqs + returnReqs) > MAX_STACKSIZE) {
320 throw new StackTooLargeException("Expected: overflow " + MAX_STACKSIZE + " elements stack limit");
321 }
322 }
323
324 public int getMemSize() {
325 return memory.size();
326 }
327
328 public void memorySave(DataWord addrB, DataWord value) {
329 //
330 memory.write(addrB.intValue(), value.getData(), value.getData().length, false);
331 }
332
333 private void memorySaveLimited(int addr, byte[] data, int dataSize) {
334 memory.write(addr, data, dataSize, true);
335 }
336
337 public void memorySave(int addr, byte[] value) {
338 memory.write(addr, value, value.length, false);
339 }
340
341 public void memoryExpand(DataWord outDataOffs, DataWord outDataSize) {
342 if (!outDataSize.isZero()) {
343 memory.extend(outDataOffs.intValue(), outDataSize.intValue());
344 }
345 }
346
347 /**
348 * Allocates a piece of memory and stores value at given offset address
349 *
350 * @param addr is the offset address
351 * @param allocSize size of memory needed to write
352 * @param value the data to write to memory
353 */
354 public void memorySave(int addr, int allocSize, byte[] value) {
355 memory.extendAndWrite(addr, allocSize, value);
356 }
357
358
359 public DataWord memoryLoad(DataWord addr) {
360 return memory.readWord(addr.intValue());
361 }
362
363 public byte[] memoryChunk(int offset, int size) {
364 return memory.read(offset, size);
365 }
366
367 /**
368 * Allocates extra memory in the program for
369 * a specified size, calculated from a given offset
370 *
371 * @param offset the memory address offset
372 * @param size the number of bytes to allocate
373 */
374 public void allocateMemory(int offset, int size) {
375 memory.extend(offset, size);
376 }
377
378 public void suicide(DataWord obtainerAddress) {
379
380 RskAddress owner = getOwnerRskAddress();
381 Coin balance = getStorage().getBalance(owner);
382 RskAddress obtainer = new RskAddress(obtainerAddress);
383
384 if (!balance.equals(Coin.ZERO)) {
385 logger.info("Transfer to: [{}] heritage: [{}]", obtainer, balance);
386
387 addInternalTx(null, null, owner, obtainer, balance, null, "suicide");
388
389 if (FastByteComparisons.compareTo(owner.getBytes(), 0, 20, obtainer.getBytes(), 0, 20) == 0) {
390 // if owner == obtainer just zeroing account according to Yellow Paper
391 getStorage().addBalance(owner, balance.negate());
392 } else {
393 getStorage().transfer(owner, obtainer, balance);
394 }
395 }
396 // In any case, remove the account
397 getResult().addDeleteAccount(this.getOwnerAddress());
398
399 SuicideInvoke invoke = new SuicideInvoke(DataWord.valueOf(owner.getBytes()), obtainerAddress, DataWord.valueOf(balance.getBytes()));
400 ProgramSubtrace subtrace = ProgramSubtrace.newSuicideSubtrace(invoke);
401
402 getTrace().addSubTrace(subtrace);
403 }
404
405 public void send(DataWord destAddress, Coin amount) {
406
407 RskAddress owner = getOwnerRskAddress();
408 RskAddress dest = new RskAddress(destAddress);
409 Coin balance = getStorage().getBalance(owner);
410
411 if (isNotCovers(balance, amount)) {
412 return; // does not do anything.
413 }
414
415 if (isLogEnabled) {
416 logger.info("Transfer to: [{}] amount: [{}]",
417 dest,
418 amount);
419 }
420
421 addInternalTx(null, null, owner, dest, amount, null, "send");
422
423 getStorage().transfer(owner, dest, amount);
424 }
425
426 public Repository getStorage() {
427 return this.storage;
428 }
429
430 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
431 public void createContract(DataWord value, DataWord memStart, DataWord memSize) {
432 RskAddress senderAddress = new RskAddress(getOwnerAddress());
433
434 byte[] nonce = getStorage().getNonce(senderAddress).toByteArray();
435 byte[] newAddressBytes = HashUtil.calcNewAddr(getOwnerAddress().getLast20Bytes(), nonce);
436 RskAddress newAddress = new RskAddress(newAddressBytes);
437
438 createContract(senderAddress, nonce, value, memStart, memSize, newAddress, false);
439 }
440
441 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
442 public void createContract2(DataWord value, DataWord memStart, DataWord memSize, DataWord salt) {
443 RskAddress senderAddress = new RskAddress(getOwnerAddress());
444 byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue());
445
446 byte[] newAddressBytes = HashUtil.calcSaltAddr(senderAddress, programCode, salt.getData());
447 byte[] nonce = getStorage().getNonce(senderAddress).toByteArray();
448 RskAddress newAddress = new RskAddress(newAddressBytes);
449
450 createContract(senderAddress, nonce, value, memStart, memSize, newAddress, true);
451 }
452
453 private void createContract( RskAddress senderAddress, byte[] nonce, DataWord value, DataWord memStart, DataWord memSize, RskAddress contractAddress, boolean isCreate2) {
454 if (getCallDeep() == getMaxDepth()) {
455 logger.debug("max depth reached creating a new contract inside contract run: [{}]", senderAddress);
456 stackPushZero();
457 return;
458 }
459
460 Coin endowment = new Coin(value.getData());
461 if (isNotCovers(getStorage().getBalance(senderAddress), endowment)) {
462 stackPushZero();
463 return;
464 }
465
466 // [1] FETCH THE CODE FROM THE MEMORY
467 byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue());
468
469 if (isLogEnabled) {
470 logger.info("creating a new contract inside contract run: [{}]", senderAddress);
471 }
472
473 // actual gas subtract
474 long gasLimit = getRemainingGas();
475 spendGas(gasLimit, "internal call");
476
477
478 if (byTestingSuite()) {
479 // This keeps track of the contracts created for a test
480 getResult().addCallCreate(programCode, EMPTY_BYTE_ARRAY,
481 gasLimit,
482 value.getNoLeadZeroesData());
483 }
484
485 // [3] UPDATE THE NONCE
486 // (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
487 if (!byTestingSuite()) {
488 getStorage().increaseNonce(senderAddress);
489 }
490 // Start tracking repository changes for the constructor of the contract
491 Repository track = getStorage().startTracking();
492
493 ProgramResult programResult = ProgramResult.empty();
494
495 if (getActivations().isActive(ConsensusRule.RSKIP125) &&
496 deletedAccountsInBlock.contains(DataWord.valueOf(contractAddress.getBytes()))) {
497 // Check if the address was previously deleted in the same block
498
499 programResult.setException(ExceptionHelper.addressCollisionException(this, contractAddress));
500 if (isLogEnabled) {
501 logger.debug("contract run halted by Exception: contract: [{}], exception: ",
502 contractAddress,
503 programResult.getException());
504 }
505
506 track.rollback();
507 stackPushZero();
508
509 return;
510 }
511
512 boolean existingAccount = track.isExist(contractAddress);
513
514 //In case of hashing collisions, check for any balance before createAccount()
515 if (existingAccount) {
516 // Hashing collisions in CREATE are rare, but in CREATE2 are possible
517 // We check for the nonce to non zero and that the code to be not empty
518 // if any of this conditions is true, the contract creation fails
519
520 byte[] code = track.getCode(contractAddress);
521 boolean hasNonEmptyCode = (code != null && code.length != 0);
522 boolean nonZeroNonce = track.getNonce(contractAddress).longValue() != 0;
523
524 if (getActivations().isActive(ConsensusRule.RSKIP125) && (hasNonEmptyCode || nonZeroNonce)) {
525 // Contract collision we fail with exactly the same behavior as would arise if
526 // the first byte in the init code were an invalid opcode
527 programResult.setException(ExceptionHelper.addressCollisionException(this, contractAddress));
528 if (isLogEnabled) {
529 logger.debug("contract run halted by Exception: contract: [{}], exception: ",
530 contractAddress,
531 programResult.getException());
532 }
533
534 // The programResult is empty and internalTx was not created so we skip this part
535 /*if (internalTx == null) {
536 throw new NullPointerException();
537 }
538
539 internalTx.reject();
540 programResult.rejectInternalTransactions();
541 programResult.rejectLogInfos();*/
542
543 track.rollback();
544 stackPushZero();
545
546 return;
547 }
548 }
549
550 track.createAccount(contractAddress, existingAccount);
551 track.setupContract(contractAddress);
552
553 if (getActivations().isActive(ConsensusRule.RSKIP125)) {
554 track.increaseNonce(contractAddress);
555 }
556
557 // [4] TRANSFER THE BALANCE
558 track.addBalance(senderAddress, endowment.negate());
559 Coin newBalance = Coin.ZERO;
560 if (!byTestingSuite()) {
561 newBalance = track.addBalance(contractAddress, endowment);
562 }
563
564
565 // [5] COOK THE INVOKE AND EXECUTE
566 programResult = getProgramResult(senderAddress, nonce, value, contractAddress, endowment, programCode, gasLimit, track, newBalance, programResult, isCreate2);
567 if (programResult == null) {
568 return;
569 }
570
571 // REFUND THE REMAIN GAS
572 refundRemainingGas(gasLimit, programResult);
573 }
574
575 private void refundRemainingGas(long gasLimit, ProgramResult programResult) {
576 if (programResult.getGasUsed() >= gasLimit) {
577 return;
578 }
579 long refundGas = GasCost.subtract(gasLimit, programResult.getGasUsed());
580 refundGas(refundGas, "remaining gas from the internal call");
581 if (isGasLogEnabled) {
582 gasLogger.info("The remaining gas is refunded, account: [{}], gas: [{}] ",
583 ByteUtil.toHexString(getOwnerAddress().getLast20Bytes()),
584 refundGas
585 );
586 }
587 }
588
589 private ProgramResult getProgramResult(RskAddress senderAddress, byte[] nonce, DataWord value,
590 RskAddress contractAddress, Coin endowment, byte[] programCode,
591 long gasLimit, Repository track, Coin newBalance, ProgramResult programResult, boolean isCreate2) {
592
593
594 InternalTransaction internalTx = addInternalTx(nonce, getGasLimit(), senderAddress, RskAddress.nullAddress(), endowment, programCode, "create");
595 ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
596 this, DataWord.valueOf(contractAddress.getBytes()), getOwnerAddress(), value, gasLimit,
597 newBalance, null, track, this.invoke.getBlockStore(), false, byTestingSuite());
598
599 returnDataBuffer = null; // reset return buffer right before the call
600
601 if (!isEmpty(programCode)) {
602 VM vm = new VM(config, precompiledContracts);
603 Program program = new Program(config, precompiledContracts, blockFactory, activations, programCode, programInvoke, internalTx, deletedAccountsInBlock);
604 vm.play(program);
605 programResult = program.getResult();
606
607 if (programResult.getException() == null && !programResult.isRevert()) {
608 getTrace().addSubTrace(ProgramSubtrace.newCreateSubtrace(new CreationData(programCode, programResult.getHReturn(), contractAddress), program.getProgramInvoke(), program.getResult(), program.getTrace().getSubtraces(), isCreate2));
609 }
610 }
611
612 if (programResult.getException() != null || programResult.isRevert()) {
613 if (isLogEnabled) {
614 logger.debug("contract run halted by Exception: contract: [{}], exception: ",
615 contractAddress,
616 programResult.getException());
617 }
618
619 if (internalTx == null) {
620 throw new NullPointerException();
621 }
622
623 internalTx.reject();
624 programResult.rejectInternalTransactions();
625 programResult.rejectLogInfos();
626
627 track.rollback();
628 stackPushZero();
629 if (programResult.getException() != null) {
630 return null;
631 } else {
632 returnDataBuffer = result.getHReturn();
633 }
634 } else {
635 // CREATE THE CONTRACT OUT OF RETURN
636 byte[] code = programResult.getHReturn();
637 int codeLength = getLength(code);
638
639 long storageCost = GasCost.multiply(GasCost.CREATE_DATA, codeLength);
640 long afterSpend = programInvoke.getGas() - storageCost - programResult.getGasUsed();
641
642 if (afterSpend < 0) {
643 programResult.setException(
644 ExceptionHelper.notEnoughSpendingGas(
645 this,
646 "No gas to return just created contract",
647 storageCost));
648 } else if (codeLength > Constants.getMaxContractSize()) {
649 programResult.setException(
650 ExceptionHelper.tooLargeContractSize(
651 this,
652 Constants.getMaxContractSize(),
653 codeLength));
654 } else {
655 programResult.spendGas(storageCost);
656 track.saveCode(contractAddress, code);
657 }
658
659 track.commit();
660
661 getResult().addDeleteAccounts(programResult.getDeleteAccounts());
662 getResult().addLogInfos(programResult.getLogInfoList());
663
664 // IN SUCCESS PUSH THE ADDRESS INTO THE STACK
665 stackPush(DataWord.valueOf(contractAddress.getBytes()));
666 }
667
668 return programResult;
669 }
670
671 public static long limitToMaxLong(DataWord gas) {
672 return gas.longValueSafe();
673
674 }
675
676 public static long limitToMaxLong(BigInteger gas) {
677 try {
678 long r = gas.longValueExact();
679 if (r<0) // check if this can happen
680 {
681 return Long.MAX_VALUE;
682 }
683 return r;
684 } catch (ArithmeticException e) {
685 return Long.MAX_VALUE;
686 }
687 }
688
689 public static long multiplyLimitToMaxLong(long a,long b) {
690 long d;
691
692 try {
693 d = Math.multiplyExact(a, b);
694 } catch (ArithmeticException e) {
695 d = Long.MAX_VALUE;
696 }
697 return d;
698 }
699
700 /**
701 * That method is for internal code invocations
702 * <p/>
703 * - Normal calls invoke a specified contract which updates itself
704 * - Stateless calls invoke code from another contract, within the context of the caller
705 *
706 * @param msg is the message call object
707 */
708 public void callToAddress(MessageCall msg) {
709
710 if (getCallDeep() == getMaxDepth()) {
711 stackPushZero();
712 refundGas(msg.getGas().longValue(), " call deep limit reach");
713 return;
714 }
715
716 byte[] data = memoryChunk(msg.getInDataOffs().intValue(), msg.getInDataSize().intValue());
717
718 // FETCH THE SAVED STORAGE
719 RskAddress codeAddress = new RskAddress(msg.getCodeAddress());
720 RskAddress senderAddress = getOwnerRskAddress();
721 RskAddress contextAddress = msg.getType().isStateless() ? senderAddress : codeAddress;
722
723 if (isLogEnabled) {
724 logger.info("{} for existing contract: address: [{}], outDataOffs: [{}], outDataSize: [{}] ", msg.getType().name(),
725 contextAddress, msg.getOutDataOffs().longValue(), msg.getOutDataSize().longValue());
726 }
727
728 Repository track = getStorage().startTracking();
729
730 // 2.1 PERFORM THE VALUE (endowment) PART
731 Coin endowment = new Coin(msg.getEndowment().getData());
732 Coin senderBalance = track.getBalance(senderAddress);
733 if (isNotCovers(senderBalance, endowment)) {
734 stackPushZero();
735 refundGas(msg.getGas().longValue(), "refund gas from message call");
736 this.cleanReturnDataBuffer();
737
738 return;
739 }
740
741 // FETCH THE CODE
742 byte[] programCode = getStorage().isExist(codeAddress) ? getStorage().getCode(codeAddress) : EMPTY_BYTE_ARRAY;
743 // programCode can be null
744
745 // Always first remove funds from sender
746 track.addBalance(senderAddress, endowment.negate());
747
748 Coin contextBalance;
749
750 if (byTestingSuite()) {
751 // This keeps track of the calls created for a test
752 getResult().addCallCreate(data, contextAddress.getBytes(),
753 msg.getGas().longValueSafe(),
754 msg.getEndowment().getNoLeadZeroesData());
755
756 return;
757 }
758
759 contextBalance = track.addBalance(contextAddress, endowment);
760
761 // CREATE CALL INTERNAL TRANSACTION
762 InternalTransaction internalTx = addInternalTx(null, getGasLimit(), senderAddress, contextAddress, endowment, programCode, "call");
763
764 boolean callResult;
765
766 if (!isEmpty(programCode)) {
767 callResult = executeCode(msg, contextAddress, contextBalance, internalTx, track, programCode, senderAddress, data);
768 } else {
769 track.commit();
770 callResult = true;
771 refundGas(GasCost.toGas(msg.getGas().longValue()), "remaining gas from the internal call");
772
773 DataWord callerAddress = DataWord.valueOf(senderAddress.getBytes());
774 DataWord ownerAddress = DataWord.valueOf(contextAddress.getBytes());
775 DataWord transferValue = DataWord.valueOf(endowment.getBytes());
776
777 TransferInvoke invoke = new TransferInvoke(callerAddress, ownerAddress, msg.getGas().longValue(), transferValue);
778 ProgramResult result = new ProgramResult();
779
780 ProgramSubtrace subtrace = ProgramSubtrace.newCallSubtrace(CallType.fromMsgType(msg.getType()), invoke, result, null, Collections.emptyList());
781
782 getTrace().addSubTrace(subtrace);
783
784 this.cleanReturnDataBuffer();
785 }
786
787 // 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
788 if (callResult) {
789 stackPushOne();
790 }
791 else {
792 stackPushZero();
793 }
794 }
795
796 private void cleanReturnDataBuffer() {
797 if (getActivations().isActive(ConsensusRule.RSKIP171)) {
798 // reset return data buffer when call did not create a new call frame
799 returnDataBuffer = null;
800 }
801 }
802
803 private ProgramInvoke getProgramInvoke() {
804 return this.invoke;
805 }
806
807 private boolean executeCode(
808 MessageCall msg,
809 RskAddress contextAddress,
810 Coin contextBalance,
811 InternalTransaction internalTx,
812 Repository track,
813 byte[] programCode,
814 RskAddress senderAddress,
815 byte[] data) {
816
817 returnDataBuffer = null; // reset return buffer right before the call
818 ProgramResult childResult;
819
820 ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
821 this, DataWord.valueOf(contextAddress.getBytes()),
822 msg.getType() == MsgType.DELEGATECALL ? getCallerAddress() : getOwnerAddress(),
823 msg.getType() == MsgType.DELEGATECALL ? getCallValue() : msg.getEndowment(),
824 limitToMaxLong(msg.getGas()), contextBalance, data, track, this.invoke.getBlockStore(),
825 msg.getType() == MsgType.STATICCALL || isStaticCall(), byTestingSuite());
826
827 VM vm = new VM(config, precompiledContracts);
828 Program program = new Program(config, precompiledContracts, blockFactory, activations, programCode, programInvoke, internalTx, deletedAccountsInBlock);
829
830 vm.play(program);
831 childResult = program.getResult();
832
833 getTrace().addSubTrace(ProgramSubtrace.newCallSubtrace(CallType.fromMsgType(msg.getType()), program.getProgramInvoke(), program.getResult(), msg.getCodeAddress(), program.getTrace().getSubtraces()));
834
835 getTrace().merge(program.getTrace());
836 getResult().merge(childResult);
837
838 boolean childCallSuccessful = true;
839
840 if (childResult.getException() != null || childResult.isRevert()) {
841 if (isGasLogEnabled) {
842 gasLogger.debug("contract run halted by Exception: contract: [{}], exception: ",
843 contextAddress,
844 childResult .getException());
845 }
846
847 internalTx.reject();
848 childResult.rejectInternalTransactions();
849 childResult.rejectLogInfos();
850
851 track.rollback();
852 // when there's an exception we skip applying results and refunding gas,
853 // and we only do that when the call is successful or there's a REVERT operation.
854 if (childResult.getException() != null) {
855 return false;
856 }
857
858 childCallSuccessful = false;
859 } else {
860 // 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
861 track.commit();
862 }
863
864
865
866 // 3. APPLY RESULTS: childResult.getHReturn() into out_memory allocated
867 byte[] buffer = childResult.getHReturn();
868 int offset = msg.getOutDataOffs().intValue();
869 int size = msg.getOutDataSize().intValue();
870
871 memorySaveLimited(offset, buffer, size);
872
873 returnDataBuffer = buffer;
874
875 // 5. REFUND THE REMAIN GAS
876 BigInteger refundGas = msg.getGas().value().subtract(toBI(childResult.getGasUsed()));
877 if (isPositive(refundGas)) {
878 // Since the original gas transferred was < Long.MAX_VALUE then the refund
879 // also fits in a long.
880 // SUICIDE refunds 24.000 and SSTORE clear refunds 15.000 gas.
881 // The accumulated refund can not exceed half the gas used
882 // for the current context (i.e. the initial call)
883 // Therefore, the regundGas also fits in a long.
884 refundGas(refundGas.longValue(), "remaining gas from the internal call");
885 if (isGasLogEnabled) {
886 gasLogger.info("The remaining gas refunded, account: [{}], gas: [{}] ", senderAddress, refundGas);
887 }
888 }
889 return childCallSuccessful;
890 }
891
892 public void spendGas(long gasValue, String cause) {
893 if (isGasLogEnabled) {
894 gasLogger.info("[{}] Spent for cause: [{}], gas: [{}]", invoke.hashCode(), cause, gasValue);
895 }
896
897 if (getRemainingGas() < gasValue) {
898 throw ExceptionHelper.notEnoughSpendingGas(this, cause, gasValue);
899 }
900
901 getResult().spendGas(gasValue);
902 }
903
904 public void restart() {
905 setPC(startAddr);
906 stackClear();
907 clearUsedGas();
908 stopped=false;
909 }
910
911 private void clearUsedGas() {
912 getResult().clearUsedGas();
913 }
914
915 public void spendAllGas() {
916 spendGas(getRemainingGas(), "Spending all remaining");
917 }
918
919 private void refundGas(long gasValue, String cause) {
920 if (isGasLogEnabled) {
921 gasLogger.info("[{}] Refund for cause: [{}], gas: [{}]", invoke.hashCode(), cause, gasValue);
922 }
923 getResult().refundGas(gasValue);
924 }
925
926 public void futureRefundGas(long gasValue) {
927 if (isLogEnabled) {
928 logger.info("Future refund added: [{}]", gasValue);
929 }
930 getResult().addFutureRefund(gasValue);
931 }
932
933 public void resetFutureRefund() {
934 getResult().resetFutureRefund();
935 }
936
937 public void storageSave(DataWord word1, DataWord word2) {
938 storageSave(word1.getData(), word2.getData());
939 }
940
941 private void storageSave(byte[] key, byte[] val) {
942 // DataWord constructor some times reference the passed byte[] instead
943 // of making a copy.
944 DataWord keyWord = DataWord.valueOf(key);
945 DataWord valWord = DataWord.valueOf(val);
946
947 getStorage().addStorageRow(getOwnerRskAddress(), keyWord, valWord);
948 }
949
950 private RskAddress getOwnerRskAddress() {
951 if (rskOwnerAddress == null) {
952 rskOwnerAddress = new RskAddress(getOwnerAddress());
953 }
954
955 return rskOwnerAddress;
956 }
957
958 public byte[] getCode() {
959 return Arrays.copyOf(ops, ops.length);
960 }
961
962 public Keccak256 getCodeHashAt(RskAddress addr, boolean standard) {
963 if(standard) {
964 return invoke.getRepository().getCodeHashStandard(addr);
965 }
966 else {
967 return invoke.getRepository().getCodeHashNonStandard(addr);
968 }
969 }
970
971 public Keccak256 getCodeHashAt(DataWord address, boolean standard) { return getCodeHashAt(new RskAddress(address), standard); }
972
973 public int getCodeLengthAt(RskAddress addr) {
974 return invoke.getRepository().getCodeLength(addr);
975 }
976
977 public int getCodeLengthAt(DataWord address) {
978 return getCodeLengthAt(new RskAddress(address));
979 }
980
981 public byte[] getCodeAt(DataWord address) {
982 return getCodeAt(new RskAddress(address));
983 }
984
985 private byte[] getCodeAt(RskAddress addr) {
986 byte[] code = invoke.getRepository().getCode(addr);
987 return nullToEmpty(code);
988 }
989
990 public DataWord getOwnerAddress() {
991 return invoke.getOwnerAddress();
992 }
993
994 public DataWord getBlockHash(DataWord dw) {
995 long blockIndex = dw.longValueSafe();
996 // always returns positive, returns Integer.MAX_VALUE on overflows
997 // block number would normally overflow int32 after ~1000 years (at 1 block every 10 seconds)
998 // So int32 arithmetic is ok, but we use int64 anyway.
999 return getBlockHash(blockIndex);
1000 }
1001
1002 public DataWord getBlockHash(long index) {
1003 long bn = this.getNumber().longValue();
1004 if ((index < bn) && (index >= Math.max(0, bn - 256))) {
1005 return DataWord.valueOf(this.invoke.getBlockStore().getBlockHashByNumber(index, getPrevHash().getData()));
1006 } else {
1007 return DataWord.ZERO;
1008 }
1009 }
1010
1011 public DataWord getBalance(DataWord address) {
1012 Coin balance = getStorage().getBalance(new RskAddress(address));
1013 return DataWord.valueOf(balance.getBytes());
1014 }
1015
1016 public DataWord getOriginAddress() {
1017 return invoke.getOriginAddress();
1018 }
1019
1020 public DataWord getCallerAddress() {
1021 return invoke.getCallerAddress();
1022 }
1023
1024 public DataWord getGasPrice() {
1025 return invoke.getMinGasPrice();
1026 }
1027
1028 public long getRemainingGas() {
1029 return invoke.getGas()- getResult().getGasUsed();
1030 }
1031
1032 public DataWord getCallValue() {
1033 return invoke.getCallValue();
1034 }
1035
1036 public DataWord getDataSize() {
1037 return invoke.getDataSize();
1038 }
1039
1040 public DataWord getDataValue(DataWord index) {
1041 return invoke.getDataValue(index);
1042 }
1043
1044 public byte[] getDataCopy(DataWord offset, DataWord length) {
1045 return invoke.getDataCopy(offset, length);
1046 }
1047
1048 public DataWord storageLoad(DataWord key) {
1049 return getStorage().getStorageValue(getOwnerRskAddress(), key);
1050 }
1051
1052 public DataWord getPrevHash() {
1053 return invoke.getPrevHash();
1054 }
1055
1056 public DataWord getCoinbase() {
1057 return invoke.getCoinbase();
1058 }
1059
1060 public DataWord getTimestamp() {
1061 return invoke.getTimestamp();
1062 }
1063
1064 public DataWord getNumber() {
1065 return invoke.getNumber();
1066 }
1067
1068 public DataWord getTransactionIndex() {
1069 return invoke.getTransactionIndex();
1070 }
1071
1072 public DataWord getDifficulty() {
1073 return invoke.getDifficulty();
1074 }
1075
1076 public DataWord getGasLimit() {
1077 return invoke.getGaslimit();
1078 }
1079
1080 public boolean isStaticCall() {
1081 return invoke.isStaticCall();
1082 }
1083
1084 public ProgramResult getResult() {
1085 return result;
1086 }
1087
1088 public void setRuntimeFailure(RuntimeException e) {
1089 getResult().setException(e);
1090 }
1091
1092 public String memoryToString() {
1093 if (memory.size()>100000) {
1094 return "<Memory too long to show>";
1095 } else {
1096 return memory.toString();
1097 }
1098 }
1099
1100 public void fullTrace() {
1101
1102 if (logger.isTraceEnabled() || listener != null) {
1103
1104 StringBuilder stackData = new StringBuilder();
1105 for (int i = 0; i < stack.size(); ++i) {
1106 stackData.append(" ").append(stack.get(i));
1107 if (i < stack.size() - 1) {
1108 stackData.append("\n");
1109 }
1110 }
1111
1112 if (stackData.length() > 0) {
1113 stackData.insert(0, "\n");
1114 }
1115
1116 RskAddress ownerAddress = new RskAddress(getOwnerAddress());
1117 StringBuilder storageData = new StringBuilder();
1118 if (getStorage().isContract(ownerAddress)) {
1119 Iterator<DataWord> it = getStorage().getStorageKeys(ownerAddress);
1120 while (it.hasNext()) {
1121 DataWord key = it.next();
1122 storageData.append(" ").append(key).append(" -> ").
1123 append(getStorage().getStorageValue(ownerAddress, key)).append('\n');
1124 }
1125 if (storageData.length() > 0) {
1126 storageData.insert(0, "\n");
1127 }
1128 }
1129
1130 StringBuilder memoryData = new StringBuilder();
1131 StringBuilder oneLine = new StringBuilder();
1132 if (memory.size() > 320) {
1133 memoryData.append("... Memory Folded.... ")
1134 .append("(")
1135 .append(memory.size())
1136 .append(") bytes");
1137 }
1138 else {
1139 for (int i = 0; i < memory.size(); ++i) {
1140
1141 byte value = memory.readByte(i);
1142 oneLine.append(ByteUtil.oneByteToHexString(value)).append(" ");
1143
1144 if ((i + 1) % 16 == 0) {
1145 String tmp = format("[%4s]-[%4s]", Integer.toString(i - 15, 16),
1146 Integer.toString(i, 16)).replace(" ", "0");
1147 memoryData.append("").append(tmp).append(" ");
1148 memoryData.append(oneLine);
1149 if (i < memory.size()) {
1150 memoryData.append("\n");
1151 }
1152 oneLine.setLength(0);
1153 }
1154 }
1155 }
1156 if (memoryData.length() > 0) {
1157 memoryData.insert(0, "\n");
1158 }
1159
1160 StringBuilder opsString = new StringBuilder();
1161 for (int i = 0; i < ops.length; ++i) {
1162
1163 String tmpString = Integer.toString(ops[i] & 0xFF, 16);
1164 tmpString = tmpString.length() == 1 ? "0" + tmpString : tmpString;
1165
1166 if (i != pc) {
1167 opsString.append(tmpString);
1168 } else {
1169 opsString.append(" >>").append(tmpString).append("");
1170 }
1171
1172 }
1173 if (pc >= ops.length) {
1174 opsString.append(" >>");
1175 }
1176 if (opsString.length() > 0) {
1177 opsString.insert(0, "\n ");
1178 }
1179
1180 logger.trace(" -- OPS -- {}", opsString);
1181 logger.trace(" -- STACK -- {}", stackData);
1182 logger.trace(" -- MEMORY -- {}", memoryData);
1183 logger.trace(" -- STORAGE -- {}\n", storageData);
1184 logger.trace("\n Spent Gas: [{}]/[{}]\n Left Gas: [{}]\n",
1185 getResult().getGasUsed(),
1186 invoke.getGas(),
1187 getRemainingGas());
1188
1189 StringBuilder globalOutput = new StringBuilder("\n");
1190 if (stackData.length() > 0) {
1191 stackData.append("\n");
1192 }
1193
1194 if (pc != 0) {
1195 globalOutput.append("[Op: ").append(OpCode.code(lastOp).name()).append("]\n");
1196 }
1197
1198 globalOutput.append(" -- OPS -- ").append(opsString).append("\n");
1199 globalOutput.append(" -- STACK -- ").append(stackData).append("\n");
1200 globalOutput.append(" -- MEMORY -- ").append(memoryData).append("\n");
1201 globalOutput.append(" -- STORAGE -- ").append(storageData).append("\n");
1202
1203 if (getResult().getHReturn() != null) {
1204 globalOutput.append("\n HReturn: ").append(
1205 ByteUtil.toHexString(getResult().getHReturn()));
1206 }
1207
1208 // sophisticated assumption that msg.data != codedata
1209 // means we are calling the contract not creating it
1210 byte[] txData = invoke.getDataCopy(DataWord.ZERO, getDataSize());
1211 if (!Arrays.equals(txData, ops)) {
1212 globalOutput.append("\n msg.data: ").append(ByteUtil.toHexString(txData));
1213 }
1214 globalOutput.append("\n\n Spent Gas: ").append(getResult().getGasUsed());
1215
1216 if (listener != null) {
1217 listener.output(globalOutput.toString());
1218 }
1219 }
1220 }
1221
1222 public void saveOpTrace() {
1223 if (this.pc < ops.length) {
1224 trace.addOp(ops[pc], pc, getCallDeep(), getRemainingGas(), this.memory, this.stack, this.storage);
1225 }
1226 }
1227
1228 public void saveOpGasCost(long gasCost) {
1229 trace.saveGasCost(gasCost);
1230 }
1231
1232 public ProgramTrace getTrace() {
1233 return trace;
1234 }
1235
1236 private int processAndSkipCodeHeader(int offset) {
1237 int ret = offset;
1238 if (ops.length >= 4) {
1239 OpCode op = OpCode.code(ops[0]);
1240 if ((op != null) && op == OpCode.HEADER) {
1241 // next byte is executable format version
1242 // header length in bytes
1243 int exe = ops[1] & 0xff;
1244 // limit to positive to prevent version 0xff < 0x00
1245 exeVersion = (byte) Math.min(exe, 127);
1246
1247 // limit to positive to prevent version 0xff < 0x00
1248 int script = ops[2] & 0xff;
1249 scriptVersion = (byte) Math.min(script, 127);
1250 int extHeaderLen = ops[3] & 0xff;
1251 ret = offset + 4 + extHeaderLen;
1252 startAddr = ret;
1253 pc = ret;
1254 }
1255 }
1256 return ret;
1257 }
1258
1259 private void precompile() {
1260 int i = 0;
1261 exeVersion = 0;
1262 scriptVersion = 0;
1263 startAddr = 0;
1264 pc = 0;
1265 i = processAndSkipCodeHeader(i);
1266 computeJumpDests(i);
1267 }
1268
1269 private void computeJumpDests(int start) {
1270 if (jumpdestSet == null) {
1271 jumpdestSet = new BitSet(ops.length);
1272 }
1273
1274 for (int i = start; i < ops.length; ++i) {
1275 OpCode op = OpCode.code(ops[i]);
1276
1277 if (op == null) {
1278 continue;
1279 }
1280
1281 if (op == OpCode.JUMPDEST) {
1282 jumpdestSet.set(i);
1283 }
1284
1285 if (op.asInt() >= OpCode.PUSH1.asInt() && op.asInt() <= OpCode.PUSH32.asInt()) {
1286 i += op.asInt() - OpCode.PUSH1.asInt() + 1;
1287 }
1288 }
1289 }
1290
1291 public DataWord getReturnDataBufferSize() {
1292 return DataWord.valueOf(getReturnDataBufferSizeI());
1293 }
1294
1295 private int getReturnDataBufferSizeI() {
1296 return returnDataBuffer == null ? 0 : returnDataBuffer.length;
1297 }
1298
1299 public Optional<byte[]> getReturnDataBufferData(DataWord off, DataWord size) {
1300 long endPosition = (long) off.intValueSafe() + size.intValueSafe();
1301 if (endPosition > getReturnDataBufferSizeI()) {
1302 return Optional.empty();
1303 }
1304
1305 if (returnDataBuffer == null) {
1306 return Optional.of(new byte[0]);
1307 }
1308
1309 byte[] copiedData = Arrays.copyOfRange(returnDataBuffer, off.intValueSafe(), Math.toIntExact(endPosition));
1310 return Optional.of(copiedData);
1311 }
1312
1313 public ActivationConfig.ForBlock getActivations() {
1314 return activations;
1315 }
1316
1317 public void addListener(ProgramOutListener listener) {
1318 this.listener = listener;
1319 }
1320
1321 public int verifyJumpDest(DataWord nextPC) {
1322 // This is painstankly slow
1323 if (nextPC.occupyMoreThan(4)) {
1324 throw ExceptionHelper.badJumpDestination(this, -1);
1325 }
1326 int ret = nextPC.intValue(); // could be negative
1327 if (ret < 0 || ret >= jumpdestSet.size() || !jumpdestSet.get(ret)) {
1328 throw ExceptionHelper.badJumpDestination(this, ret);
1329 }
1330 return ret;
1331 }
1332
1333 public void callToPrecompiledAddress(MessageCall msg, PrecompiledContract contract) {
1334
1335 if (getCallDeep() == getMaxDepth()) {
1336 stackPushZero();
1337 this.refundGas(msg.getGas().longValue(), " call deep limit reach");
1338
1339 return;
1340 }
1341
1342 Repository track = getStorage().startTracking();
1343
1344 RskAddress senderAddress = getOwnerRskAddress();
1345 RskAddress codeAddress = new RskAddress(msg.getCodeAddress());
1346 RskAddress contextAddress = msg.getType().isStateless() ? senderAddress : codeAddress;
1347
1348 Coin endowment = new Coin(msg.getEndowment().getData());
1349 Coin senderBalance = track.getBalance(senderAddress);
1350 if (senderBalance.compareTo(endowment) < 0) {
1351 stackPushZero();
1352 this.refundGas(msg.getGas().longValue(), "refund gas from message call");
1353 this.cleanReturnDataBuffer();
1354
1355 return;
1356 }
1357
1358 byte[] data = this.memoryChunk(msg.getInDataOffs().intValue(),
1359 msg.getInDataSize().intValue());
1360
1361 // Charge for endowment - is not reversible by rollback
1362 track.transfer(senderAddress, contextAddress, new Coin(msg.getEndowment().getData()));
1363
1364 // we are assuming that transfer is already creating destination account even if the amount is zero
1365 if (!track.isContract(codeAddress)) {
1366 track.setupContract(codeAddress);
1367 }
1368
1369 if (byTestingSuite()) {
1370 // This keeps track of the calls created for a test
1371 this.getResult().addCallCreate(data,
1372 codeAddress.getBytes(),
1373 msg.getGas().longValueSafe(),
1374 msg.getEndowment().getNoLeadZeroesData());
1375
1376 stackPushOne();
1377 return;
1378 }
1379
1380 // Special initialization for Bridge, Remasc and NativeContract contracts
1381 if (contract instanceof Bridge || contract instanceof RemascContract || contract instanceof NativeContract) {
1382 // CREATE CALL INTERNAL TRANSACTION
1383 InternalTransaction internalTx = addInternalTx(
1384 null,
1385 getGasLimit(),
1386 senderAddress,
1387 contextAddress,
1388 endowment,
1389 EMPTY_BYTE_ARRAY,
1390 "call"
1391 );
1392
1393 // Propagate the "local call" nature of the originating transaction down to the callee
1394 internalTx.setLocalCallTransaction(this.transaction.isLocalCallTransaction());
1395
1396 Block executionBlock = blockFactory.newBlock(
1397 blockFactory.getBlockHeaderBuilder()
1398 .setParentHash(getPrevHash().getData())
1399 .setCoinbase(new RskAddress(getCoinbase().getLast20Bytes()))
1400 .setDifficultyFromBytes(getDifficulty().getData())
1401 .setNumber(getNumber().longValue())
1402 .setGasLimit(getGasLimit().getData())
1403 .setTimestamp(getTimestamp().longValue())
1404 .build(),
1405 Collections.emptyList(),
1406 Collections.emptyList()
1407 );
1408
1409 contract.init(internalTx, executionBlock, track, this.invoke.getBlockStore(), null, result.getLogInfoList());
1410 }
1411
1412 long requiredGas = contract.getGasForData(data);
1413 if (requiredGas > msg.getGas().longValue()) {
1414 this.refundGas(0, CALL_PRECOMPILED_CAUSE); //matches cpp logic
1415 this.stackPushZero();
1416 track.rollback();
1417 this.cleanReturnDataBuffer();
1418 } else {
1419 if (getActivations().isActive(ConsensusRule.RSKIP197)) {
1420 executePrecompiledAndHandleError(contract, msg, requiredGas, track, data);
1421 } else {
1422 executePrecompiled(contract, msg, requiredGas, track, data);
1423 }
1424 }
1425 }
1426
1427 /**
1428 * This is for compatibility before RSKIP197, no error handling was implemented when calling to precompiled contracts.
1429 *
1430 * This method shouldn't be modified, all new changes should go to executePrecompiledAndHandleError() method
1431 */
1432 @Deprecated
1433 private void executePrecompiled(PrecompiledContract contract, MessageCall msg, long requiredGas, Repository track, byte[] data) {
1434 try {
1435 this.refundGas(msg.getGas().longValue() - requiredGas, CALL_PRECOMPILED_CAUSE);
1436 byte[] out = contract.execute(data);
1437 if (getActivations().isActive(ConsensusRule.RSKIP90)) {
1438 this.returnDataBuffer = out;
1439 }
1440 saveOutAfterExecution(msg, out);
1441 this.stackPushOne();
1442 track.commit();
1443 } catch (VMException e) {
1444 throw new RuntimeException(e);
1445 }
1446 }
1447
1448 /**
1449 * This is after RSKIP197, where we fix the way in which error is handled after a precompiled execution.
1450 */
1451 private void executePrecompiledAndHandleError(PrecompiledContract contract, MessageCall msg, long requiredGas, Repository track, byte[] data) {
1452 try {
1453 logger.trace("Executing Precompiled contract...");
1454 this.returnDataBuffer = contract.execute(data);
1455 logger.trace("Executing Precompiled setting output.");
1456 this.memorySaveLimited(msg.getOutDataOffs().intValue(), this.returnDataBuffer, msg.getOutDataSize().intValue());
1457 this.stackPushOne();
1458 track.commit();
1459 } catch (VMException e) {
1460 logger.trace("Precompiled execution error. Pushing Zero to stack and performing rollback.", e);
1461 this.stackPushZero();
1462 track.rollback();
1463 this.returnDataBuffer = null;
1464 } finally {
1465 final long refundingGas = msg.getGas().longValue() - requiredGas;
1466 this.refundGas(refundingGas, CALL_PRECOMPILED_CAUSE);
1467 }
1468 }
1469
1470 /**
1471 * This is for compatibility before RSKIP197. {@code memorySaveLimited()} should be called directly instead.
1472 */
1473 @Deprecated
1474 private void saveOutAfterExecution(MessageCall msg, byte[] out) {
1475 logger.trace("Executing Precompiled saving memory.");
1476 // Avoid saving null returns to memory and limit the memory it can use.
1477 // If we're behind RSK150 activation, don't care about the null return, just save.
1478 if (getActivations().isActive(ConsensusRule.RSKIP150) && out != null) {
1479 this.memorySaveLimited(msg.getOutDataOffs().intValue(), out, msg.getOutDataSize().intValue());
1480 } else if (!getActivations().isActive(ConsensusRule.RSKIP150)) {
1481 this.memorySave(msg.getOutDataOffs().intValue(), out);
1482 }
1483 }
1484
1485 private boolean byTestingSuite() {
1486 return invoke.byTestingSuite();
1487 }
1488
1489 public interface ProgramOutListener {
1490 void output(String out);
1491 }
1492
1493 @SuppressWarnings("serial")
1494 public static class OutOfGasException extends RuntimeException {
1495
1496 public OutOfGasException(String message, Object... args) {
1497 super(format(message, args));
1498 }
1499 }
1500
1501 @SuppressWarnings("serial")
1502 public static class IllegalOperationException extends RuntimeException {
1503
1504 public IllegalOperationException(String message, Object... args) {
1505 super(format(message, args));
1506 }
1507 }
1508
1509 @SuppressWarnings("serial")
1510 public static class BadJumpDestinationException extends RuntimeException {
1511
1512 public BadJumpDestinationException(String message, Object... args) {
1513 super(format(message, args));
1514 }
1515 }
1516
1517
1518 @SuppressWarnings("serial")
1519 public static class StackTooSmallException extends RuntimeException {
1520
1521 public StackTooSmallException(String message, Object... args) {
1522 super(format(message, args));
1523 }
1524 }
1525
1526
1527 @SuppressWarnings("serial")
1528 public static class StaticCallModificationException extends RuntimeException {
1529 public StaticCallModificationException(String message, Object... args) {
1530 super(format(message, args));
1531 }
1532 }
1533
1534 public static class AddressCollisionException extends RuntimeException {
1535 public AddressCollisionException(String message) {
1536 super(message);
1537 }
1538 }
1539
1540 public static class ExceptionHelper {
1541
1542 private ExceptionHelper() { }
1543
1544 public static StaticCallModificationException modificationException(@Nonnull Program program) {
1545 return new StaticCallModificationException("Attempt to call a state modifying opcode inside STATICCALL: tx[%s]", extractTxHash(program));
1546 }
1547
1548 public static OutOfGasException notEnoughOpGas(@Nonnull Program program, OpCode op, long opGas, long programGas) {
1549 return new OutOfGasException("Not enough gas for '%s' operation executing: opGas[%d], programGas[%d], tx[%s]", op, opGas, programGas, extractTxHash(program));
1550 }
1551
1552 public static OutOfGasException notEnoughOpGas(Program program, OpCode op, DataWord opGas, DataWord programGas) {
1553 return notEnoughOpGas(program, op, opGas.longValue(), programGas.longValue());
1554 }
1555
1556 public static OutOfGasException notEnoughOpGas(Program program, OpCode op, BigInteger opGas, BigInteger programGas) {
1557 return notEnoughOpGas(program, op, opGas.longValue(), programGas.longValue());
1558 }
1559
1560 public static OutOfGasException notEnoughSpendingGas(@Nonnull Program program, String cause, long gasValue) {
1561 return new OutOfGasException("Not enough gas for '%s' cause spending: invokeGas[%d], gas[%d], usedGas[%d], tx[%s]",
1562 cause, program.invoke.getGas(), gasValue, program.getResult().getGasUsed(), extractTxHash(program));
1563 }
1564
1565 public static OutOfGasException gasOverflow(@Nonnull Program program, BigInteger actualGas, BigInteger gasLimit) {
1566 return new OutOfGasException("Gas value overflow: actualGas[%d], gasLimit[%d], tx[%s]", actualGas.longValue(), gasLimit.longValue(), extractTxHash(program));
1567 }
1568 public static OutOfGasException gasOverflow(@Nonnull Program program, long actualGas, BigInteger gasLimit) {
1569 return new OutOfGasException("Gas value overflow: actualGas[%d], gasLimit[%d], tx[%s]", actualGas, gasLimit.longValue(), extractTxHash(program));
1570 }
1571 public static IllegalOperationException invalidOpCode(@Nonnull Program program) {
1572 return new IllegalOperationException("Invalid operation code: opcode[%s], tx[%s]", ByteUtil.toHexString(new byte[] {program.getCurrentOp()}, 0, 1), extractTxHash(program));
1573 }
1574
1575 public static BadJumpDestinationException badJumpDestination(@Nonnull Program program, int pc) {
1576 return new BadJumpDestinationException("Operation with pc isn't 'JUMPDEST': PC[%d], tx[%s]", pc, extractTxHash(program));
1577 }
1578
1579 public static StackTooSmallException tooSmallStack(@Nonnull Program program, int expectedSize, int actualSize) {
1580 return new StackTooSmallException("Expected stack size %d but actual %d, tx: %s", expectedSize, actualSize, extractTxHash(program));
1581 }
1582
1583 public static RuntimeException tooLargeContractSize(@Nonnull Program program, int maxSize, int actualSize) {
1584 return new RuntimeException(format("Maximum contract size allowed %d but actual %d, tx: %s", maxSize, actualSize, extractTxHash(program)));
1585 }
1586
1587 public static AddressCollisionException addressCollisionException(@Nonnull Program program, RskAddress address) {
1588 return new AddressCollisionException("Trying to create a contract with existing contract address: 0x" + address + ", tx: " + extractTxHash(program));
1589 }
1590
1591 @Nonnull
1592 private static String extractTxHash(@Nonnull Program program) {
1593 return program.transaction == null ? "<null>" : "0x" + program.transaction.getHash().toHexString();
1594 }
1595 }
1596
1597 @SuppressWarnings("serial")
1598 public class StackTooLargeException extends RuntimeException {
1599 public StackTooLargeException(String message) {
1600 super(message);
1601 }
1602 }
1603
1604 /**
1605 * used mostly for testing reasons
1606 */
1607 public byte[] getMemory() {
1608 return memory.read(0, memory.size());
1609 }
1610
1611 /**
1612 * used mostly for testing reasons
1613 */
1614 public void initMem(byte[] data) {
1615 this.memory.write(0, data, data.length, false);
1616 }
1617
1618 public byte getExeVersion() {
1619 return exeVersion;
1620 }
1621 public byte getScriptVersion() {
1622 return scriptVersion;
1623 }
1624 public int getStartAddr(){
1625 return startAddr;
1626 }
1627
1628 @VisibleForTesting
1629 public BitSet getJumpdestSet() { return this.jumpdestSet; }
1630 }