diff --git a/src/main/java/org/tron/common/runtime/RuntimeImpl.java b/src/main/java/org/tron/common/runtime/RuntimeImpl.java index 0ce63e9b461..3e47d78369d 100644 --- a/src/main/java/org/tron/common/runtime/RuntimeImpl.java +++ b/src/main/java/org/tron/common/runtime/RuntimeImpl.java @@ -92,7 +92,7 @@ public class RuntimeImpl implements Runtime { @Getter @Setter - private boolean isStaticCall = false; + private boolean isConstantCall = false; @Setter private boolean enableEventLinstener; @@ -136,9 +136,9 @@ public RuntimeImpl(TransactionTrace trace, BlockCapsule block, Deposit deposit, * For constant trx with latest blockCap. */ public RuntimeImpl(Transaction tx, BlockCapsule block, DepositImpl deposit, - ProgramInvokeFactory programInvokeFactory, boolean isStaticCall) { + ProgramInvokeFactory programInvokeFactory, boolean isConstantCall) { this(tx, block, deposit, programInvokeFactory); - this.isStaticCall = isStaticCall; + this.isConstantCall = isConstantCall; } private RuntimeImpl(Transaction tx, BlockCapsule block, DepositImpl deposit, @@ -546,7 +546,7 @@ private void call() } AccountCapsule caller = this.deposit.getAccount(callerAddress); long energyLimit; - if (isStaticCall) { + if (isConstantCall) { energyLimit = Constant.ENERGY_LIMIT_IN_CONSTANT_TX; } else { AccountCapsule creator = this.deposit @@ -564,8 +564,8 @@ private void call() .createProgramInvoke(TrxType.TRX_CONTRACT_CALL_TYPE, executorType, trx, tokenValue, tokenId, blockCap.getInstance(), deposit, vmStartInUs, vmShouldEndInUs, energyLimit); - if (isStaticCall) { - programInvoke.setStaticCall(); + if (isConstantCall) { + programInvoke.setConstantCall(); } this.vm = new VM(config); rootInternalTransaction = new InternalTransaction(trx, trxType); @@ -616,7 +616,7 @@ public void go() { vm.play(program); result = program.getResult(); - if (isStaticCall) { + if (isConstantCall) { long callValue = TransactionCapsule.getCallValue(trx.getRawData().getContract(0)); long callTokenValue = TransactionCapsule .getCallTokenValue(trx.getRawData().getContract(0)); @@ -624,6 +624,10 @@ public void go() { runtimeError = "constant cannot set call value or call token value."; result.rejectInternalTransactions(); } + if (result.getException() != null) { + runtimeError = result.getException().getMessage(); + result.rejectInternalTransactions(); + } return; } @@ -702,7 +706,7 @@ public void go() { } logger.info("runtime result is :{}", result.getException().getMessage()); } - if (!isStaticCall) { + if (!isConstantCall) { trace.setBill(result.getEnergyUsed()); } } diff --git a/src/main/java/org/tron/common/runtime/utils/MUtil.java b/src/main/java/org/tron/common/runtime/utils/MUtil.java index 8f428014a03..0132b79d770 100644 --- a/src/main/java/org/tron/common/runtime/utils/MUtil.java +++ b/src/main/java/org/tron/common/runtime/utils/MUtil.java @@ -58,13 +58,4 @@ public static byte[] convertToTronAddress(byte[] address) { } return address; } - - public static byte[] allZero32TronAddress() { - byte[] newAddress = new byte[32]; - byte[] temp = new byte[]{Wallet.getAddressPreFixByte()}; - System.arraycopy(temp, 0, newAddress, 11, temp.length); - - return newAddress; - } - } diff --git a/src/main/java/org/tron/common/runtime/vm/DataWord.java b/src/main/java/org/tron/common/runtime/vm/DataWord.java index d938a259faa..442768b4042 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -521,4 +521,19 @@ public static DataWord[] parseArray(byte[] data) { } return words; } + + public static boolean equalAddressByteArray(byte[] arr1, byte[] arr2){ + if (arr1 == arr2) + return true; + if (arr1==null || arr2==null || arr1.length < 20 || arr2.length < 20) + return false; + + int i = arr1.length - 20; + int j = arr2.length - 20; + + for (; i < arr1.length && j < arr2.length; i++, j++) + if (arr1[i] != arr2[j]) + return false; + return true; + } } diff --git a/src/main/java/org/tron/common/runtime/vm/EnergyCost.java b/src/main/java/org/tron/common/runtime/vm/EnergyCost.java index 22cf4164bfe..94a87b3a2c2 100644 --- a/src/main/java/org/tron/common/runtime/vm/EnergyCost.java +++ b/src/main/java/org/tron/common/runtime/vm/EnergyCost.java @@ -276,6 +276,7 @@ public int getEXT_CODE_HASH() { return EXT_CODE_HASH; } + private static EnergyCost instance = null; public static EnergyCost getInstance() { diff --git a/src/main/java/org/tron/common/runtime/vm/PrecompiledContracts.java b/src/main/java/org/tron/common/runtime/vm/PrecompiledContracts.java index 27481feeca3..c1baffaf38c 100644 --- a/src/main/java/org/tron/common/runtime/vm/PrecompiledContracts.java +++ b/src/main/java/org/tron/common/runtime/vm/PrecompiledContracts.java @@ -50,6 +50,7 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; import org.spongycastle.util.encoders.Hex; import org.tron.common.crypto.ECKey; @@ -60,7 +61,6 @@ import org.tron.common.crypto.zksnark.Fp; import org.tron.common.crypto.zksnark.PairingCheck; import org.tron.common.runtime.config.VMConfig; -import org.tron.common.runtime.utils.MUtil; import org.tron.common.runtime.vm.program.Program; import org.tron.common.runtime.vm.program.ProgramResult; import org.tron.common.storage.Deposit; @@ -227,7 +227,7 @@ public ProgramResult getResult() { @Setter @Getter - private boolean isStaticCall; + private boolean isConstantCall; @Getter @Setter @@ -683,7 +683,7 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { - if (isStaticCall()) { + if (isConstantCall()) { return Pair.of(true, new DataWord(0).getData()); } if (data == null || data.length != 2 * DataWord.WORD_SIZE) { @@ -880,7 +880,7 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { - if (isStaticCall()) { + if (isConstantCall()) { return Pair.of(true, new DataWord(0).getData()); } @@ -946,7 +946,7 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { - if (isStaticCall()) { + if (isConstantCall()) { return Pair.of(true, new DataWord(0).getData()); } @@ -1022,7 +1022,7 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { - if (isStaticCall()) { + if (isConstantCall()) { return Pair.of(true, new DataWord(0).getData()); } @@ -1107,7 +1107,7 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { - if (isStaticCall()) { + if (isConstantCall()) { return Pair.of(true, new DataWord(0).getData()); } @@ -1331,7 +1331,6 @@ public Pair execute(byte[] data) { public static class MultiValidateSign extends PrecompiledContract { private static final ExecutorService workers; private static final int ENGERYPERSIGN = 1500; - private static final byte[] ZEROADDR = MUtil.allZero32TronAddress(); private static final byte[] EMPTYADDR = new byte[DataWord.WORD_SIZE]; static { @@ -1361,6 +1360,7 @@ public ValidateSignResult call() { @Data @AllArgsConstructor private static class ValidateSignResult { + private Boolean res; private int nonce; } @@ -1395,7 +1395,7 @@ private Pair doExecute(byte[] data) } int min = Math.min(cnt, DataWord.WORD_SIZE); byte[] res = new byte[DataWord.WORD_SIZE]; - if (isStaticCall()) { + if (isConstantCall()) { //for static call not use thread pool to avoid potential effect for (int i = 0; i < min; i++) { if (validSign(signatures[i], hash, addresses[i])) { @@ -1412,7 +1412,8 @@ private Pair doExecute(byte[] data) .submit(new ValidateSignTask(countDownLatch, hash, signatures[i], addresses[i], i)); futures.add(future); } - boolean withNoTimeout = countDownLatch.await(getCPUTimeLeftInUs() * 1000, TimeUnit.NANOSECONDS); + boolean withNoTimeout = countDownLatch + .await(getCPUTimeLeftInUs() * 1000, TimeUnit.NANOSECONDS); if (!withNoTimeout) { logger.info("MultiValidateSign timeout"); @@ -1433,9 +1434,9 @@ private static boolean validSign(byte[] sign, byte[] hash, byte[] address) { byte v; byte[] r; byte[] s; - DataWord out = null; - if (sign.length < 65 || Arrays.equals(ZEROADDR, address) - || Arrays.equals(EMPTYADDR, address)) { + byte[] out = null; + if (ArrayUtils.isEmpty(sign) || sign.length < 65 + || DataWord.equalAddressByteArray(EMPTYADDR, address)) { return false; } try { @@ -1447,13 +1448,12 @@ private static boolean validSign(byte[] sign, byte[] hash, byte[] address) { } ECKey.ECDSASignature signature = ECKey.ECDSASignature.fromComponents(r, s, v); if (signature.validateComponents()) { - out = new DataWord(ECKey.signatureToAddress(hash, signature)); + out = ECKey.signatureToAddress(hash, signature); } } catch (Throwable any) { logger.info("ECRecover error", any.getMessage()); } - return out != null && Arrays.equals(new DataWord(address).getLast20Bytes(), - out.getLast20Bytes()); + return DataWord.equalAddressByteArray(address, out); } private static byte[][] extractBytes32Array(DataWord[] words, int offset) { diff --git a/src/main/java/org/tron/common/runtime/vm/VM.java b/src/main/java/org/tron/common/runtime/vm/VM.java index 5415a5c0479..740860534ab 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -1074,7 +1074,7 @@ public void step(Program program) { case LOG3: case LOG4: { - if (program.isStaticCall()) { + if (program.isConstantCall()) { throw new Program.StaticCallModificationException(); } DataWord address = program.getContractAddress(); @@ -1152,7 +1152,7 @@ public void step(Program program) { } break; case SSTORE: { - if (program.isStaticCall()) { + if (program.isConstantCall()) { throw new Program.StaticCallModificationException(); } @@ -1283,7 +1283,7 @@ public void step(Program program) { } break; case CREATE: { - if (program.isStaticCall()) { + if (program.isConstantCall()) { throw new Program.StaticCallModificationException(); } DataWord value = program.stackPop(); @@ -1295,7 +1295,7 @@ public void step(Program program) { } break; case CREATE2: { - if (program.isStaticCall()) { + if (program.isConstantCall()) { throw new Program.StaticCallModificationException(); } DataWord value = program.stackPop(); @@ -1330,7 +1330,7 @@ public void step(Program program) { value = DataWord.ZERO; } - if (program.isStaticCall() && (op == CALL || op == CALLTOKEN) && !value.isZero()) { + if (program.isConstantCall() && (op == CALL || op == CALLTOKEN) && !value.isZero()) { throw new Program.StaticCallModificationException(); } @@ -1409,7 +1409,7 @@ public void step(Program program) { break; } case SUICIDE: { - if (program.isStaticCall()) { + if (program.isConstantCall()) { throw new Program.StaticCallModificationException(); } @@ -1427,7 +1427,6 @@ public void step(Program program) { default: break; } - program.setPreviouslyExecutedOp(op.val()); } catch (RuntimeException e) { logger.info("VM halted: [{}]", e.getMessage()); diff --git a/src/main/java/org/tron/common/runtime/vm/program/ContractState.java b/src/main/java/org/tron/common/runtime/vm/program/ContractState.java index 2fa3afbf600..68e68431ec4 100644 --- a/src/main/java/org/tron/common/runtime/vm/program/ContractState.java +++ b/src/main/java/org/tron/common/runtime/vm/program/ContractState.java @@ -54,6 +54,11 @@ public Manager getDbManager() { return deposit.getDbManager(); } + @Override + public AccountCapsule createNormalAccount(byte[] address) { + return deposit.createNormalAccount(address); + } + @Override public void setProgramListener(ProgramListener listener) { this.programListener = listener; diff --git a/src/main/java/org/tron/common/runtime/vm/program/Program.java b/src/main/java/org/tron/common/runtime/vm/program/Program.java index e0a31bb5c56..250ffffc33e 100644 --- a/src/main/java/org/tron/common/runtime/vm/program/Program.java +++ b/src/main/java/org/tron/common/runtime/vm/program/Program.java @@ -701,6 +701,7 @@ public void callToAddress(MessageCall msg) { msg.getEndowment().getNoLeadZeroesData()); } else if (!ArrayUtils.isEmpty(senderAddress) && !ArrayUtils.isEmpty(contextAddress) && senderAddress != contextAddress && endowment > 0) { + createAccountIfNotExist(deposit, contextAddress); if (!isTokenTransfer) { try { TransferActuator @@ -749,7 +750,7 @@ this, new DataWord(contextAddress), !isTokenTransfer ? callValue : new DataWord(0), !isTokenTransfer ? new DataWord(0) : callValue, !isTokenTransfer ? new DataWord(0) : msg.getTokenId(), - contextBalance, data, deposit, msg.getType().callIsStatic() || isStaticCall(), + contextBalance, data, deposit, msg.getType().callIsStatic() || isConstantCall(), byTestingSuite(), vmStartInUs, getVmShouldEndInUs(), msg.getEnergy().longValueSafe()); VM vm = new VM(config); Program program = new Program(programCode, programInvoke, internalTx, config, @@ -1044,8 +1045,8 @@ public DataWord getDifficulty() { return invoke.getDifficulty().clone(); } - public boolean isStaticCall() { - return invoke.isStaticCall(); + public boolean isConstantCall() { + return invoke.isConstantCall(); } public ProgramResult getResult() { @@ -1450,7 +1451,7 @@ public void callToPrecompiledAddress(MessageCall msg, // this is the depositImpl, not contractState as above contract.setDeposit(deposit); contract.setResult(this.result); - contract.setStaticCall(isStaticCall()); + contract.setConstantCall(isConstantCall()); contract.setVmShouldEndInUs(getVmShouldEndInUs()); Pair out = contract.execute(data); @@ -1779,4 +1780,14 @@ public long getVmStartInUs() { private boolean isContractExist(AccountCapsule existingAddr, Deposit deposit) { return deposit.getContract(existingAddr.getAddress().toByteArray()) != null; } + + private void createAccountIfNotExist(Deposit deposit, byte[] contextAddress) { + if (VMConfig.allowTvmSolidity059()) { + //after solidity059 proposal , allow contract transfer trc10 or trx to non-exist address(would create one) + AccountCapsule sender = deposit.getAccount(contextAddress); + if (sender == null) { + deposit.createNormalAccount(contextAddress); + } + } + } } diff --git a/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvoke.java b/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvoke.java index 4553d19a25a..a1fb3c70364 100644 --- a/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvoke.java +++ b/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvoke.java @@ -63,7 +63,7 @@ public interface ProgramInvoke { Deposit getDeposit(); - boolean isStaticCall(); + boolean isConstantCall(); long getVmShouldEndInUs(); @@ -71,7 +71,7 @@ public interface ProgramInvoke { long getEnergyLimit(); - void setStaticCall(); + void setConstantCall(); BlockCapsule getBlockByNum(int index); } diff --git a/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeImpl.java b/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeImpl.java index 5c35ad484cf..36e1c14ebf9 100644 --- a/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeImpl.java +++ b/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeImpl.java @@ -21,12 +21,10 @@ import java.util.Arrays; import java.util.Objects; import lombok.extern.slf4j.Slf4j; -import org.spongycastle.util.encoders.Hex; import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.program.Program.IllegalOperationException; import org.tron.common.storage.Deposit; import org.tron.core.capsule.BlockCapsule; -import org.tron.core.db.BlockStore; import org.tron.core.exception.StoreException; @Slf4j @@ -57,13 +55,13 @@ public class ProgramInvokeImpl implements ProgramInvoke { private boolean byTransaction = true; private boolean byTestingSuite = false; private int callDeep = 0; - private boolean isStaticCall = false; + private boolean isConstantCall = false; public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance, DataWord callValue, DataWord tokenValue, DataWord tokenId, byte[] msgData, DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord difficulty, - Deposit deposit, int callDeep, boolean isStaticCall, boolean byTestingSuite, + Deposit deposit, int callDeep, boolean isConstantCall, boolean byTestingSuite, long vmStartInUs, long vmShouldEndInUs, long energyLimit) { this.address = address; this.origin = origin; @@ -85,7 +83,7 @@ public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, Dat this.deposit = deposit; this.byTransaction = false; - this.isStaticCall = isStaticCall; + this.isConstantCall = isConstantCall; this.byTestingSuite = byTestingSuite; this.vmStartInUs = vmStartInUs; this.vmShouldEndInUs = vmShouldEndInUs; @@ -262,8 +260,8 @@ public Deposit getDeposit() { } @Override - public boolean isStaticCall() { - return isStaticCall; + public boolean isConstantCall() { + return isConstantCall; } @Override @@ -374,8 +372,8 @@ public long getEnergyLimit() { } @Override - public void setStaticCall() { - isStaticCall = true; + public void setConstantCall() { + isConstantCall = true; } @Override diff --git a/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeMockImpl.java b/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeMockImpl.java index 9f68412715a..02f888f2220 100644 --- a/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeMockImpl.java +++ b/src/main/java/org/tron/common/runtime/vm/program/invoke/ProgramInvokeMockImpl.java @@ -48,7 +48,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke { private byte[] ownerAddress = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826"); private final byte[] contractAddress = Hex.decode("471fd3ad3e9eeadeec4608b92d16ce6b500704cc"); - private boolean isStaticCall; + private boolean isConstantCall; public ProgramInvokeMockImpl(byte[] msgDataRaw) { this(); @@ -224,8 +224,8 @@ public void setOwnerAddress(byte[] ownerAddress) { } @Override - public boolean isStaticCall() { - return isStaticCall; + public boolean isConstantCall() { + return isConstantCall; } @Override @@ -243,8 +243,8 @@ public void setEnergyLimit(long customizedEnergyLimit) { } @Override - public void setStaticCall() { - isStaticCall = true; + public void setConstantCall() { + isConstantCall = true; } @Override diff --git a/src/main/java/org/tron/common/storage/Deposit.java b/src/main/java/org/tron/common/storage/Deposit.java index 4763d942872..7e0bba2f9cd 100644 --- a/src/main/java/org/tron/common/storage/Deposit.java +++ b/src/main/java/org/tron/common/storage/Deposit.java @@ -12,15 +12,17 @@ import org.tron.core.capsule.VotesCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.db.Manager; -import org.tron.protos.Protocol; +import org.tron.protos.Protocol.AccountType; public interface Deposit { Manager getDbManager(); - AccountCapsule createAccount(byte[] address, Protocol.AccountType type); + AccountCapsule createNormalAccount(byte[] address); - AccountCapsule createAccount(byte[] address, String accountName, Protocol.AccountType type); + AccountCapsule createAccount(byte[] address, AccountType type); + + AccountCapsule createAccount(byte[] address, String accountName, AccountType type); AccountCapsule getAccount(byte[] address); diff --git a/src/main/java/org/tron/common/storage/DepositImpl.java b/src/main/java/org/tron/common/storage/DepositImpl.java index 17da1d21b36..b15ff9ac7c8 100644 --- a/src/main/java/org/tron/common/storage/DepositImpl.java +++ b/src/main/java/org/tron/common/storage/DepositImpl.java @@ -79,6 +79,18 @@ public Manager getDbManager() { return dbManager; } + @Override + public AccountCapsule createNormalAccount(byte[] address) { + boolean withDefaultPermission = + dbManager.getDynamicPropertiesStore().getAllowMultiSign() == 1; + Key key = new Key(address); + AccountCapsule account = new AccountCapsule(ByteString.copyFrom(address), AccountType.Normal, + dbManager.getHeadBlockTimeStamp(), withDefaultPermission, dbManager); + + accountCache.put(key, new Value(account.getData(), Type.VALUE_TYPE_CREATE)); + return account; + } + private BlockStore getBlockStore() { return dbManager.getBlockStore(); } diff --git a/src/test/java/org/tron/common/runtime/vm/BatchSendTest.java b/src/test/java/org/tron/common/runtime/vm/BatchSendTest.java new file mode 100644 index 00000000000..5387ea3237d --- /dev/null +++ b/src/test/java/org/tron/common/runtime/vm/BatchSendTest.java @@ -0,0 +1,187 @@ +package org.tron.common.runtime.vm; + +import com.google.protobuf.ByteString; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; +import org.tron.common.application.Application; +import org.tron.common.application.ApplicationFactory; +import org.tron.common.application.TronApplicationContext; +import org.tron.common.crypto.ECKey; +import org.tron.common.runtime.Runtime; +import org.tron.common.runtime.TvmTestUtils; +import org.tron.common.runtime.config.VMConfig; +import org.tron.common.storage.DepositImpl; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.FileUtil; +import org.tron.common.utils.Utils; +import org.tron.core.Constant; +import org.tron.core.Wallet; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.config.DefaultConfig; +import org.tron.core.config.args.Args; +import org.tron.core.db.Manager; +import org.tron.core.exception.ContractExeException; +import org.tron.core.exception.ContractValidateException; +import org.tron.core.exception.ReceiptCheckErrException; +import org.tron.core.exception.VMIllegalException; +import org.tron.protos.Protocol.AccountType; +import org.tron.protos.Protocol.Transaction; +import stest.tron.wallet.common.client.utils.AbiUtil; + +@Slf4j +public class BatchSendTest { + + private static Runtime runtime; + private static Manager dbManager; + private static TronApplicationContext context; + private static Application appT; + private static DepositImpl deposit; + private static final String dbPath = "output_BatchSendTest"; + private static final String OWNER_ADDRESS; + private static final String TRANSFER_TO; + private static final long TOTAL_SUPPLY = 1000_000_000L; + private static final int TRX_NUM = 10; + private static final int NUM = 1; + private static final long START_TIME = 1; + private static final long END_TIME = 2; + private static final int VOTE_SCORE = 2; + private static final String DESCRIPTION = "TRX"; + private static final String URL = "https://tron.network"; + private static AccountCapsule ownerCapsule; + + static { + Args.setParam(new String[]{"--output-directory", dbPath, "--debug"}, Constant.TEST_CONF); + context = new TronApplicationContext(DefaultConfig.class); + appT = ApplicationFactory.create(context); + OWNER_ADDRESS = Wallet.getAddressPreFixString() + "abd4b9367799eaa3197fecb144eb71de1e049abc"; + TRANSFER_TO = Wallet.getAddressPreFixString() + "548794500882809695a8a687866e76d4271a1abc"; + dbManager = context.getBean(Manager.class); + deposit = DepositImpl.createRoot(dbManager); + deposit.createAccount(Hex.decode(TRANSFER_TO), AccountType.Normal); + deposit.addBalance(Hex.decode(TRANSFER_TO), 10); + deposit.commit(); + ownerCapsule = + new AccountCapsule( + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)), + ByteString.copyFromUtf8("owner"), + AccountType.AssetIssue); + + ownerCapsule.setBalance(1000_1000_1000L); + dbManager.getAccountStore().put(ownerCapsule.getAddress().toByteArray(), ownerCapsule); + dbManager.getDynamicPropertiesStore().saveAllowSameTokenName(1); + VMConfig.initAllowMultiSign(1); + VMConfig.initAllowTvmTransferTrc10(1); + VMConfig.initAllowTvmConstantinople(1); + VMConfig.initAllowTvmSolidity059(1); + } + + + /** + * pragma solidity ^0.5.4; + * + * contract TestBatchSendTo { constructor() public payable{} + * + * function depositIn() public payable{} + * + * + * function batchSendTo (address payable to1 ,address payable to2 ,address payable to3, uint256 + * m1,uint256 m2,uint256 m3) public { to1.send(m1 ); to2.send(m2 ); to3.send(m3 ); } + * + * } + */ + @Test + public void TransferTokenTest() + throws ContractExeException, ReceiptCheckErrException, VMIllegalException, ContractValidateException { + // 1. Deploy*/ + byte[] contractAddress = deployTransferContract(); + deposit.commit(); + Assert.assertEquals(1000, dbManager.getAccountStore().get(contractAddress).getBalance()); + + String selectorStr = "batchSendTo(address,address,address,uint256,uint256,uint256)"; + ECKey ecKey1 = new ECKey(Utils.getRandom()); + ECKey ecKey2 = new ECKey(Utils.getRandom()); + ECKey ecKey3 = new ECKey(Utils.getRandom()); + + List params = new ArrayList<>(); + params.add(Wallet.encode58Check(ecKey1.getAddress())); + params.add(Wallet.encode58Check(ecKey2.getAddress())); + params.add(Wallet.encode58Check(ecKey3.getAddress())); + params.add(100); + params.add(1100); + params.add(200); + byte[] input = Hex.decode(AbiUtil + .parseMethod(selectorStr, params)); + + // 2. Test trigger with tokenValue and tokenId, also test internal transaction transferToken function */ + long triggerCallValue = 0; + long feeLimit = 100000000; + long tokenValue = 0; + Transaction transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + input, + triggerCallValue, feeLimit, tokenValue, 0); + runtime = TvmTestUtils.processTransactionAndReturnRuntime(transaction, dbManager, null); + Assert.assertNull(runtime.getRuntimeError()); + //send success, create account + Assert.assertEquals(100, dbManager.getAccountStore().get(ecKey1.getAddress()).getBalance()); + //send failed, do not create account + Assert.assertNull(dbManager.getAccountStore().get(ecKey2.getAddress())); + //send success, create account + Assert.assertEquals(200, dbManager.getAccountStore().get(ecKey3.getAddress()).getBalance()); + + } + + + private byte[] deployTransferContract() + throws ContractExeException, ReceiptCheckErrException, ContractValidateException, VMIllegalException { + String contractName = "TestTransferTo"; + byte[] address = Hex.decode(OWNER_ADDRESS); + String ABI = + "[]"; + String code = "608060405261019c806100136000396000f3fe608060405260043610610045577c0100000000000" + + "00000000000000000000000000000000000000000000060003504632a205edf811461004a5780634cd2270c" + + "146100c8575b600080fd5b34801561005657600080fd5b50d3801561006357600080fd5b50d2801561007057" + + "600080fd5b506100c6600480360360c081101561008757600080fd5b5073ffffffffffffffffffffffffffff" + + "ffffffffffff813581169160208101358216916040820135169060608101359060808101359060a001356100" + + "d0565b005b6100c661016e565b60405173ffffffffffffffffffffffffffffffffffffffff87169084156108" + + "fc029085906000818181858888f1505060405173ffffffffffffffffffffffffffffffffffffffff89169350" + + "85156108fc0292508591506000818181858888f1505060405173ffffffffffffffffffffffffffffffffffff" + + "ffff8816935084156108fc0292508491506000818181858888f15050505050505050505050565b56fea165627" + + "a7a72305820cc2d598d1b3f968bbdc7825ce83d22dad48192f4bf95bda7f9e4ddf61669ba830029"; + + long value = 1000; + long feeLimit = 100000000; + long consumeUserResourcePercent = 0; + long tokenValue = 0; + long tokenId = 0; + + byte[] contractAddress = TvmTestUtils + .deployContractWholeProcessReturnContractAddress(contractName, address, ABI, code, value, + feeLimit, consumeUserResourcePercent, null, tokenValue, tokenId, + deposit, null); + return contractAddress; + } + + + /** + * Release resources. + */ + @AfterClass + public static void destroy() { + Args.clearParam(); + appT.shutdownServices(); + appT.shutdown(); + context.destroy(); + if (FileUtil.deleteDir(new File(dbPath))) { + logger.info("Release resources successful."); + } else { + logger.info("Release resources failure."); + } + } +} diff --git a/src/test/java/org/tron/common/runtime/vm/MultiValidateSignContractTest.java b/src/test/java/org/tron/common/runtime/vm/MultiValidateSignContractTest.java index c55199bd916..2dca12ce378 100644 --- a/src/test/java/org/tron/common/runtime/vm/MultiValidateSignContractTest.java +++ b/src/test/java/org/tron/common/runtime/vm/MultiValidateSignContractTest.java @@ -102,11 +102,12 @@ void correctionTest() { ECKey key = new ECKey(); byte[] sign = key.sign(hash).toByteArray(); if (i % 5 == 0) { + addresses.add(Wallet.encode58Check(MUtil.convertToTronAddress(new byte[20]))); signatures.add(Hex.toHexString(DataWord.ONE().getData())); } else { + addresses.add(Wallet.encode58Check(key.getAddress())); signatures.add(Hex.toHexString(sign)); } - addresses.add(Wallet.encode58Check(key.getAddress())); } Pair ret = null; ret = validateMultiSign(hash, signatures, addresses); diff --git a/src/test/java/org/tron/common/runtime/vm/TransferFailedEnergyTest.java b/src/test/java/org/tron/common/runtime/vm/TransferFailedEnergyTest.java index e4c33d3c017..063727e2d86 100644 --- a/src/test/java/org/tron/common/runtime/vm/TransferFailedEnergyTest.java +++ b/src/test/java/org/tron/common/runtime/vm/TransferFailedEnergyTest.java @@ -218,6 +218,7 @@ public void testTransferFailedAfterAllowTvmConstantinopl() throws ContractExeException, ReceiptCheckErrException, VMIllegalException, ContractValidateException { VMConfig.initAllowTvmTransferTrc10(1); VMConfig.initAllowTvmConstantinople(1); + VMConfig.initAllowTvmSolidity059(0); String contractName = "EnergyOfTransferFailedTest"; byte[] address = Hex.decode(OWNER_ADDRESS); @@ -254,6 +255,7 @@ public void testTransferFailedBeforeAllowTvmConstantinopl() throws ContractExeException, ReceiptCheckErrException, VMIllegalException, ContractValidateException { VMConfig.initAllowTvmTransferTrc10(1); VMConfig.initAllowTvmConstantinople(0); + VMConfig.initAllowTvmSolidity059(0); String contractName = "EnergyOfTransferFailedTest"; byte[] address = Hex.decode(OWNER_ADDRESS); diff --git a/src/test/java/org/tron/common/runtime/vm/TransferToAccountTest.java b/src/test/java/org/tron/common/runtime/vm/TransferToAccountTest.java new file mode 100644 index 00000000000..7bd54823257 --- /dev/null +++ b/src/test/java/org/tron/common/runtime/vm/TransferToAccountTest.java @@ -0,0 +1,311 @@ +package org.tron.common.runtime.vm; + +import com.google.protobuf.ByteString; +import java.io.File; +import lombok.extern.slf4j.Slf4j; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; +import org.tron.common.application.Application; +import org.tron.common.application.ApplicationFactory; +import org.tron.common.application.TronApplicationContext; +import org.tron.common.crypto.ECKey; +import org.tron.common.runtime.Runtime; +import org.tron.common.runtime.RuntimeImpl; +import org.tron.common.runtime.TvmTestUtils; +import org.tron.common.runtime.config.VMConfig; +import org.tron.common.runtime.vm.program.invoke.ProgramInvokeFactoryImpl; +import org.tron.common.storage.DepositImpl; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.FileUtil; +import org.tron.common.utils.Utils; +import org.tron.core.Constant; +import org.tron.core.Wallet; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.AssetIssueCapsule; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.config.DefaultConfig; +import org.tron.core.config.args.Args; +import org.tron.core.db.Manager; +import org.tron.core.exception.ContractExeException; +import org.tron.core.exception.ContractValidateException; +import org.tron.core.exception.ReceiptCheckErrException; +import org.tron.core.exception.VMIllegalException; +import org.tron.protos.Contract.AssetIssueContract; +import org.tron.protos.Protocol.AccountType; +import org.tron.protos.Protocol.Block; +import org.tron.protos.Protocol.Transaction; +import stest.tron.wallet.common.client.utils.AbiUtil; + +@Slf4j +public class TransferToAccountTest { + + private static Runtime runtime; + private static Manager dbManager; + private static TronApplicationContext context; + private static Application appT; + private static DepositImpl deposit; + private static final String dbPath = "output_TransferToAccountTest"; + private static final String OWNER_ADDRESS; + private static final String TRANSFER_TO; + private static final long TOTAL_SUPPLY = 1000_000_000L; + private static final int TRX_NUM = 10; + private static final int NUM = 1; + private static final long START_TIME = 1; + private static final long END_TIME = 2; + private static final int VOTE_SCORE = 2; + private static final String DESCRIPTION = "TRX"; + private static final String URL = "https://tron.network"; + private static AccountCapsule ownerCapsule; + + static { + Args.setParam(new String[]{"--output-directory", dbPath, "--debug"}, Constant.TEST_CONF); + context = new TronApplicationContext(DefaultConfig.class); + appT = ApplicationFactory.create(context); + OWNER_ADDRESS = Wallet.getAddressPreFixString() + "abd4b9367799eaa3197fecb144eb71de1e049abc"; + TRANSFER_TO = Wallet.getAddressPreFixString() + "548794500882809695a8a687866e76d4271a1abc"; + dbManager = context.getBean(Manager.class); + deposit = DepositImpl.createRoot(dbManager); + deposit.createAccount(Hex.decode(TRANSFER_TO), AccountType.Normal); + deposit.addBalance(Hex.decode(TRANSFER_TO), 10); + deposit.commit(); + ownerCapsule = + new AccountCapsule( + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)), + ByteString.copyFromUtf8("owner"), + AccountType.AssetIssue); + + ownerCapsule.setBalance(1000_1000_1000L); + } + + + private long createAsset(String tokenName) { + dbManager.getDynamicPropertiesStore().saveAllowSameTokenName(1); + VMConfig.initAllowTvmTransferTrc10(1); + VMConfig.initAllowTvmConstantinople(1); + VMConfig.initAllowTvmSolidity059(1); + long id = dbManager.getDynamicPropertiesStore().getTokenIdNum() + 1; + dbManager.getDynamicPropertiesStore().saveTokenIdNum(id); + AssetIssueContract assetIssueContract = + AssetIssueContract.newBuilder() + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS))) + .setName(ByteString.copyFrom(ByteArray.fromString(tokenName))) + .setId(Long.toString(id)) + .setTotalSupply(TOTAL_SUPPLY) + .setTrxNum(TRX_NUM) + .setNum(NUM) + .setStartTime(START_TIME) + .setEndTime(END_TIME) + .setVoteScore(VOTE_SCORE) + .setDescription(ByteString.copyFrom(ByteArray.fromString(DESCRIPTION))) + .setUrl(ByteString.copyFrom(ByteArray.fromString(URL))) + .build(); + AssetIssueCapsule assetIssueCapsule = new AssetIssueCapsule(assetIssueContract); + dbManager.getAssetIssueV2Store().put(assetIssueCapsule.createDbV2Key(), assetIssueCapsule); + + ownerCapsule.addAssetV2(ByteArray.fromString(String.valueOf(id)), 100_000_000); + dbManager.getAccountStore().put(ownerCapsule.getAddress().toByteArray(), ownerCapsule); + return id; + } + + + /** + * pragma solidity ^0.5.4; + * + * contract TestTransferTo { constructor() public payable{} + * + * function depositIn() public payable{} + * + * function transferTokenTo(address payable toAddress, trcToken id,uint256 amount) public payable + * { toAddress.transferToken(amount,id); } + * + * function transferTo(address payable toAddress ,uint256 amount) public payable { + * toAddress.transfer(amount); } + * + * } + */ + @Test + public void TransferTokenTest() + throws ContractExeException, ReceiptCheckErrException, VMIllegalException, ContractValidateException { + // 1. Test deploy with tokenValue and tokenId */ + long id = createAsset("testToken1"); + byte[] contractAddress = deployTransferContract(id); + deposit.commit(); + Assert.assertEquals(100, + dbManager.getAccountStore().get(contractAddress).getAssetMapV2().get(String.valueOf(id)) + .longValue()); + Assert.assertEquals(1000, dbManager.getAccountStore().get(contractAddress).getBalance()); + + String selectorStr = "transferTokenTo(address,trcToken,uint256)"; + + byte[] input = Hex.decode(AbiUtil + .parseMethod(selectorStr, + "\"" + Wallet.encode58Check(Hex.decode(TRANSFER_TO)) + "\"" + "," + id + ",9")); + + // 2. Test trigger with tokenValue and tokenId, also test internal transaction transferToken function */ + long triggerCallValue = 100; + long feeLimit = 100000000; + long tokenValue = 8; + Transaction transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + input, + triggerCallValue, feeLimit, tokenValue, id); + runtime = TvmTestUtils.processTransactionAndReturnRuntime(transaction, dbManager, null); + + Assert.assertNull(runtime.getRuntimeError()); + Assert.assertEquals(9, dbManager.getAccountStore().get(Hex.decode(TRANSFER_TO)).getAssetMapV2() + .get(String.valueOf(id)).longValue()); + Assert.assertEquals(100 + tokenValue - 9, + dbManager.getAccountStore().get(contractAddress).getAssetMapV2().get(String.valueOf(id)) + .longValue()); + long energyCostWhenExist = runtime.getResult().getEnergyUsed(); + + // 3.Test transferToken To Non-exist address + ECKey ecKey = new ECKey(Utils.getRandom()); + input = Hex.decode(AbiUtil + .parseMethod(selectorStr, + "\"" + Wallet.encode58Check(ecKey.getAddress()) + "\"" + "," + id + ",9")); + transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + input, + triggerCallValue, feeLimit, tokenValue, id); + runtime = TvmTestUtils.processTransactionAndReturnRuntime(transaction, dbManager, null); + + Assert.assertNull(runtime.getRuntimeError()); + Assert.assertEquals(100 + tokenValue * 2 - 18, + dbManager.getAccountStore().get(contractAddress).getAssetMapV2().get(String.valueOf(id)) + .longValue()); + Assert.assertEquals(9, + dbManager.getAccountStore().get(ecKey.getAddress()).getAssetMapV2() + .get(String.valueOf(id)).longValue()); + long energyCostWhenNonExist = runtime.getResult().getEnergyUsed(); + //4.Test Energy + Assert.assertEquals(energyCostWhenNonExist - energyCostWhenExist, + EnergyCost.getInstance().getNEW_ACCT_CALL()); + //5. Test transfer Trx with exsit account + + selectorStr = "transferTo(address,uint256)"; + input = Hex.decode(AbiUtil + .parseMethod(selectorStr, + "\"" + Wallet.encode58Check(Hex.decode(TRANSFER_TO)) + "\"" + ",9")); + transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + input, + triggerCallValue, feeLimit, 0, 0); + runtime = TvmTestUtils.processTransactionAndReturnRuntime(transaction, dbManager, null); + Assert.assertNull(runtime.getRuntimeError()); + Assert.assertEquals(19, dbManager.getAccountStore().get(Hex.decode(TRANSFER_TO)).getBalance()); + energyCostWhenExist = runtime.getResult().getEnergyUsed(); + + //6. Test transfer Trx with non-exsit account + selectorStr = "transferTo(address,uint256)"; + ecKey = new ECKey(Utils.getRandom()); + input = Hex.decode(AbiUtil + .parseMethod(selectorStr, + "\"" + Wallet.encode58Check(ecKey.getAddress()) + "\"" + ",9")); + transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + input, + triggerCallValue, feeLimit, 0, 0); + runtime = TvmTestUtils.processTransactionAndReturnRuntime(transaction, dbManager, null); + Assert.assertNull(runtime.getRuntimeError()); + Assert.assertEquals(9, dbManager.getAccountStore().get(ecKey.getAddress()).getBalance()); + energyCostWhenNonExist = runtime.getResult().getEnergyUsed(); + + //7.test energy + Assert.assertEquals(energyCostWhenNonExist - energyCostWhenExist, + EnergyCost.getInstance().getNEW_ACCT_CALL()); + + //8.test transfer to itself + selectorStr = "transferTo(address,uint256)"; + input = Hex.decode(AbiUtil + .parseMethod(selectorStr, + "\"" + Wallet.encode58Check(contractAddress) + "\"" + ",9")); + transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + input, + triggerCallValue, feeLimit, 0, 0); + runtime = TvmTestUtils.processTransactionAndReturnRuntime(transaction, dbManager, null); + Assert.assertTrue(runtime.getRuntimeError().contains("failed")); + + // 9.Test transferToken Big Amount + + selectorStr = "transferTokenTo(address,trcToken,uint256)"; + ecKey = new ECKey(Utils.getRandom()); + String params = "000000000000000000000000548794500882809695a8a687866e76d4271a1abc" + + Hex.toHexString(new DataWord(id).getData()) + + "0000000000000000000000000000000011111111111111111111111111111111"; + byte[] triggerData = TvmTestUtils.parseAbi(selectorStr, params); + + transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + triggerData, + triggerCallValue, feeLimit, tokenValue, id); + runtime = TvmTestUtils.processTransactionAndReturnRuntime(transaction, dbManager, null); + + Assert.assertEquals("endowment out of long range", runtime.getRuntimeError()); + + // 10.Test transferToken using static call + selectorStr = "transferTo(address,uint256)"; + ecKey = new ECKey(Utils.getRandom()); + input = Hex.decode(AbiUtil + .parseMethod(selectorStr, + "\"" + Wallet.encode58Check(ecKey.getAddress()) + "\"" + ",1")); + transaction = TvmTestUtils + .generateTriggerSmartContractAndGetTransaction(Hex.decode(OWNER_ADDRESS), contractAddress, + input, + 0, feeLimit, 0, 0); + deposit = DepositImpl.createRoot(dbManager); + RuntimeImpl runtimeImpl = new RuntimeImpl(transaction, + new BlockCapsule(Block.newBuilder().build()), deposit, + new ProgramInvokeFactoryImpl(), + true); + runtimeImpl.execute(); + runtimeImpl.go(); + + Assert.assertEquals("Attempt to call a state modifying opcode inside STATICCALL", + runtimeImpl.getRuntimeError()); + + + } + + + private byte[] deployTransferContract(long id) + throws ContractExeException, ReceiptCheckErrException, ContractValidateException, VMIllegalException { + String contractName = "TestTransferTo"; + byte[] address = Hex.decode(OWNER_ADDRESS); + String ABI = + "[]"; + String code = "60806040526101cf806100136000396000f3fe608060405260043610610050577c010000000000000000000000000000000000000000000000000000000060003504632ccb1b3081146100555780634cd2270c14610090578063d4d6422614610098575b600080fd5b61008e6004803603604081101561006b57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356100d7565b005b61008e61011f565b61008e600480360360608110156100ae57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060208101359060400135610121565b60405173ffffffffffffffffffffffffffffffffffffffff83169082156108fc029083906000818181858888f1935050505015801561011a573d6000803e3d6000fd5b505050565b565b73ffffffffffffffffffffffffffffffffffffffff831681156108fc0282848015801561014d57600080fd5b50806780000000000000001115801561016557600080fd5b5080620f42401015801561017857600080fd5b50604051600081818185878a8ad094505050505015801561019d573d6000803e3d6000fd5b5050505056fea165627a7a723058202eab0934f57baf17ec1ddb6649b416e35d7cb846482d1232ca229258e83d22af0029"; + + long value = 1000; + long feeLimit = 100000000; + long consumeUserResourcePercent = 0; + long tokenValue = 100; + long tokenId = id; + + byte[] contractAddress = TvmTestUtils + .deployContractWholeProcessReturnContractAddress(contractName, address, ABI, code, value, + feeLimit, consumeUserResourcePercent, null, tokenValue, tokenId, + deposit, null); + return contractAddress; + } + + + /** + * Release resources. + */ + @AfterClass + public static void destroy() { + Args.clearParam(); + appT.shutdownServices(); + appT.shutdown(); + context.destroy(); + if (FileUtil.deleteDir(new File(dbPath))) { + logger.info("Release resources successful."); + } else { + logger.info("Release resources failure."); + } + } +}