From fd9852cc311903c14de43218fa65868d3b0bdb87 Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 13 Mar 2019 16:43:31 +0800 Subject: [PATCH 01/37] Add opcode: SHL,SHR,SAR --- .../org/tron/common/runtime/vm/DataWord.java | 148 +++++++++++++++--- .../org/tron/common/runtime/vm/OpCode.java | 12 ++ .../java/org/tron/common/runtime/vm/VM.java | 36 +++++ 3 files changed, 173 insertions(+), 23 deletions(-) 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 5c555c34516..1338cc8dede 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -17,6 +17,8 @@ */ package org.tron.common.runtime.vm; +import static org.tron.common.utils.ByteUtil.numberOfLeadingZeros; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import org.spongycastle.util.Arrays; @@ -40,10 +42,12 @@ public class DataWord implements Comparable { /* Maximum value of the DataWord */ public static final int DATAWORD_UNIT_SIZE = 32; + public static final int MAX_POW = 256; public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack public static final DataWord ZERO_EMPTY_ARRAY = new DataWord(new byte[0]); // don't push it in to the stack + public static final DataWord ONE = DataWord.of((byte) 1); private byte[] data = new byte[32]; @@ -65,6 +69,36 @@ private DataWord(ByteBuffer buffer) { this.data = targetByteBuffer.array(); } + public static DataWord of(byte[] data) { + if (data == null || data.length == 0) { + return DataWord.ZERO; + } + + int leadingZeroBits = numberOfLeadingZeros(data); + int valueBits = 8 * data.length - leadingZeroBits; + if (valueBits <= 8) { + if (data[data.length - 1] == 0) return DataWord.ZERO; + if (data[data.length - 1] == 1) return DataWord.ONE; + } + + if (data.length == 32) + return new DataWord(java.util.Arrays.copyOf(data, data.length)); + else if (data.length <= 32) { + byte[] bytes = new byte[32]; + System.arraycopy(data, 0, bytes, 32 - data.length, data.length); + return new DataWord(bytes); + } else { + throw new RuntimeException(String.format("Data word can't exceed 32 bytes: 0x%s", ByteUtil.toHexString(data))); + } + } + + public static DataWord of(byte num) { + byte[] bb = new byte[32]; + bb[31] = num; + return new DataWord(bb); + + } + @JsonCreator public DataWord(String data) { this(Hex.decode(data)); @@ -234,38 +268,60 @@ public DataWord xor(DataWord w2) { return this; } - public void negate() { - - if (this.isZero()) return; - - for (int i = 0; i < this.data.length; ++i) { - this.data[i] = (byte) ~this.data[i]; - } - - for (int i = this.data.length - 1; i >= 0; --i) { - this.data[i] = (byte) (1 + this.data[i] & 0xFF); - if (this.data[i] != 0) break; - } - } - - public void bnot() { + public DataWord bnot() { if (this.isZero()) { - this.data = ByteUtil.copyToArray(MAX_VALUE); - return; + return new DataWord(ByteUtil.copyToArray(MAX_VALUE)); } - this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); - } + return new DataWord(ByteUtil.copyToArray(MAX_VALUE.subtract(this.value()))); + } + + public DataWord negate() { + if (this.isZero()) return ZERO; + return bnot().add(DataWord.ONE); + } + +// public void negate() { +// +// if (this.isZero()) return; +// +// for (int i = 0; i < this.data.length; ++i) { +// this.data[i] = (byte) ~this.data[i]; +// } +// +// for (int i = this.data.length - 1; i >= 0; --i) { +// this.data[i] = (byte) (1 + this.data[i] & 0xFF); +// if (this.data[i] != 0) break; +// } +// } + +// public void bnot() { +// if (this.isZero()) { +// this.data = ByteUtil.copyToArray(MAX_VALUE); +// return; +// } +// this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); +// } // By : Holger // From : http://stackoverflow.com/a/24023466/459349 - public void add(DataWord word) { - byte[] result = new byte[32]; +// public void add(DataWord word) { +// byte[] result = new byte[32]; +// for (int i = 31, overflow = 0; i >= 0; i--) { +// int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; +// result[i] = (byte) v; +// overflow = v >>> 8; +// } +// this.data = result; +// } + + public DataWord add(DataWord word) { + byte[] newData = new byte[32]; for (int i = 31, overflow = 0; i >= 0; i--) { int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; - result[i] = (byte) v; + newData[i] = (byte) v; overflow = v >>> 8; } - this.data = result; + return new DataWord(newData); } // old add-method with BigInteger quick hack @@ -450,4 +506,50 @@ public String asString(){ public String toHexString() { return Hex.toHexString(data); } + + /** + * Shift left, both this and input arg are treated as unsigned + * @param arg + * @return this << arg + */ + public DataWord shiftLeft(DataWord arg) { + if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { + return DataWord.ZERO; + } + + BigInteger result = value().shiftLeft(arg.intValueSafe()); + return new DataWord(ByteUtil.copyToArray(result.and(MAX_VALUE))); + } + + /** + * Shift right, both this and input arg are treated as unsigned + * @param arg + * @return this >> arg + */ + public DataWord shiftRight(DataWord arg) { + if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { + return DataWord.ZERO; + } + + BigInteger result = value().shiftRight(arg.intValueSafe()); + return new DataWord(ByteUtil.copyToArray(result.and(MAX_VALUE))); + } + + /** + * Shift right, this is signed, while input arg is treated as unsigned + * @param arg + * @return this >> arg + */ + public DataWord shiftRightSigned(DataWord arg) { + if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { + if (this.isNegative()) { + return DataWord.ONE.negate(); + } else { + return DataWord.ZERO; + } + } + + BigInteger result = sValue().shiftRight(arg.intValueSafe()); + return new DataWord(ByteUtil.copyToArray(result.and(MAX_VALUE))); + } } diff --git a/src/main/java/org/tron/common/runtime/vm/OpCode.java b/src/main/java/org/tron/common/runtime/vm/OpCode.java index a7aede3f4b2..bcc9b8c9615 100644 --- a/src/main/java/org/tron/common/runtime/vm/OpCode.java +++ b/src/main/java/org/tron/common/runtime/vm/OpCode.java @@ -130,6 +130,18 @@ public enum OpCode { * (0x1a) Retrieve single byte from word */ BYTE(0x1a, 2, 1, OpCode.Tier.VeryLowTier), + /** + * (0x1b) Shift left + */ + SHL(0x1b, 2, 1, OpCode.Tier.VeryLowTier), + /** + * (0x1c) Logical shift right + */ + SHR(0x1c, 2, 1, OpCode.Tier.VeryLowTier), + /** + * (0x1d) Arithmetic shift right + */ + SAR(0x1d, 2, 1, OpCode.Tier.VeryLowTier), /* Cryptographic Operations */ 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 3f10cf4d3f5..a7b473b3713 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -592,6 +592,42 @@ public void step(Program program) { program.step(); } break; + case SHL: { + DataWord word1 = program.stackPop(); + DataWord word2 = program.stackPop(); + final DataWord result = word2.shiftLeft(word1); + + if (logger.isInfoEnabled()) + hint = "" + result.value(); + + program.stackPush(result); + program.step(); + } + break; + case SHR: { + DataWord word1 = program.stackPop(); + DataWord word2 = program.stackPop(); + final DataWord result = word2.shiftRight(word1); + + if (logger.isInfoEnabled()) + hint = "" + result.value(); + + program.stackPush(result); + program.step(); + } + break; + case SAR: { + DataWord word1 = program.stackPop(); + DataWord word2 = program.stackPop(); + final DataWord result = word2.shiftRightSigned(word1); + + if (logger.isInfoEnabled()) + hint = "" + result.value(); + + program.stackPush(result); + program.step(); + } + break; case ADDMOD: { DataWord word1 = program.stackPop(); DataWord word2 = program.stackPop(); From 5ec51d5f1c3e0f821140b6263fe11b0f05b87b01 Mon Sep 17 00:00:00 2001 From: ashu Date: Mon, 11 Mar 2019 16:01:14 +0800 Subject: [PATCH 02/37] Add `CREATE2` --- .../org/tron/common/runtime/vm/OpCode.java | 2 ++ .../java/org/tron/common/runtime/vm/VM.java | 19 +++++++++++ .../org/tron/common/runtime/vm/VMUtils.java | 7 ++++ .../common/runtime/vm/program/Program.java | 33 ++++++++++++------- src/main/java/org/tron/core/Wallet.java | 8 +++++ 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/tron/common/runtime/vm/OpCode.java b/src/main/java/org/tron/common/runtime/vm/OpCode.java index bcc9b8c9615..416b8641eaf 100644 --- a/src/main/java/org/tron/common/runtime/vm/OpCode.java +++ b/src/main/java/org/tron/common/runtime/vm/OpCode.java @@ -624,6 +624,8 @@ public enum OpCode { */ STATICCALL(0xfa, 6, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.Static), + CREATE2(0xf5, 4, 1, OpCode.Tier.SpecialTier), + /** * (0xfd) The `REVERT` instruction will stop execution, roll back all state changes done so far * and provide a pointer to a memory section, which can be interpreted as an error code or message. 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 a7b473b3713..71fe461b06c 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -239,6 +239,13 @@ public void step(Program program) { energyCost = energyCosts.getCREATE() + calcMemEnergy(energyCosts, oldMemSize, memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)), 0, op); break; + case CREATE2: + DataWord codeSize = stack.get(stack.size() - 3); + energyCost = energyCosts.getCREATE() + + calcMemEnergy(energyCosts, oldMemSize, + memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)), 0, op) + + VMUtils.getSizeInWords(codeSize.longValueSafe()) * energyCosts.getSHA3_WORD(); + break; case LOG0: case LOG1: case LOG2: @@ -1247,6 +1254,18 @@ public void step(Program program) { program.step(); } break; + case CREATE2: { + if (program.isStaticCall()) { + throw new Program.StaticCallModificationException(); + } + DataWord value = program.stackPop(); + DataWord inOffset = program.stackPop(); + DataWord inSize = program.stackPop(); + DataWord salt = program.stackPop(); + program.createContract2(value, inOffset, inSize, salt); + program.step(); + } + break; case TOKENBALANCE: { DataWord tokenId = program.stackPop(); DataWord address = program.stackPop(); diff --git a/src/main/java/org/tron/common/runtime/vm/VMUtils.java b/src/main/java/org/tron/common/runtime/vm/VMUtils.java index 67424f8b799..bed883315cd 100644 --- a/src/main/java/org/tron/common/runtime/vm/VMUtils.java +++ b/src/main/java/org/tron/common/runtime/vm/VMUtils.java @@ -156,4 +156,11 @@ public static String unzipAndDecode(String content) { return content; } } + + /** + * Returns number of VM words required to hold data of size {@code size} + */ + public static long getSizeInWords(long size) { + return size == 0 ? 0 : (size - 1) / 32 + 1; + } } 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 4a5e5260a41..6bd14fc90c1 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 @@ -443,25 +443,28 @@ public void createContract(DataWord value, DataWord memStart, DataWord memSize) stackPushZero(); return; } + // [1] FETCH THE CODE FROM THE MEMORY + byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue()); - byte[] senderAddress = convertToTronAddress(this.getContractAddress().getLast20Bytes()); + byte[] newAddress = Wallet + .generateContractAddress(rootTransactionId, nonce); - long endowment = value.value().longValueExact(); - if (getContractState().getBalance(senderAddress) < endowment) { - stackPushZero(); - return; - } + createContractImpl(value, programCode, newAddress); + } - // [1] FETCH THE CODE FROM THE MEMORY - byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue()); + private void createContractImpl(DataWord value, byte[] programCode, byte[] newAddress) { + byte[] senderAddress = convertToTronAddress(this.getContractAddress().getLast20Bytes()); if (logger.isDebugEnabled()) { logger.debug("creating a new contract inside contract run: [{}]", Hex.toHexString(senderAddress)); } - byte[] newAddress = Wallet - .generateContractAddress(rootTransactionId, nonce); + long endowment = value.value().longValueExact(); + if (getContractState().getBalance(senderAddress) < endowment) { + stackPushZero(); + return; + } AccountCapsule existingAddr = getContractState().getAccount(newAddress); boolean contractAlreadyExists = existingAddr != null; @@ -535,7 +538,7 @@ this, new DataWord(newAddress), getContractAddress(), value, new DataWord(0), if (!createResult.isRevert()) { if (afterSpend < 0) { createResult.setException( - Program.Exception.notEnoughSpendEnergy("No energy to save just created contract code", + Exception.notEnoughSpendEnergy("No energy to save just created contract code", saveCodeEnergy, programInvoke.getEnergyLimit() - createResult.getEnergyUsed())); } else { createResult.spendEnergy(saveCodeEnergy); @@ -1168,6 +1171,14 @@ public static String stringifyMultiline(byte[] code) { return sb.toString(); } + public void createContract2(DataWord value, DataWord memStart, DataWord memSize, DataWord salt) { + byte[] senderAddress = convertToTronAddress(this.getContractAddress().getLast20Bytes()); + byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue()); + + byte[] contractAddress = Wallet.generateContractAddress2(senderAddress, programCode, salt.getData()); + createContractImpl(value, programCode, contractAddress); + } + static class ByteCodeIterator { byte[] code; diff --git a/src/main/java/org/tron/core/Wallet.java b/src/main/java/org/tron/core/Wallet.java index 8cc80f6473e..bf784efca14 100755 --- a/src/main/java/org/tron/core/Wallet.java +++ b/src/main/java/org/tron/core/Wallet.java @@ -75,6 +75,7 @@ import org.tron.common.storage.DepositImpl; import org.tron.common.utils.Base58; import org.tron.common.utils.ByteArray; +import org.tron.common.utils.ByteUtil; import org.tron.common.utils.Sha256Hash; import org.tron.common.utils.Utils; import org.tron.core.actuator.Actuator; @@ -279,6 +280,13 @@ public static byte[] generateContractAddress(byte[] ownerAddress, byte[] txRawDa } + // for `CREATE2` + public static byte[] generateContractAddress2(byte[] address, byte[] code, byte[] salt) { + byte[] mergedData = ByteUtil.merge(address, code, salt); + return Hash.sha3omit12(mergedData); + } + + // for `CREATE` public static byte[] generateContractAddress(byte[] transactionRootId, long nonce) { byte[] nonceBytes = Longs.toByteArray(nonce); byte[] combined = new byte[transactionRootId.length + nonceBytes.length]; From 15b8649e2c874d623f35e50c9ffed43eac678b6c Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 13 Mar 2019 16:03:04 +0800 Subject: [PATCH 03/37] Create contract save returnH value --- src/main/java/org/tron/common/runtime/RuntimeImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/tron/common/runtime/RuntimeImpl.java b/src/main/java/org/tron/common/runtime/RuntimeImpl.java index 7c01825c223..742b6394a76 100644 --- a/src/main/java/org/tron/common/runtime/RuntimeImpl.java +++ b/src/main/java/org/tron/common/runtime/RuntimeImpl.java @@ -21,6 +21,7 @@ import org.tron.common.logsfilter.EventPluginLoader; import org.tron.common.logsfilter.trigger.ContractTrigger; import org.tron.common.runtime.config.VMConfig; +import org.tron.common.runtime.utils.MUtil; import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.EnergyCost; import org.tron.common.runtime.vm.LogInfoTriggerParser; @@ -465,7 +466,8 @@ && isCheckTransaction()) { deposit.createContract(contractAddress, new ContractCapsule(newSmartContract)); byte[] code = newSmartContract.getBytecode().toByteArray(); - deposit.saveCode(contractAddress, ProgramPrecompile.getCode(code)); +// byte[] precompiledCode = ProgramPrecompile.getCode(code); +// deposit.saveCode(contractAddress, precompiledCode); // transfer from callerAddress to contractAddress according to callValue if (callValue > 0) { @@ -530,6 +532,8 @@ private void call() checkTokenValueAndId(tokenValue, tokenId); byte[] code = this.deposit.getCode(contractAddress); + logger.info("ysc " + " contract:" + Wallet.encode58Check(contractAddress)); + logger.info("ysc " + " code:" + Hex.toHexString(code)); if (isNotEmpty(code)) { long feeLimit = trx.getRawData().getFeeLimit(); @@ -634,6 +638,7 @@ public void go() { } } else { result.spendEnergy(saveCodeEnergy); + deposit.saveCode(program.getContractAddress().getNoLeadZeroesData(), code); } } From 5164c05a2b2d5cadc6535c8f7abea697f11cef4f Mon Sep 17 00:00:00 2001 From: ashu Date: Fri, 15 Mar 2019 17:38:49 +0800 Subject: [PATCH 04/37] Add dataword negate method --- .../org/tron/common/runtime/vm/DataWord.java | 18 ++++++++++++++++-- .../java/org/tron/common/runtime/vm/VM.java | 10 +++++----- .../java/org/tron/core/config/args/Args.java | 6 +++--- src/main/resources/config-localtest.conf | 5 +++-- 4 files changed, 27 insertions(+), 12 deletions(-) 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 1338cc8dede..1410c085689 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -275,11 +275,25 @@ public DataWord bnot() { return new DataWord(ByteUtil.copyToArray(MAX_VALUE.subtract(this.value()))); } + private byte[] copyData() { + return java.util.Arrays.copyOf(data, data.length); + } + public DataWord negate() { + if (this.isZero()) return ZERO; - return bnot().add(DataWord.ONE); - } + byte[] newData = this.copyData(); + for (int i = 0; i < this.data.length; ++i) { + newData[i] = (byte) ~this.data[i]; + } + + for (int i = this.data.length - 1; i >= 0; --i) { + newData[i] = (byte) (1 + this.data[i] & 0xFF); + if (newData[i] != 0) break; + } + return new DataWord(newData); + } // public void negate() { // // if (this.isZero()) return; 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 71fe461b06c..721b427de28 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -85,11 +85,11 @@ public void step(Program program) { } // hard fork for 3.2 - if (!VMConfig.allowTvmTransferTrc10()) { - if (op == CALLTOKEN || op == TOKENBALANCE || op == CALLTOKENVALUE || op == CALLTOKENID) { - throw Program.Exception.invalidOpCode(program.getCurrentOp()); - } - } +// if (!VMConfig.allowTvmTransferTrc10()) { +// if (op == CALLTOKEN || op == TOKENBALANCE || op == CALLTOKENVALUE || op == CALLTOKENID) { +// throw Program.Exception.invalidOpCode(program.getCurrentOp()); +// } +// } program.setLastOp(op.val()); program.verifyStackSize(op.require()); diff --git a/src/main/java/org/tron/core/config/args/Args.java b/src/main/java/org/tron/core/config/args/Args.java index b51cfeef360..927e3342c50 100644 --- a/src/main/java/org/tron/core/config/args/Args.java +++ b/src/main/java/org/tron/core/config/args/Args.java @@ -864,9 +864,9 @@ public static void setParam(final String[] args, final String confFileName) { INSTANCE.blockNumForEneryLimit = config.hasPath("enery.limit.block.num") ? config.getInt("enery.limit.block.num") : 4727890L; - INSTANCE.vmTrace = - config.hasPath("vm.vmTrace") ? config - .getBoolean("vm.vmTrace") : false; + INSTANCE.vmTrace = true; +// config.hasPath("vm.vmTrace") ? config +// .getBoolean("vm.vmTrace") : false; INSTANCE.saveInternalTx = config.hasPath("vm.saveInternalTx") && config.getBoolean("vm.saveInternalTx"); diff --git a/src/main/resources/config-localtest.conf b/src/main/resources/config-localtest.conf index d28c70493a2..11af4c63397 100644 --- a/src/main/resources/config-localtest.conf +++ b/src/main/resources/config-localtest.conf @@ -94,7 +94,7 @@ node { maxActiveNodesWithSameIp = 10 - minParticipationRate = 15 + minParticipationRate = 0 # check the peer data transfer ,disconnect factor disconnectNumberFactor = 0.4 @@ -265,9 +265,10 @@ block = { vm = { - supportConstant = false + supportConstant = true minTimeRatio = 0.0 maxTimeRatio = 5.0 + vm.vmTrace = true } committee = { From cefa536221496b8221c53c40ba2d7cbc3cd7bc05 Mon Sep 17 00:00:00 2001 From: ashu Date: Mon, 18 Mar 2019 21:23:42 +0800 Subject: [PATCH 05/37] Fix DataWord bug --- .../org/tron/common/runtime/vm/DataWord.java | 82 ++++++------------- 1 file changed, 24 insertions(+), 58 deletions(-) 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 1410c085689..8da12d31946 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -268,74 +268,38 @@ public DataWord xor(DataWord w2) { return this; } - public DataWord bnot() { - if (this.isZero()) { - return new DataWord(ByteUtil.copyToArray(MAX_VALUE)); - } - return new DataWord(ByteUtil.copyToArray(MAX_VALUE.subtract(this.value()))); - } + public void negate() { - private byte[] copyData() { - return java.util.Arrays.copyOf(data, data.length); - } + if (this.isZero()) return; - public DataWord negate() { - - if (this.isZero()) return ZERO; - - byte[] newData = this.copyData(); for (int i = 0; i < this.data.length; ++i) { - newData[i] = (byte) ~this.data[i]; + this.data[i] = (byte) ~this.data[i]; } for (int i = this.data.length - 1; i >= 0; --i) { - newData[i] = (byte) (1 + this.data[i] & 0xFF); - if (newData[i] != 0) break; + this.data[i] = (byte) (1 + this.data[i] & 0xFF); + if (this.data[i] != 0) break; } - return new DataWord(newData); - } -// public void negate() { -// -// if (this.isZero()) return; -// -// for (int i = 0; i < this.data.length; ++i) { -// this.data[i] = (byte) ~this.data[i]; -// } -// -// for (int i = this.data.length - 1; i >= 0; --i) { -// this.data[i] = (byte) (1 + this.data[i] & 0xFF); -// if (this.data[i] != 0) break; -// } -// } - -// public void bnot() { -// if (this.isZero()) { -// this.data = ByteUtil.copyToArray(MAX_VALUE); -// return; -// } -// this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); -// } + } + + public void bnot() { + if (this.isZero()) { + this.data = ByteUtil.copyToArray(MAX_VALUE); + return; + } + this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); + } // By : Holger // From : http://stackoverflow.com/a/24023466/459349 -// public void add(DataWord word) { -// byte[] result = new byte[32]; -// for (int i = 31, overflow = 0; i >= 0; i--) { -// int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; -// result[i] = (byte) v; -// overflow = v >>> 8; -// } -// this.data = result; -// } - - public DataWord add(DataWord word) { - byte[] newData = new byte[32]; + public void add(DataWord word) { + byte[] result = new byte[32]; for (int i = 31, overflow = 0; i >= 0; i--) { int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; - newData[i] = (byte) v; + result[i] = (byte) v; overflow = v >>> 8; } - return new DataWord(newData); + this.data = result; } // old add-method with BigInteger quick hack @@ -488,8 +452,8 @@ public int hashCode() { public int compareTo(DataWord o) { if (o == null || o.getData() == null) return -1; int result = FastByteComparisons.compareTo( - data, 0, data.length, - o.getData(), 0, o.getData().length); + data, 0, data.length, + o.getData(), 0, o.getData().length); // Convert result into -1, 0 or 1 as is the convention return (int) Math.signum(result); } @@ -518,7 +482,7 @@ public String asString(){ } public String toHexString() { - return Hex.toHexString(data); + return Hex.toHexString(data); } /** @@ -557,7 +521,9 @@ public DataWord shiftRight(DataWord arg) { public DataWord shiftRightSigned(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { if (this.isNegative()) { - return DataWord.ONE.negate(); + DataWord result = DataWord.ONE; + result.negate(); + return result; } else { return DataWord.ZERO; } From 078af92c778942e9911173354031cdf7591075d9 Mon Sep 17 00:00:00 2001 From: ashu Date: Tue, 19 Mar 2019 18:18:28 +0800 Subject: [PATCH 06/37] Fix sender --- .../java/org/tron/common/runtime/vm/program/Program.java | 2 +- src/main/java/org/tron/core/Wallet.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) 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 6bd14fc90c1..82d25343949 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 @@ -1172,7 +1172,7 @@ public static String stringifyMultiline(byte[] code) { } public void createContract2(DataWord value, DataWord memStart, DataWord memSize, DataWord salt) { - byte[] senderAddress = convertToTronAddress(this.getContractAddress().getLast20Bytes()); + byte[] senderAddress = convertToTronAddress(this.getCallerAddress().getLast20Bytes()); byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue()); byte[] contractAddress = Wallet.generateContractAddress2(senderAddress, programCode, salt.getData()); diff --git a/src/main/java/org/tron/core/Wallet.java b/src/main/java/org/tron/core/Wallet.java index bf784efca14..f7f36522c5c 100755 --- a/src/main/java/org/tron/core/Wallet.java +++ b/src/main/java/org/tron/core/Wallet.java @@ -39,6 +39,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.spongycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -70,6 +71,7 @@ import org.tron.common.runtime.Runtime; import org.tron.common.runtime.RuntimeImpl; import org.tron.common.runtime.config.VMConfig; +import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.program.ProgramResult; import org.tron.common.runtime.vm.program.invoke.ProgramInvokeFactoryImpl; import org.tron.common.storage.DepositImpl; @@ -286,6 +288,11 @@ public static byte[] generateContractAddress2(byte[] address, byte[] code, byte[ return Hash.sha3omit12(mergedData); } + // for test + public static byte[] generateContractAddress2(byte[] address, byte[] code, long salt) { + return generateContractAddress2(address, code, new DataWord(salt).getData()); + } + // for `CREATE` public static byte[] generateContractAddress(byte[] transactionRootId, long nonce) { byte[] nonceBytes = Longs.toByteArray(nonce); From a78dfc5bddf289932e99799df1eff003183a2156 Mon Sep 17 00:00:00 2001 From: ashu Date: Tue, 19 Mar 2019 19:25:56 +0800 Subject: [PATCH 07/37] Add bytes coder --- .../wallet/common/client/utils/AbiUtil.java | 158 ++++++++++-------- 1 file changed, 84 insertions(+), 74 deletions(-) diff --git a/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java b/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java index 6c77fa00c9d..041363f71ee 100644 --- a/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java +++ b/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java @@ -6,8 +6,10 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; import org.spongycastle.util.encoders.Hex; import org.tron.common.crypto.Hash; +import org.tron.common.utils.ByteUtil; import org.tron.core.Wallet; public class AbiUtil { @@ -16,21 +18,16 @@ public class AbiUtil { static Pattern paramTypeNumber = Pattern.compile("^(u?int)([0-9]*)$"); static Pattern paramTypeArray = Pattern.compile("^(.*)\\[([0-9]*)\\]$"); // - - abstract static class Coder { + static abstract class Coder { boolean dynamic = false; String name; String type; // DataWord[] encode abstract byte[] encode(String value); - abstract byte[] decode(); } - /** - * constructor. - */ public static String[] getTypes(String methodSign) { int start = methodSign.indexOf('(') + 1; @@ -44,9 +41,6 @@ public static String[] getTypes(String methodSign) { public static String geMethodId(String methodSign) { return null; } - /** - * constructor. - */ public static Coder getParamCoder(String type) { @@ -63,13 +57,11 @@ public static Coder getParamCoder(String type) { return new CoderNumber(); } - if (paramTypeBytes.matcher(type).find()) { + if (paramTypeBytes.matcher(type).find()) return new CoderFixedBytes(); - } - if (paramTypeNumber.matcher(type).find()) { + if (paramTypeNumber.matcher(type).find()) return new CoderNumber(); - } Matcher m = paramTypeArray.matcher(type); if (m.find()) { @@ -86,7 +78,6 @@ public static Coder getParamCoder(String type) { static class CoderArray extends Coder { private String elementType; private int length; - CoderArray(String arrayType, int length) { this.elementType = arrayType; this.length = length; @@ -101,7 +92,7 @@ byte[] encode(String arrayValues) { Coder coder = getParamCoder(elementType); - List strings = null; + List strings; try { ObjectMapper mapper = new ObjectMapper(); strings = mapper.readValue(arrayValues, List.class); @@ -123,7 +114,7 @@ byte[] encode(String arrayValues) { } if (this.length == -1) { - return concat(new DataWord(strings.size()).getData(), pack(coders, strings)); + return ByteUtil.merge(new DataWord(strings.size()).getData(), pack(coders, strings)); } else { return pack(coders, strings); } @@ -200,7 +191,7 @@ static class CoderDynamicBytes extends Coder { @Override byte[] encode(String value) { - return encodeDynamicBytes(value); + return encodeDynamicBytes(value, true); } @Override @@ -232,6 +223,9 @@ static class CoderAddress extends Coder { @Override byte[] encode(String value) { byte[] address = Wallet.decodeFromBase58Check(value); + if (address == null) { + return null; + } return new DataWord(address).getData(); } @@ -256,18 +250,27 @@ byte[] decode() { return new byte[0]; } } - /** - * constructor. - */ - public static byte[] encodeDynamicBytes(String value) { - byte[] data = value.getBytes(); + public static byte[] encodeDynamicBytes(String value, boolean hex) { + byte[] data; + if (hex) { + if (value.startsWith("0x")) { + value = value.substring(2); + } + data = Hex.decode(value); + } else { + data = value.getBytes(); + } + return encodeDynamicBytes(data); + } + + public static byte[] encodeDynamicBytes(byte[] data) { List ret = new ArrayList<>(); ret.add(new DataWord(data.length)); int readInx = 0; - int len = value.getBytes().length; - while (readInx < value.getBytes().length) { + int len = data.length; + while (readInx < data.length) { byte[] wordData = new byte[32]; int readLen = len - readInx >= 32 ? 32 : (len - readInx); System.arraycopy(data, readInx, wordData, 0, readLen); @@ -286,10 +289,13 @@ public static byte[] encodeDynamicBytes(String value) { return retBytes; } - /** - * constructor. - */ + public static byte[] encodeDynamicBytes(String value) { + byte[] data = value.getBytes(); + List ret = new ArrayList<>(); + ret.add(new DataWord(data.length)); + return encodeDynamicBytes(data); + } public static byte[] pack(List codes, List values) { int staticSize = 0; @@ -299,10 +305,21 @@ public static byte[] pack(List codes, List values) { for (int idx = 0;idx < codes.size(); idx++) { Coder coder = codes.get(idx); - String value = values.get(idx).toString(); - + Object parameter = values.get(idx); + String value; + if (parameter instanceof List) { + StringBuilder sb = new StringBuilder(); + for (Object item: (List) parameter) { + if (sb.length() != 0) { + sb.append(","); + } + sb.append("\"").append(item).append("\""); + } + value = "[" + sb.toString() + "]"; + } else { + value = parameter.toString(); + } byte[] encoded = coder.encode(value); - encodedList.add(encoded); if (coder.dynamic) { @@ -325,7 +342,7 @@ public static byte[] pack(List codes, List values) { System.arraycopy(new DataWord(dynamicOffset).getData(), 0,data, offset, 32); offset += 32; - System.arraycopy(encodedList.get(idx), 0,data, dynamicOffset, encodedList.get(idx).length); + System.arraycopy(encodedList.get(idx), 0,data, dynamicOffset, encodedList.get(idx).length ); dynamicOffset += encodedList.get(idx).length; } else { System.arraycopy(encodedList.get(idx), 0,data, offset, encodedList.get(idx).length); @@ -335,16 +352,10 @@ public static byte[] pack(List codes, List values) { return data; } - /** - * constructor. - */ public static String parseMethod(String methodSign, String params) { return parseMethod(methodSign, params, false); } - /** - * constructor. - */ public static String parseMethod(String methodSign, String input, boolean isHex) { byte[] selector = new byte[4]; @@ -360,18 +371,16 @@ public static String parseMethod(String methodSign, String input, boolean isHex) return Hex.toHexString(selector) + Hex.toHexString(encodedParms); } - /** - * constructor. - */ public static byte[] encodeInput(String methodSign, String input) { ObjectMapper mapper = new ObjectMapper(); input = "[" + input + "]"; - List items = null; + List items; try { items = mapper.readValue(input, List.class); } catch (IOException e) { e.printStackTrace(); + return null; } List coders = new ArrayList<>(); @@ -382,12 +391,33 @@ public static byte[] encodeInput(String methodSign, String input) { return pack(coders, items); } - /** - * constructor. - */ + + public static String parseMethod(String methodSign, List parameters) { + String[] inputArr = new String[parameters.size()]; + int i = 0; + for (Object parameter: parameters) { + if (parameter instanceof List) { + StringBuilder sb = new StringBuilder(); + for (Object item: (List) parameter) { + if (sb.length() != 0) { + sb.append(","); + } + sb.append("\"").append(item).append("\""); + } + inputArr[i++] = "[" + sb.toString() + "]"; + } else { + inputArr[i++] = (parameter instanceof String) ? ("\"" + parameter + "\"") : ("" + parameter); + } + } + return parseMethod(methodSign, StringUtils.join(inputArr, ',')); + } + + public static byte[] getTronAddress(DataWord address) { + return ByteUtil.merge(new byte[] {41}, address.getLast20Bytes()); + } public static void main(String[] args) { - // String method = "test(address,string,int)"; +// String method = "test(address,string,int)"; String method = "test(string,int2,string)"; String params = "asdf,3123,adf"; @@ -402,19 +432,9 @@ public static void main(String[] args) { String method1 = "test(uint256,string,string,uint256[])"; - String expected1 = "db103cf3000000000000000000000000000000000000000000000000000000000000000500" - + "0000000000000000000000000000000000000000000000000000000000008000000000000000000000000000" - + "000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000" - + "0000000000010000000000000000000000000000000000000000000000000000000000000000014200000000" - + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - + "0000000000000000000000000000014300000000000000000000000000000000000000000000000000000000" - + "0000000000000000000000000000000000000000000000000000000000000000000003000000000000000000" - + "0000000000000000000000000000000000000000000001000000000000000000000000000000000000000000" - + "00000000000000000000020000000000000000000000000000000000000000000000000000000000000003"; + String expected1 = "db103cf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000014200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"; String method2 = "test(uint256,string,string,uint256[3])"; - String expected2 = "000000000000000000000000000000000000000000000000000000000000000100000000000" - + "0000000000000000000000000000000000000000000000000000200000000000000000000000000000000000" - + "00000000000000000000000000003"; + String expected2 = "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"; String listString = "1 ,\"B\",\"C\", [1, 2, 3]"; System.out.println(parseMethod(method1, listString)); System.out.println(parseMethod(method2, listString)); @@ -423,25 +443,15 @@ public static void main(String[] args) { String bytesValue2 = "123123123"; System.out.println(parseMethod(byteMethod1, bytesValue1)); +// System.out.println(parseMethod(byteMethod1, bytesValue2)); +// String method3 = "voteForSingleWitness(address,uint256)"; +// String method3 = "voteForSingleWitness(address)"; +// String params3 = "\"TNNqZuYhMfQvooC4kJwTsMJEQVU3vWGa5u\""; +// +// System.out.println(parseMethod(method3, params3)); } - /** - * constructor. - */ - - public static byte[] concat(byte[]... bytesArray) { - int length = 0; - for (byte[] bytes: bytesArray) { - length += bytes.length; - } - byte[] ret = new byte[length]; - int index = 0; - for (byte[] bytes: bytesArray) { - System.arraycopy(bytes, 0, ret, index, bytes.length); - index += bytes.length; - } - return ret; - } + From bde8009f77a9f6b39db3325bf89f4f6a2fc49794 Mon Sep 17 00:00:00 2001 From: ashu Date: Tue, 19 Mar 2019 19:26:51 +0800 Subject: [PATCH 08/37] Add create2 opcode unit test --- .../tron/common/runtime/vm/Create2Test.java | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/test/java/org/tron/common/runtime/vm/Create2Test.java diff --git a/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/src/test/java/org/tron/common/runtime/vm/Create2Test.java new file mode 100644 index 00000000000..b9bb5df34d3 --- /dev/null +++ b/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -0,0 +1,117 @@ +package org.tron.common.runtime.vm; + +import java.util.Arrays; +import java.util.Collections; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; +import org.testng.Assert; +import org.tron.common.runtime.TVMTestResult; +import org.tron.common.runtime.TVMTestUtils; +import org.tron.common.runtime.utils.MUtil; +import org.tron.common.utils.ByteUtil; +import org.tron.core.Wallet; +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.Transaction; +import stest.tron.wallet.common.client.utils.AbiUtil; + +@Slf4j +public class Create2Test extends VMTestBase { +/* +pragma solidity 0.5.0; +contract Factory { + event Deployed(address addr, uint256 salt); + function deploy(bytes memory code, uint256 salt) public returns(address){ + address addr; + assembly { + addr := create2(0, add(code, 0x20), mload(code), salt) + if iszero(extcodesize(addr)) { + revert(0, 0) + } + } + emit Deployed(addr, salt); + return addr; + } +} + + + +contract TestConstract { + uint public i; + constructor () public { + } + function plusOne() public returns(uint){ + i++; + } +} + */ + + /* +contract:TestConstract +deploy script: +deploycontract TestConstract_0.5.0 [{"constant":false,"inputs":[],"name":"plusOne","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"i","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] 608060405234801561001057600080fd5b50d3801561001d57600080fd5b50d2801561002a57600080fd5b5060d7806100396000396000f3fe608060405260043610602c5760003560e01c63ffffffff16806368e5c066146031578063e5aa3d5814606d575b600080fd5b348015603c57600080fd5b50d38015604857600080fd5b50d28015605457600080fd5b50605b6097565b60408051918252519081900360200190f35b348015607857600080fd5b50d38015608457600080fd5b50d28015609057600080fd5b50605b60a5565b600080546001019081905590565b6000548156fea165627a7a72305820c637cddbfa24b6530000f2e54d90e0f6c15907835674109287f64303446f9afb0029 # # false 1000000000 100 10000000 0 0 # +tirgger script: +triggercontract Txxxxxxxxxxx plusOne() # false 1000000000 0 0 # +triggercontract Txxxxxxxxxxx i() # false 1000000000 0 0 # + + +contract:Factory +deploy script: +deploycontract Factory_0.5.0 [{"constant":false,"inputs":[{"name":"code","type":"bytes"},{"name":"salt","type":"uint256"}],"name":"deploy","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"salt","type":"uint256"}],"name":"Deployed","type":"event"}] 608060405234801561001057600080fd5b50d3801561001d57600080fd5b50d2801561002a57600080fd5b506101c18061003a6000396000f3fe6080604052600436106100245760003560e01c63ffffffff1680639c4ae2d014610029575b600080fd5b34801561003557600080fd5b50d3801561004257600080fd5b50d2801561004f57600080fd5b506100f86004803603604081101561006657600080fd5b81019060208101813564010000000081111561008157600080fd5b82018360208201111561009357600080fd5b803590602001918460018302840111640100000000831117156100b557600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295505091359250610121915050565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b600080828451602086016000f59050803b151561013d57600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905281517fb03c53b28e78a88e31607a27e1fa48234dce28d5d9d9ec7b295aeb02e674a1e1929181900390910190a1939250505056fea165627a7a7230582079653f6506bd7d3bdf4954ec98c452c5455d2b11444642db00b38fa422b25a650029 # # false 1000000000 100 10000000 0 0 # +tirgger script: +triggercontract Txxxxxxxxxxx deploy(bytes,uint256) bytes,uint256 false 1000000000 0 0 # + + + +*/ + + @Test + public void testCreate2() + throws ContractExeException, ReceiptCheckErrException, VMIllegalException, ContractValidateException { + + String contractName = "Factory_0"; + byte[] address = Hex.decode(OWNER_ADDRESS); + String ABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"code\",\"type\":\"bytes\"},{\"name\":\"salt\",\"type\":\"uint256\"}],\"name\":\"deploy\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"salt\",\"type\":\"uint256\"}],\"name\":\"Deployed\",\"type\":\"event\"}]"; + String factoryCode = "608060405234801561001057600080fd5b50d3801561001d57600080fd5b50d2801561002a57600080fd5b506101c18061003a6000396000f3fe6080604052600436106100245760003560e01c63ffffffff1680639c4ae2d014610029575b600080fd5b34801561003557600080fd5b50d3801561004257600080fd5b50d2801561004f57600080fd5b506100f86004803603604081101561006657600080fd5b81019060208101813564010000000081111561008157600080fd5b82018360208201111561009357600080fd5b803590602001918460018302840111640100000000831117156100b557600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295505091359250610121915050565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b600080828451602086016000f59050803b151561013d57600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905281517fb03c53b28e78a88e31607a27e1fa48234dce28d5d9d9ec7b295aeb02e674a1e1929181900390910190a1939250505056fea165627a7a7230582079653f6506bd7d3bdf4954ec98c452c5455d2b11444642db00b38fa422b25a650029"; + String testCode = "608060405234801561001057600080fd5b50d3801561001d57600080fd5b50d2801561002a57600080fd5b5060d7806100396000396000f3fe608060405260043610602c5760003560e01c63ffffffff16806368e5c066146031578063e5aa3d5814606d575b600080fd5b348015603c57600080fd5b50d38015604857600080fd5b50d28015605457600080fd5b50605b6097565b60408051918252519081900360200190f35b348015607857600080fd5b50d38015608457600080fd5b50d28015609057600080fd5b50605b60a5565b600080546001019081905590565b6000548156fea165627a7a72305820c637cddbfa24b6530000f2e54d90e0f6c15907835674109287f64303446f9afb0029"; + long value = 0; + long fee = 100000000; + long consumeUserResourcePercent = 0; + String methodSign = "deploy(bytes,uint256)"; + + // deploy contract + Transaction trx = TVMTestUtils.generateDeploySmartContractAndGetTransaction( + contractName, address, ABI, factoryCode, value, fee, consumeUserResourcePercent, null); + byte[] factoryAddress = Wallet.generateContractAddress(trx); + runtime = TVMTestUtils.processTransactionAndReturnRuntime(trx, rootDeposit, null); + Assert.assertNull(runtime.getRuntimeError()); + + + // Trigger contract method: deploy(bytes,uint) + long salt = 100L; + String hexInput = AbiUtil.parseMethod(methodSign, Arrays.asList(testCode, salt)); + TVMTestResult result = TVMTestUtils + .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), + factoryAddress, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNull(result.getRuntime().getRuntimeError()); + + byte[] returnValue = result.getRuntime().getResult().getHReturn(); + byte[] actualContract = MUtil.convertToTronAddress(Arrays.copyOfRange(returnValue, 12, 32)); + byte[] expectedContract = Wallet.generateContractAddress2(address, Hex.decode(testCode), salt); + // check deployed contract + Assert.assertEquals(actualContract, expectedContract); + + // trigger deployed contract + String methodToTrigger = "plusOne()"; + hexInput = AbiUtil.parseMethod(methodToTrigger, Collections.emptyList()); + result = TVMTestUtils + .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), + actualContract, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNull(result.getRuntime().getRuntimeError()); + Assert.assertEquals(result.getRuntime().getResult().getHReturn(), new DataWord(1).getData()); + } + +} From 6b18e28c087b5374a6c2562d9fe07455090439b1 Mon Sep 17 00:00:00 2001 From: ashu Date: Tue, 19 Mar 2019 19:37:11 +0800 Subject: [PATCH 09/37] Remove debug msg --- src/main/java/org/tron/common/runtime/RuntimeImpl.java | 4 ++-- src/main/java/org/tron/common/runtime/vm/VM.java | 10 +++++----- src/main/java/org/tron/core/config/args/Args.java | 6 +++--- src/main/resources/config-localtest.conf | 3 +-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/tron/common/runtime/RuntimeImpl.java b/src/main/java/org/tron/common/runtime/RuntimeImpl.java index 742b6394a76..12aa6fa1c34 100644 --- a/src/main/java/org/tron/common/runtime/RuntimeImpl.java +++ b/src/main/java/org/tron/common/runtime/RuntimeImpl.java @@ -466,6 +466,7 @@ && isCheckTransaction()) { deposit.createContract(contractAddress, new ContractCapsule(newSmartContract)); byte[] code = newSmartContract.getBytecode().toByteArray(); + // TODO add hardfork // byte[] precompiledCode = ProgramPrecompile.getCode(code); // deposit.saveCode(contractAddress, precompiledCode); @@ -532,8 +533,6 @@ private void call() checkTokenValueAndId(tokenValue, tokenId); byte[] code = this.deposit.getCode(contractAddress); - logger.info("ysc " + " contract:" + Wallet.encode58Check(contractAddress)); - logger.info("ysc " + " code:" + Hex.toHexString(code)); if (isNotEmpty(code)) { long feeLimit = trx.getRawData().getFeeLimit(); @@ -638,6 +637,7 @@ public void go() { } } else { result.spendEnergy(saveCodeEnergy); + // TODO add hardfork deposit.saveCode(program.getContractAddress().getNoLeadZeroesData(), code); } } 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 721b427de28..71fe461b06c 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -85,11 +85,11 @@ public void step(Program program) { } // hard fork for 3.2 -// if (!VMConfig.allowTvmTransferTrc10()) { -// if (op == CALLTOKEN || op == TOKENBALANCE || op == CALLTOKENVALUE || op == CALLTOKENID) { -// throw Program.Exception.invalidOpCode(program.getCurrentOp()); -// } -// } + if (!VMConfig.allowTvmTransferTrc10()) { + if (op == CALLTOKEN || op == TOKENBALANCE || op == CALLTOKENVALUE || op == CALLTOKENID) { + throw Program.Exception.invalidOpCode(program.getCurrentOp()); + } + } program.setLastOp(op.val()); program.verifyStackSize(op.require()); diff --git a/src/main/java/org/tron/core/config/args/Args.java b/src/main/java/org/tron/core/config/args/Args.java index 927e3342c50..b51cfeef360 100644 --- a/src/main/java/org/tron/core/config/args/Args.java +++ b/src/main/java/org/tron/core/config/args/Args.java @@ -864,9 +864,9 @@ public static void setParam(final String[] args, final String confFileName) { INSTANCE.blockNumForEneryLimit = config.hasPath("enery.limit.block.num") ? config.getInt("enery.limit.block.num") : 4727890L; - INSTANCE.vmTrace = true; -// config.hasPath("vm.vmTrace") ? config -// .getBoolean("vm.vmTrace") : false; + INSTANCE.vmTrace = + config.hasPath("vm.vmTrace") ? config + .getBoolean("vm.vmTrace") : false; INSTANCE.saveInternalTx = config.hasPath("vm.saveInternalTx") && config.getBoolean("vm.saveInternalTx"); diff --git a/src/main/resources/config-localtest.conf b/src/main/resources/config-localtest.conf index 11af4c63397..a9e7a47724d 100644 --- a/src/main/resources/config-localtest.conf +++ b/src/main/resources/config-localtest.conf @@ -94,7 +94,7 @@ node { maxActiveNodesWithSameIp = 10 - minParticipationRate = 0 + minParticipationRate = 15 # check the peer data transfer ,disconnect factor disconnectNumberFactor = 0.4 @@ -268,7 +268,6 @@ vm = { supportConstant = true minTimeRatio = 0.0 maxTimeRatio = 5.0 - vm.vmTrace = true } committee = { From a5d4069f71ac137f04636fc8403fcb0a750dc153 Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 13 Mar 2019 16:43:31 +0800 Subject: [PATCH 10/37] Add opcode: SHL,SHR,SAR --- .../org/tron/common/runtime/vm/DataWord.java | 148 +++++++++++++++--- .../org/tron/common/runtime/vm/OpCode.java | 12 ++ .../java/org/tron/common/runtime/vm/VM.java | 36 +++++ 3 files changed, 173 insertions(+), 23 deletions(-) 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 5c555c34516..1338cc8dede 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -17,6 +17,8 @@ */ package org.tron.common.runtime.vm; +import static org.tron.common.utils.ByteUtil.numberOfLeadingZeros; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import org.spongycastle.util.Arrays; @@ -40,10 +42,12 @@ public class DataWord implements Comparable { /* Maximum value of the DataWord */ public static final int DATAWORD_UNIT_SIZE = 32; + public static final int MAX_POW = 256; public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack public static final DataWord ZERO_EMPTY_ARRAY = new DataWord(new byte[0]); // don't push it in to the stack + public static final DataWord ONE = DataWord.of((byte) 1); private byte[] data = new byte[32]; @@ -65,6 +69,36 @@ private DataWord(ByteBuffer buffer) { this.data = targetByteBuffer.array(); } + public static DataWord of(byte[] data) { + if (data == null || data.length == 0) { + return DataWord.ZERO; + } + + int leadingZeroBits = numberOfLeadingZeros(data); + int valueBits = 8 * data.length - leadingZeroBits; + if (valueBits <= 8) { + if (data[data.length - 1] == 0) return DataWord.ZERO; + if (data[data.length - 1] == 1) return DataWord.ONE; + } + + if (data.length == 32) + return new DataWord(java.util.Arrays.copyOf(data, data.length)); + else if (data.length <= 32) { + byte[] bytes = new byte[32]; + System.arraycopy(data, 0, bytes, 32 - data.length, data.length); + return new DataWord(bytes); + } else { + throw new RuntimeException(String.format("Data word can't exceed 32 bytes: 0x%s", ByteUtil.toHexString(data))); + } + } + + public static DataWord of(byte num) { + byte[] bb = new byte[32]; + bb[31] = num; + return new DataWord(bb); + + } + @JsonCreator public DataWord(String data) { this(Hex.decode(data)); @@ -234,38 +268,60 @@ public DataWord xor(DataWord w2) { return this; } - public void negate() { - - if (this.isZero()) return; - - for (int i = 0; i < this.data.length; ++i) { - this.data[i] = (byte) ~this.data[i]; - } - - for (int i = this.data.length - 1; i >= 0; --i) { - this.data[i] = (byte) (1 + this.data[i] & 0xFF); - if (this.data[i] != 0) break; - } - } - - public void bnot() { + public DataWord bnot() { if (this.isZero()) { - this.data = ByteUtil.copyToArray(MAX_VALUE); - return; + return new DataWord(ByteUtil.copyToArray(MAX_VALUE)); } - this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); - } + return new DataWord(ByteUtil.copyToArray(MAX_VALUE.subtract(this.value()))); + } + + public DataWord negate() { + if (this.isZero()) return ZERO; + return bnot().add(DataWord.ONE); + } + +// public void negate() { +// +// if (this.isZero()) return; +// +// for (int i = 0; i < this.data.length; ++i) { +// this.data[i] = (byte) ~this.data[i]; +// } +// +// for (int i = this.data.length - 1; i >= 0; --i) { +// this.data[i] = (byte) (1 + this.data[i] & 0xFF); +// if (this.data[i] != 0) break; +// } +// } + +// public void bnot() { +// if (this.isZero()) { +// this.data = ByteUtil.copyToArray(MAX_VALUE); +// return; +// } +// this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); +// } // By : Holger // From : http://stackoverflow.com/a/24023466/459349 - public void add(DataWord word) { - byte[] result = new byte[32]; +// public void add(DataWord word) { +// byte[] result = new byte[32]; +// for (int i = 31, overflow = 0; i >= 0; i--) { +// int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; +// result[i] = (byte) v; +// overflow = v >>> 8; +// } +// this.data = result; +// } + + public DataWord add(DataWord word) { + byte[] newData = new byte[32]; for (int i = 31, overflow = 0; i >= 0; i--) { int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; - result[i] = (byte) v; + newData[i] = (byte) v; overflow = v >>> 8; } - this.data = result; + return new DataWord(newData); } // old add-method with BigInteger quick hack @@ -450,4 +506,50 @@ public String asString(){ public String toHexString() { return Hex.toHexString(data); } + + /** + * Shift left, both this and input arg are treated as unsigned + * @param arg + * @return this << arg + */ + public DataWord shiftLeft(DataWord arg) { + if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { + return DataWord.ZERO; + } + + BigInteger result = value().shiftLeft(arg.intValueSafe()); + return new DataWord(ByteUtil.copyToArray(result.and(MAX_VALUE))); + } + + /** + * Shift right, both this and input arg are treated as unsigned + * @param arg + * @return this >> arg + */ + public DataWord shiftRight(DataWord arg) { + if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { + return DataWord.ZERO; + } + + BigInteger result = value().shiftRight(arg.intValueSafe()); + return new DataWord(ByteUtil.copyToArray(result.and(MAX_VALUE))); + } + + /** + * Shift right, this is signed, while input arg is treated as unsigned + * @param arg + * @return this >> arg + */ + public DataWord shiftRightSigned(DataWord arg) { + if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { + if (this.isNegative()) { + return DataWord.ONE.negate(); + } else { + return DataWord.ZERO; + } + } + + BigInteger result = sValue().shiftRight(arg.intValueSafe()); + return new DataWord(ByteUtil.copyToArray(result.and(MAX_VALUE))); + } } diff --git a/src/main/java/org/tron/common/runtime/vm/OpCode.java b/src/main/java/org/tron/common/runtime/vm/OpCode.java index a7aede3f4b2..bcc9b8c9615 100644 --- a/src/main/java/org/tron/common/runtime/vm/OpCode.java +++ b/src/main/java/org/tron/common/runtime/vm/OpCode.java @@ -130,6 +130,18 @@ public enum OpCode { * (0x1a) Retrieve single byte from word */ BYTE(0x1a, 2, 1, OpCode.Tier.VeryLowTier), + /** + * (0x1b) Shift left + */ + SHL(0x1b, 2, 1, OpCode.Tier.VeryLowTier), + /** + * (0x1c) Logical shift right + */ + SHR(0x1c, 2, 1, OpCode.Tier.VeryLowTier), + /** + * (0x1d) Arithmetic shift right + */ + SAR(0x1d, 2, 1, OpCode.Tier.VeryLowTier), /* Cryptographic Operations */ 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 3f10cf4d3f5..a7b473b3713 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -592,6 +592,42 @@ public void step(Program program) { program.step(); } break; + case SHL: { + DataWord word1 = program.stackPop(); + DataWord word2 = program.stackPop(); + final DataWord result = word2.shiftLeft(word1); + + if (logger.isInfoEnabled()) + hint = "" + result.value(); + + program.stackPush(result); + program.step(); + } + break; + case SHR: { + DataWord word1 = program.stackPop(); + DataWord word2 = program.stackPop(); + final DataWord result = word2.shiftRight(word1); + + if (logger.isInfoEnabled()) + hint = "" + result.value(); + + program.stackPush(result); + program.step(); + } + break; + case SAR: { + DataWord word1 = program.stackPop(); + DataWord word2 = program.stackPop(); + final DataWord result = word2.shiftRightSigned(word1); + + if (logger.isInfoEnabled()) + hint = "" + result.value(); + + program.stackPush(result); + program.step(); + } + break; case ADDMOD: { DataWord word1 = program.stackPop(); DataWord word2 = program.stackPop(); From 4bf766e672dc460ba0f2910bcd6a5cce52e3c35b Mon Sep 17 00:00:00 2001 From: ashu Date: Mon, 18 Mar 2019 21:23:42 +0800 Subject: [PATCH 11/37] Add bitwise shift instructions --- .../org/tron/common/runtime/vm/DataWord.java | 78 +++++++------------ 1 file changed, 29 insertions(+), 49 deletions(-) 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 1338cc8dede..8da12d31946 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -268,60 +268,38 @@ public DataWord xor(DataWord w2) { return this; } - public DataWord bnot() { + public void negate() { + + if (this.isZero()) return; + + for (int i = 0; i < this.data.length; ++i) { + this.data[i] = (byte) ~this.data[i]; + } + + for (int i = this.data.length - 1; i >= 0; --i) { + this.data[i] = (byte) (1 + this.data[i] & 0xFF); + if (this.data[i] != 0) break; + } + } + + public void bnot() { if (this.isZero()) { - return new DataWord(ByteUtil.copyToArray(MAX_VALUE)); + this.data = ByteUtil.copyToArray(MAX_VALUE); + return; } - return new DataWord(ByteUtil.copyToArray(MAX_VALUE.subtract(this.value()))); - } - - public DataWord negate() { - if (this.isZero()) return ZERO; - return bnot().add(DataWord.ONE); - } - -// public void negate() { -// -// if (this.isZero()) return; -// -// for (int i = 0; i < this.data.length; ++i) { -// this.data[i] = (byte) ~this.data[i]; -// } -// -// for (int i = this.data.length - 1; i >= 0; --i) { -// this.data[i] = (byte) (1 + this.data[i] & 0xFF); -// if (this.data[i] != 0) break; -// } -// } - -// public void bnot() { -// if (this.isZero()) { -// this.data = ByteUtil.copyToArray(MAX_VALUE); -// return; -// } -// this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); -// } + this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); + } // By : Holger // From : http://stackoverflow.com/a/24023466/459349 -// public void add(DataWord word) { -// byte[] result = new byte[32]; -// for (int i = 31, overflow = 0; i >= 0; i--) { -// int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; -// result[i] = (byte) v; -// overflow = v >>> 8; -// } -// this.data = result; -// } - - public DataWord add(DataWord word) { - byte[] newData = new byte[32]; + public void add(DataWord word) { + byte[] result = new byte[32]; for (int i = 31, overflow = 0; i >= 0; i--) { int v = (this.data[i] & 0xff) + (word.data[i] & 0xff) + overflow; - newData[i] = (byte) v; + result[i] = (byte) v; overflow = v >>> 8; } - return new DataWord(newData); + this.data = result; } // old add-method with BigInteger quick hack @@ -474,8 +452,8 @@ public int hashCode() { public int compareTo(DataWord o) { if (o == null || o.getData() == null) return -1; int result = FastByteComparisons.compareTo( - data, 0, data.length, - o.getData(), 0, o.getData().length); + data, 0, data.length, + o.getData(), 0, o.getData().length); // Convert result into -1, 0 or 1 as is the convention return (int) Math.signum(result); } @@ -504,7 +482,7 @@ public String asString(){ } public String toHexString() { - return Hex.toHexString(data); + return Hex.toHexString(data); } /** @@ -543,7 +521,9 @@ public DataWord shiftRight(DataWord arg) { public DataWord shiftRightSigned(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { if (this.isNegative()) { - return DataWord.ONE.negate(); + DataWord result = DataWord.ONE; + result.negate(); + return result; } else { return DataWord.ZERO; } From 417297f0f820fab4cf4daa79a09c26f04fae15eb Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 20 Mar 2019 18:34:56 +0800 Subject: [PATCH 12/37] Add shift test --- .../tron/common/runtime/vm/DataWordTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/test/java/org/tron/common/runtime/vm/DataWordTest.java b/src/test/java/org/tron/common/runtime/vm/DataWordTest.java index 3388dfab36a..189e582365e 100644 --- a/src/test/java/org/tron/common/runtime/vm/DataWordTest.java +++ b/src/test/java/org/tron/common/runtime/vm/DataWordTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue; import java.math.BigInteger; +import java.util.Arrays; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.spongycastle.util.encoders.Hex; @@ -430,4 +431,106 @@ private static BigInteger pow(BigInteger x, BigInteger y) { } return result; } + + @Test + public void testShiftLeft() { + Object[][] cases = { + { "0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, + { "0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000002"}, + { "0000000000000000000000000000000000000000000000000000000000000001", "ff", "8000000000000000000000000000000000000000000000000000000000000000"}, + { "0000000000000000000000000000000000000000000000000000000000000001", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "0000000000000000000000000000000000000000000000000000000000000001", "0101", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "8000000000000000000000000000000000000000000000000000000000000000"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"}, + }; + + testShiftLeft(cases); + } + + private void testShiftLeft(Object[][] cases) { + for (Object[] c: cases) { + DataWord value = new DataWord(Hex.decode(c[0].toString())); + DataWord arg = new DataWord(Hex.decode(c[1].toString())); + DataWord expected = new DataWord(Hex.decode(c[2].toString())); + DataWord result = value.shiftLeft(arg); + + assertEquals(result, expected); + } + } + + @Test + public void testShiftRight() { + Object[][] cases = { + { "0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, + { "0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "01", "4000000000000000000000000000000000000000000000000000000000000000"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "ff", "0000000000000000000000000000000000000000000000000000000000000001"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "0101", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000001"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + }; + + testShiftRight(cases); + } + + private void testShiftRight(Object[][] cases) { + for (Object[] c: cases) { + DataWord value = new DataWord(c[0].toString()); + DataWord arg = new DataWord(c[1].toString()); + DataWord expected = new DataWord(c[2].toString()); + DataWord result = value.shiftRight(arg); + + assertEquals(result, expected); + } + } + + @Test + public void testShiftRightSigned() { + String[][] cases = { +// { "0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, +// { "0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, +// { "8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"}, +// { "8000000000000000000000000000000000000000000000000000000000000000", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, +// { "8000000000000000000000000000000000000000000000000000000000000000", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "0101", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "4000000000000000000000000000000000000000000000000000000000000000", "fe", "0000000000000000000000000000000000000000000000000000000000000001"}, + { "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "f8", "000000000000000000000000000000000000000000000000000000000000007f"}, + { "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "fe", "0000000000000000000000000000000000000000000000000000000000000001"}, + { "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + }; + + testShiftRightSigned(cases); + } + + private void testShiftRightSigned(String[][] cases) { + int i = 1; + for (String[] c: cases) { + DataWord value = new DataWord(c[0]); + DataWord arg = new DataWord(c[1]); + DataWord expected = new DataWord(c[2]); + DataWord actual = value.shiftRightSigned(arg); + +// assertEquals(); + assertEquals(i + " " + Arrays.asList(c).toString(), expected,actual); +// assertEquals("value:" + c.toString(), result, expected); + + i++; + } + } + + } From ec2e7aee90a35601195109bc8ab95deab925c854 Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 20 Mar 2019 19:50:13 +0800 Subject: [PATCH 13/37] Fix shiftRightSigned bug --- .../org/tron/common/runtime/vm/DataWord.java | 29 +++++++++---------- .../tron/common/runtime/vm/DataWordTest.java | 15 ++++------ 2 files changed, 18 insertions(+), 26 deletions(-) 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 8da12d31946..4634f5ca636 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -27,7 +27,6 @@ import org.tron.common.utils.FastByteComparisons; import org.tron.core.db.ByteArrayWrapper; -import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -45,9 +44,15 @@ public class DataWord implements Comparable { public static final int MAX_POW = 256; public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); + // TODO not safe public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack - public static final DataWord ZERO_EMPTY_ARRAY = new DataWord(new byte[0]); // don't push it in to the stack - public static final DataWord ONE = DataWord.of((byte) 1); + + public static DataWord ONE() { + return DataWord.of((byte)1); + } +// public static DataWord ZERO() { +// return new DataWord(new byte[32]); +// } private byte[] data = new byte[32]; @@ -78,7 +83,7 @@ public static DataWord of(byte[] data) { int valueBits = 8 * data.length - leadingZeroBits; if (valueBits <= 8) { if (data[data.length - 1] == 0) return DataWord.ZERO; - if (data[data.length - 1] == 1) return DataWord.ONE; + if (data[data.length - 1] == 1) return DataWord.ONE(); } if (data.length == 32) @@ -269,23 +274,15 @@ public DataWord xor(DataWord w2) { } public void negate() { - if (this.isZero()) return; - for (int i = 0; i < this.data.length; ++i) { - this.data[i] = (byte) ~this.data[i]; - } - - for (int i = this.data.length - 1; i >= 0; --i) { - this.data[i] = (byte) (1 + this.data[i] & 0xFF); - if (this.data[i] != 0) break; - } + bnot(); + add(DataWord.ONE()); } public void bnot() { if (this.isZero()) { this.data = ByteUtil.copyToArray(MAX_VALUE); - return; } this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); } @@ -521,11 +518,11 @@ public DataWord shiftRight(DataWord arg) { public DataWord shiftRightSigned(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { if (this.isNegative()) { - DataWord result = DataWord.ONE; + DataWord result = ONE(); result.negate(); return result; } else { - return DataWord.ZERO; + return new DataWord(new byte[32]); } } diff --git a/src/test/java/org/tron/common/runtime/vm/DataWordTest.java b/src/test/java/org/tron/common/runtime/vm/DataWordTest.java index 189e582365e..749360e5ed8 100644 --- a/src/test/java/org/tron/common/runtime/vm/DataWordTest.java +++ b/src/test/java/org/tron/common/runtime/vm/DataWordTest.java @@ -495,11 +495,11 @@ private void testShiftRight(Object[][] cases) { @Test public void testShiftRightSigned() { String[][] cases = { -// { "0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, -// { "0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, -// { "8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"}, -// { "8000000000000000000000000000000000000000000000000000000000000000", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, -// { "8000000000000000000000000000000000000000000000000000000000000000", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, + { "0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + { "8000000000000000000000000000000000000000000000000000000000000000", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, { "8000000000000000000000000000000000000000000000000000000000000000", "0101", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, @@ -523,12 +523,7 @@ private void testShiftRightSigned(String[][] cases) { DataWord arg = new DataWord(c[1]); DataWord expected = new DataWord(c[2]); DataWord actual = value.shiftRightSigned(arg); - -// assertEquals(); assertEquals(i + " " + Arrays.asList(c).toString(), expected,actual); -// assertEquals("value:" + c.toString(), result, expected); - - i++; } } From b0c742cedce252d51b7ee6b1fc7cc3d733305e92 Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 20 Mar 2019 19:59:05 +0800 Subject: [PATCH 14/37] Eliminate hidden dangers of DataWord where ZERO is pushed in to the stack --- .../org/tron/common/runtime/vm/DataWord.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) 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 4634f5ca636..e3709f8dc6f 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -45,14 +45,14 @@ public class DataWord implements Comparable { public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); // TODO not safe - public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack +// public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack public static DataWord ONE() { return DataWord.of((byte)1); } -// public static DataWord ZERO() { -// return new DataWord(new byte[32]); -// } + public static DataWord ZERO() { + return new DataWord(new byte[32]); + } private byte[] data = new byte[32]; @@ -76,13 +76,13 @@ private DataWord(ByteBuffer buffer) { public static DataWord of(byte[] data) { if (data == null || data.length == 0) { - return DataWord.ZERO; + return DataWord.ZERO(); } int leadingZeroBits = numberOfLeadingZeros(data); int valueBits = 8 * data.length - leadingZeroBits; if (valueBits <= 8) { - if (data[data.length - 1] == 0) return DataWord.ZERO; + if (data[data.length - 1] == 0) return DataWord.ZERO(); if (data[data.length - 1] == 1) return DataWord.ONE(); } @@ -317,7 +317,7 @@ public void mul(DataWord word) { public void div(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -329,7 +329,7 @@ public void div(DataWord word) { public void sDiv(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -353,7 +353,7 @@ public void exp(DataWord word) { public void mod(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -364,7 +364,7 @@ public void mod(DataWord word) { public void sMod(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -489,7 +489,7 @@ public String toHexString() { */ public DataWord shiftLeft(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { - return DataWord.ZERO; + return DataWord.ZERO(); } BigInteger result = value().shiftLeft(arg.intValueSafe()); @@ -503,7 +503,7 @@ public DataWord shiftLeft(DataWord arg) { */ public DataWord shiftRight(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { - return DataWord.ZERO; + return DataWord.ZERO(); } BigInteger result = value().shiftRight(arg.intValueSafe()); @@ -522,7 +522,7 @@ public DataWord shiftRightSigned(DataWord arg) { result.negate(); return result; } else { - return new DataWord(new byte[32]); + return ZERO(); } } From 9ead8475fec8aecf09e8e97947f100df3b663fc5 Mon Sep 17 00:00:00 2001 From: ashu Date: Thu, 21 Mar 2019 10:08:00 +0800 Subject: [PATCH 15/37] Fix unsafe method --- .../org/tron/common/runtime/vm/DataWord.java | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) 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 8da12d31946..e3709f8dc6f 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -27,7 +27,6 @@ import org.tron.common.utils.FastByteComparisons; import org.tron.core.db.ByteArrayWrapper; -import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -45,9 +44,15 @@ public class DataWord implements Comparable { public static final int MAX_POW = 256; public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); - public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack - public static final DataWord ZERO_EMPTY_ARRAY = new DataWord(new byte[0]); // don't push it in to the stack - public static final DataWord ONE = DataWord.of((byte) 1); + // TODO not safe +// public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack + + public static DataWord ONE() { + return DataWord.of((byte)1); + } + public static DataWord ZERO() { + return new DataWord(new byte[32]); + } private byte[] data = new byte[32]; @@ -71,14 +76,14 @@ private DataWord(ByteBuffer buffer) { public static DataWord of(byte[] data) { if (data == null || data.length == 0) { - return DataWord.ZERO; + return DataWord.ZERO(); } int leadingZeroBits = numberOfLeadingZeros(data); int valueBits = 8 * data.length - leadingZeroBits; if (valueBits <= 8) { - if (data[data.length - 1] == 0) return DataWord.ZERO; - if (data[data.length - 1] == 1) return DataWord.ONE; + if (data[data.length - 1] == 0) return DataWord.ZERO(); + if (data[data.length - 1] == 1) return DataWord.ONE(); } if (data.length == 32) @@ -269,23 +274,15 @@ public DataWord xor(DataWord w2) { } public void negate() { - if (this.isZero()) return; - for (int i = 0; i < this.data.length; ++i) { - this.data[i] = (byte) ~this.data[i]; - } - - for (int i = this.data.length - 1; i >= 0; --i) { - this.data[i] = (byte) (1 + this.data[i] & 0xFF); - if (this.data[i] != 0) break; - } + bnot(); + add(DataWord.ONE()); } public void bnot() { if (this.isZero()) { this.data = ByteUtil.copyToArray(MAX_VALUE); - return; } this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); } @@ -320,7 +317,7 @@ public void mul(DataWord word) { public void div(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -332,7 +329,7 @@ public void div(DataWord word) { public void sDiv(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -356,7 +353,7 @@ public void exp(DataWord word) { public void mod(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -367,7 +364,7 @@ public void mod(DataWord word) { public void sMod(DataWord word) { if (word.isZero()) { - this.and(ZERO); + this.and(ZERO()); return; } @@ -492,7 +489,7 @@ public String toHexString() { */ public DataWord shiftLeft(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { - return DataWord.ZERO; + return DataWord.ZERO(); } BigInteger result = value().shiftLeft(arg.intValueSafe()); @@ -506,7 +503,7 @@ public DataWord shiftLeft(DataWord arg) { */ public DataWord shiftRight(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { - return DataWord.ZERO; + return DataWord.ZERO(); } BigInteger result = value().shiftRight(arg.intValueSafe()); @@ -521,11 +518,11 @@ public DataWord shiftRight(DataWord arg) { public DataWord shiftRightSigned(DataWord arg) { if (arg.value().compareTo(BigInteger.valueOf(MAX_POW)) >= 0) { if (this.isNegative()) { - DataWord result = DataWord.ONE; + DataWord result = ONE(); result.negate(); return result; } else { - return DataWord.ZERO; + return ZERO(); } } From f7673d84964041585ca48a28cc23c4aa540881a1 Mon Sep 17 00:00:00 2001 From: ashu Date: Thu, 21 Mar 2019 10:48:02 +0800 Subject: [PATCH 16/37] Add Zero DataWord --- src/main/java/org/tron/common/runtime/vm/DataWord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e3709f8dc6f..0d1456bafb1 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -45,7 +45,7 @@ public class DataWord implements Comparable { public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); // TODO not safe -// public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack + public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack public static DataWord ONE() { return DataWord.of((byte)1); From 1057095dc0500d3698efadf20c61babca47ba011 Mon Sep 17 00:00:00 2001 From: ashu Date: Thu, 21 Mar 2019 11:57:50 +0800 Subject: [PATCH 17/37] Fix bug --- src/main/java/org/tron/common/runtime/vm/DataWord.java | 1 + 1 file changed, 1 insertion(+) 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 0d1456bafb1..a7f80252138 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -283,6 +283,7 @@ public void negate() { public void bnot() { if (this.isZero()) { this.data = ByteUtil.copyToArray(MAX_VALUE); + return; } this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value())); } From b9348c8786a63ec33a6a47bb5857f39bba5aa26e Mon Sep 17 00:00:00 2001 From: ashu Date: Thu, 21 Mar 2019 12:19:04 +0800 Subject: [PATCH 18/37] Zero DataWord can be rightValue --- src/main/java/org/tron/common/runtime/vm/DataWord.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 a7f80252138..fc23ff5b767 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -318,7 +318,7 @@ public void mul(DataWord word) { public void div(DataWord word) { if (word.isZero()) { - this.and(ZERO()); + this.and(ZERO); return; } @@ -330,7 +330,7 @@ public void div(DataWord word) { public void sDiv(DataWord word) { if (word.isZero()) { - this.and(ZERO()); + this.and(ZERO); return; } @@ -354,7 +354,7 @@ public void exp(DataWord word) { public void mod(DataWord word) { if (word.isZero()) { - this.and(ZERO()); + this.and(ZERO); return; } From 9aa288bc51ebd00b636d86e4dc485800ff243588 Mon Sep 17 00:00:00 2001 From: ashu Date: Thu, 21 Mar 2019 17:45:37 +0800 Subject: [PATCH 19/37] Add repeat create2 test case --- .../tron/common/runtime/vm/Create2Test.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/src/test/java/org/tron/common/runtime/vm/Create2Test.java index b9bb5df34d3..c50bbc0c780 100644 --- a/src/test/java/org/tron/common/runtime/vm/Create2Test.java +++ b/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -106,12 +106,22 @@ public void testCreate2() // trigger deployed contract String methodToTrigger = "plusOne()"; - hexInput = AbiUtil.parseMethod(methodToTrigger, Collections.emptyList()); - result = TVMTestUtils + for (int i = 1; i < 3; i++) { + hexInput = AbiUtil.parseMethod(methodToTrigger, Collections.emptyList()); + result = TVMTestUtils + .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), + actualContract, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNull(result.getRuntime().getRuntimeError()); + Assert.assertEquals(result.getRuntime().getResult().getHReturn(), new DataWord(i).getData()); + } + + // deploy contract again + result = TVMTestUtils .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), - actualContract, Hex.decode(hexInput), 0, fee, manager, null); - Assert.assertNull(result.getRuntime().getRuntimeError()); - Assert.assertEquals(result.getRuntime().getResult().getHReturn(), new DataWord(1).getData()); + factoryAddress, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNotNull(result.getRuntime().getRuntimeError()); + Assert.assertEquals(result.getRuntime().getRuntimeError(), "REVERT opcode executed"); + } } From 0b9faefc3872cce0f44b8336e1905bc045f959c2 Mon Sep 17 00:00:00 2001 From: jeancky Date: Fri, 22 Mar 2019 11:48:03 +0800 Subject: [PATCH 20/37] refactor --- .../org/tron/common/runtime/vm/DataWord.java | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) 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 fc23ff5b767..7d37416e1c1 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -68,9 +68,9 @@ public DataWord(long num) { } private DataWord(ByteBuffer buffer) { - final ByteBuffer targetByteBuffer = ByteBuffer.allocate(32); + final ByteBuffer targetByteBuffer = ByteBuffer.allocate(DATAWORD_UNIT_SIZE); final byte[] array = buffer.array(); - System.arraycopy(array, 0, targetByteBuffer.array(), 32 - array.length, array.length); + System.arraycopy(array, 0, targetByteBuffer.array(), DATAWORD_UNIT_SIZE - array.length, array.length); this.data = targetByteBuffer.array(); } @@ -85,20 +85,11 @@ public static DataWord of(byte[] data) { if (data[data.length - 1] == 0) return DataWord.ZERO(); if (data[data.length - 1] == 1) return DataWord.ONE(); } - - if (data.length == 32) - return new DataWord(java.util.Arrays.copyOf(data, data.length)); - else if (data.length <= 32) { - byte[] bytes = new byte[32]; - System.arraycopy(data, 0, bytes, 32 - data.length, data.length); - return new DataWord(bytes); - } else { - throw new RuntimeException(String.format("Data word can't exceed 32 bytes: 0x%s", ByteUtil.toHexString(data))); - } + return new DataWord(java.util.Arrays.copyOf(data, data.length)); } public static DataWord of(byte num) { - byte[] bb = new byte[32]; + byte[] bb = new byte[DATAWORD_UNIT_SIZE]; bb[31] = num; return new DataWord(bb); @@ -116,12 +107,12 @@ public DataWord(ByteArrayWrapper wrappedData){ public DataWord(byte[] data) { if (data == null) { this.data = ByteUtil.EMPTY_BYTE_ARRAY; - } else if (data.length == 32) { + } else if (data.length == DATAWORD_UNIT_SIZE) { this.data = data; - } else if (data.length <= 32) { - System.arraycopy(data, 0, this.data, 32 - data.length, data.length); + } else if (data.length < DATAWORD_UNIT_SIZE) { + System.arraycopy(data, 0, this.data, DATAWORD_UNIT_SIZE - data.length, data.length); } else { - throw new RuntimeException("Data word can't exceed 32 bytes: " + data); + throw new RuntimeException("Data word can't exceed 32 bytes: " + ByteUtil.toHexString(data)); } } From 7895022f18bab3427c01c7dc9da8ed265e624ca4 Mon Sep 17 00:00:00 2001 From: ashu Date: Fri, 22 Mar 2019 12:31:01 +0800 Subject: [PATCH 21/37] Polish code --- src/main/java/org/tron/common/runtime/vm/DataWord.java | 10 +++++++--- src/main/java/org/tron/common/runtime/vm/VM.java | 9 +++++---- src/main/java/org/tron/common/runtime/vm/VMUtils.java | 7 ------- 3 files changed, 12 insertions(+), 14 deletions(-) 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 fc23ff5b767..7d75938b91e 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -40,7 +40,7 @@ public class DataWord implements Comparable { /* Maximum value of the DataWord */ - public static final int DATAWORD_UNIT_SIZE = 32; + public static final int UNIT_SIZE = 32; public static final int MAX_POW = 256; public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); @@ -136,8 +136,8 @@ public byte[] getData() { public byte[] getClonedData() { byte[] ret = ByteUtil.EMPTY_BYTE_ARRAY; if (data != null){ - ret = new byte[DATAWORD_UNIT_SIZE]; - int dataSize = Math.min(data.length, DATAWORD_UNIT_SIZE); + ret = new byte[UNIT_SIZE]; + int dataSize = Math.min(data.length, UNIT_SIZE); System.arraycopy(data, 0, ret, 0, dataSize); } return ret; @@ -530,4 +530,8 @@ public DataWord shiftRightSigned(DataWord arg) { BigInteger result = sValue().shiftRight(arg.intValueSafe()); return new DataWord(ByteUtil.copyToArray(result.and(MAX_VALUE))); } + + public static long sizeInWords(long bytesSize) { + return bytesSize == 0 ? 0 : (bytesSize - 1) / UNIT_SIZE + 1; + } } 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 71fe461b06c..43137dbdcd0 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -241,10 +241,11 @@ public void step(Program program) { break; case CREATE2: DataWord codeSize = stack.get(stack.size() - 3); - energyCost = energyCosts.getCREATE() + - calcMemEnergy(energyCosts, oldMemSize, - memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)), 0, op) + - VMUtils.getSizeInWords(codeSize.longValueSafe()) * energyCosts.getSHA3_WORD(); + energyCost = energyCosts.getCREATE(); + energyCost += calcMemEnergy(energyCosts, oldMemSize, + memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)), 0, op); + energyCost += DataWord.sizeInWords(codeSize.intValueSafe()) * energyCosts.getSHA3_WORD(); + break; case LOG0: case LOG1: diff --git a/src/main/java/org/tron/common/runtime/vm/VMUtils.java b/src/main/java/org/tron/common/runtime/vm/VMUtils.java index bed883315cd..67424f8b799 100644 --- a/src/main/java/org/tron/common/runtime/vm/VMUtils.java +++ b/src/main/java/org/tron/common/runtime/vm/VMUtils.java @@ -156,11 +156,4 @@ public static String unzipAndDecode(String content) { return content; } } - - /** - * Returns number of VM words required to hold data of size {@code size} - */ - public static long getSizeInWords(long size) { - return size == 0 ? 0 : (size - 1) / 32 + 1; - } } From 7aaac97c81ef571a5ffd4edef3aa06edfb18c189 Mon Sep 17 00:00:00 2001 From: jeancky Date: Fri, 22 Mar 2019 12:33:17 +0800 Subject: [PATCH 22/37] refactor --- src/main/java/org/tron/common/runtime/vm/OpCode.java | 7 +++++-- src/main/java/org/tron/core/Wallet.java | 5 ----- src/test/java/org/tron/common/runtime/vm/Create2Test.java | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/tron/common/runtime/vm/OpCode.java b/src/main/java/org/tron/common/runtime/vm/OpCode.java index 416b8641eaf..539aaa98db1 100644 --- a/src/main/java/org/tron/common/runtime/vm/OpCode.java +++ b/src/main/java/org/tron/common/runtime/vm/OpCode.java @@ -616,6 +616,11 @@ public enum OpCode { */ DELEGATECALL(0xf4, 6, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.Stateless, CallFlags.Delegate), + /** + * (0xf5) Skinny CREATE2, same as CREATE but with deterministic address + */ + CREATE2(0xf5, 4, 1, OpCode.Tier.SpecialTier), + /** * opcode that can be used to call another contract (or itself) while disallowing any * modifications to the state during the call (and its subcalls, if present). @@ -624,8 +629,6 @@ public enum OpCode { */ STATICCALL(0xfa, 6, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.Static), - CREATE2(0xf5, 4, 1, OpCode.Tier.SpecialTier), - /** * (0xfd) The `REVERT` instruction will stop execution, roll back all state changes done so far * and provide a pointer to a memory section, which can be interpreted as an error code or message. diff --git a/src/main/java/org/tron/core/Wallet.java b/src/main/java/org/tron/core/Wallet.java index f7f36522c5c..3c4cc8eca34 100755 --- a/src/main/java/org/tron/core/Wallet.java +++ b/src/main/java/org/tron/core/Wallet.java @@ -288,11 +288,6 @@ public static byte[] generateContractAddress2(byte[] address, byte[] code, byte[ return Hash.sha3omit12(mergedData); } - // for test - public static byte[] generateContractAddress2(byte[] address, byte[] code, long salt) { - return generateContractAddress2(address, code, new DataWord(salt).getData()); - } - // for `CREATE` public static byte[] generateContractAddress(byte[] transactionRootId, long nonce) { byte[] nonceBytes = Longs.toByteArray(nonce); diff --git a/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/src/test/java/org/tron/common/runtime/vm/Create2Test.java index b9bb5df34d3..7d9835fc895 100644 --- a/src/test/java/org/tron/common/runtime/vm/Create2Test.java +++ b/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -17,6 +17,7 @@ import org.tron.core.exception.VMIllegalException; import org.tron.protos.Protocol.Transaction; import stest.tron.wallet.common.client.utils.AbiUtil; +import stest.tron.wallet.common.client.utils.DataWord; @Slf4j public class Create2Test extends VMTestBase { @@ -91,7 +92,6 @@ public void testCreate2() // Trigger contract method: deploy(bytes,uint) - long salt = 100L; String hexInput = AbiUtil.parseMethod(methodSign, Arrays.asList(testCode, salt)); TVMTestResult result = TVMTestUtils .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), @@ -100,7 +100,7 @@ public void testCreate2() byte[] returnValue = result.getRuntime().getResult().getHReturn(); byte[] actualContract = MUtil.convertToTronAddress(Arrays.copyOfRange(returnValue, 12, 32)); - byte[] expectedContract = Wallet.generateContractAddress2(address, Hex.decode(testCode), salt); + byte[] expectedContract = Wallet.generateContractAddress2(address, Hex.decode(testCode), new DataWord(100L).getData()); // check deployed contract Assert.assertEquals(actualContract, expectedContract); From 30c55b7824e7bcf0ae4cf4cab0edb284458187fb Mon Sep 17 00:00:00 2001 From: ashu Date: Fri, 22 Mar 2019 12:38:45 +0800 Subject: [PATCH 23/37] Rename final var --- prop.properties | 0 .../org/tron/common/runtime/vm/DataWord.java | 22 +++++++++---------- .../runtime/vm/PrecompiledContracts.java | 14 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 prop.properties diff --git a/prop.properties b/prop.properties new file mode 100644 index 00000000000..e69de29bb2d 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 fb865c450a4..50ee5d59bda 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -40,7 +40,7 @@ public class DataWord implements Comparable { /* Maximum value of the DataWord */ - public static final int UNIT_SIZE = 32; + public static final int WORD_SIZE = 32; public static final int MAX_POW = 256; public static final BigInteger _2_256 = BigInteger.valueOf(2).pow(256); public static final BigInteger MAX_VALUE = _2_256.subtract(BigInteger.ONE); @@ -68,9 +68,9 @@ public DataWord(long num) { } private DataWord(ByteBuffer buffer) { - final ByteBuffer targetByteBuffer = ByteBuffer.allocate(DATAWORD_UNIT_SIZE); + final ByteBuffer targetByteBuffer = ByteBuffer.allocate(WORD_SIZE); final byte[] array = buffer.array(); - System.arraycopy(array, 0, targetByteBuffer.array(), DATAWORD_UNIT_SIZE - array.length, array.length); + System.arraycopy(array, 0, targetByteBuffer.array(), WORD_SIZE - array.length, array.length); this.data = targetByteBuffer.array(); } @@ -89,7 +89,7 @@ public static DataWord of(byte[] data) { } public static DataWord of(byte num) { - byte[] bb = new byte[DATAWORD_UNIT_SIZE]; + byte[] bb = new byte[WORD_SIZE]; bb[31] = num; return new DataWord(bb); @@ -107,10 +107,10 @@ public DataWord(ByteArrayWrapper wrappedData){ public DataWord(byte[] data) { if (data == null) { this.data = ByteUtil.EMPTY_BYTE_ARRAY; - } else if (data.length == DATAWORD_UNIT_SIZE) { + } else if (data.length == WORD_SIZE) { this.data = data; - } else if (data.length < DATAWORD_UNIT_SIZE) { - System.arraycopy(data, 0, this.data, DATAWORD_UNIT_SIZE - data.length, data.length); + } else if (data.length < WORD_SIZE) { + System.arraycopy(data, 0, this.data, WORD_SIZE - data.length, data.length); } else { throw new RuntimeException("Data word can't exceed 32 bytes: " + ByteUtil.toHexString(data)); } @@ -121,14 +121,14 @@ public byte[] getData() { } /** - * be careful, this one will not throw Exception when data.length > DATAWORD_UNIT_SIZE + * be careful, this one will not throw Exception when data.length > WORD_SIZE * @return */ public byte[] getClonedData() { byte[] ret = ByteUtil.EMPTY_BYTE_ARRAY; if (data != null){ - ret = new byte[UNIT_SIZE]; - int dataSize = Math.min(data.length, UNIT_SIZE); + ret = new byte[WORD_SIZE]; + int dataSize = Math.min(data.length, WORD_SIZE); System.arraycopy(data, 0, ret, 0, dataSize); } return ret; @@ -523,6 +523,6 @@ public DataWord shiftRightSigned(DataWord arg) { } public static long sizeInWords(long bytesSize) { - return bytesSize == 0 ? 0 : (bytesSize - 1) / UNIT_SIZE + 1; + return bytesSize == 0 ? 0 : (bytesSize - 1) / WORD_SIZE + 1; } } 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 b2782e7c4bf..6c6bf670741 100644 --- a/src/main/java/org/tron/common/runtime/vm/PrecompiledContracts.java +++ b/src/main/java/org/tron/common/runtime/vm/PrecompiledContracts.java @@ -713,7 +713,7 @@ public Pair execute(byte[] data) { if (isRootCallConstant()) { return Pair.of(true, new DataWord(0).getData()); } - if (data == null || data.length != 2 * DataWord.DATAWORD_UNIT_SIZE) { + if (data == null || data.length != 2 * DataWord.WORD_SIZE) { return Pair.of(false, new DataWord(0).getData()); } @@ -977,7 +977,7 @@ public Pair execute(byte[] data) { return Pair.of(true, new DataWord(0).getData()); } - if (data == null || data.length != 2 * DataWord.DATAWORD_UNIT_SIZE) { + if (data == null || data.length != 2 * DataWord.WORD_SIZE) { return Pair.of(false, new DataWord(0).getData()); } @@ -1053,7 +1053,7 @@ public Pair execute(byte[] data) { return Pair.of(true, new DataWord(0).getData()); } - if (data == null || data.length == 0 || (data.length % (2 * DataWord.DATAWORD_UNIT_SIZE) + if (data == null || data.length == 0 || (data.length % (2 * DataWord.WORD_SIZE) != 0)) { return Pair.of(false, new DataWord(0).getData()); } @@ -1138,7 +1138,7 @@ public Pair execute(byte[] data) { return Pair.of(true, new DataWord(0).getData()); } - if (data == null || data.length != DataWord.DATAWORD_UNIT_SIZE) { + if (data == null || data.length != DataWord.WORD_SIZE) { return Pair.of(false, new DataWord(0).getData()); } Contract.ProposalDeleteContract.Builder builder = Contract.ProposalDeleteContract @@ -1204,7 +1204,7 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { - if (data == null || data.length != DataWord.DATAWORD_UNIT_SIZE) { + if (data == null || data.length != DataWord.WORD_SIZE) { return Pair.of(false, new DataWord(0).getData()); } DataWord address = new DataWord(data); @@ -1265,7 +1265,7 @@ public Pair execute(byte[] data) { // return Pair.of(true, new DataWord(0).getData()); // } // -// if (data == null || (data.length <= DataWord.DATAWORD_UNIT_SIZE * 2 || data.length > DataWord.DATAWORD_UNIT_SIZE * 3)) { +// if (data == null || (data.length <= DataWord.WORD_SIZE * 2 || data.length > DataWord.WORD_SIZE * 3)) { // return Pair.of(false, new DataWord(0).getData()); // } // @@ -1332,7 +1332,7 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { - if (data == null || data.length != DataWord.DATAWORD_UNIT_SIZE * 2) { + if (data == null || data.length != DataWord.WORD_SIZE * 2) { return Pair.of(false, new DataWord(0).getData()); } From 17b0ba363a66c08f214cc5077704951cbecb7321 Mon Sep 17 00:00:00 2001 From: Heng Zhang Date: Fri, 22 Mar 2019 17:10:27 +0800 Subject: [PATCH 24/37] refactor: add ALLOW_TVM_CONSTANTINOPLE proposal --- src/main/java/org/tron/core/Wallet.java | 15 ++++++++---- .../core/actuator/ProposalCreateActuator.java | 15 +++++++++++- .../java/org/tron/core/config/Parameter.java | 1 + .../java/org/tron/core/config/args/Args.java | 9 +++++++ .../tron/core/db/DynamicPropertiesStore.java | 24 +++++++++++++++---- .../tron/core/witness/ProposalController.java | 4 ++++ 6 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/tron/core/Wallet.java b/src/main/java/org/tron/core/Wallet.java index 8cc80f6473e..bf6cbf6025e 100755 --- a/src/main/java/org/tron/core/Wallet.java +++ b/src/main/java/org/tron/core/Wallet.java @@ -396,7 +396,8 @@ public TransactionCapsule createTransactionCapsule(com.google.protobuf.Message m } trx.setReference(blockId.getNum(), blockId.getBytes()); long expiration = - dbManager.getHeadBlockTimeStamp() + Args.getInstance().getTrxExpirationTimeInMilliseconds(); + dbManager.getHeadBlockTimeStamp() + Args.getInstance() + .getTrxExpirationTimeInMilliseconds(); trx.setExpiration(expiration); trx.setTimestamp(); } catch (Exception e) { @@ -561,7 +562,7 @@ public TransactionSignWeight getTransactionSignWeight(Transaction trx) { throw new PermissionException("Permission type is error"); } //check oprations - if (!checkPermissionOprations(permission, contract)){ + if (!checkPermissionOprations(permission, contract)) { throw new PermissionException("Permission denied"); } } @@ -887,7 +888,12 @@ public Protocol.ChainParameters getChainParameters() { .setKey("getMultiSignFee") .setValue(dbManager.getDynamicPropertiesStore().getMultiSignFee()) .build()); - +// ALLOW_TVM_CONSTANTINOPLE, // 1, 24 + builder.addChainParameter( + Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getAllowTvmConstantinople") + .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmConstantinople()) + .build()); return builder.build(); } @@ -1301,7 +1307,8 @@ public Transaction triggerContract(TriggerSmartContract triggerSmartContract, Runtime runtime = new RuntimeImpl(trxCap.getInstance(), new BlockCapsule(headBlock), deposit, new ProgramInvokeFactoryImpl(), true); VMConfig.initVmHardFork(); - VMConfig.initAllowTvmTransferTrc10(dbManager.getDynamicPropertiesStore().getAllowTvmTransferTrc10()); + VMConfig.initAllowTvmTransferTrc10( + dbManager.getDynamicPropertiesStore().getAllowTvmTransferTrc10()); VMConfig.initAllowMultiSign(dbManager.getDynamicPropertiesStore().getAllowMultiSign()); runtime.execute(); runtime.go(); diff --git a/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java b/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java index 9b5402715a5..625e7a7a6b3 100755 --- a/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java +++ b/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java @@ -269,7 +269,8 @@ private void validateValue(Map.Entry entry) throws ContractValidateE } case (22): { if (!dbManager.getForkController().pass(ForkBlockVersionEnum.VERSION_3_5)) { - throw new ContractValidateException("Bad chain parameter id: UPDATE_ACCOUNT_PERMISSION_FEE"); + throw new ContractValidateException( + "Bad chain parameter id: UPDATE_ACCOUNT_PERMISSION_FEE"); } if (entry.getValue() < 0 || entry.getValue() > 100_000_000_000L) { throw new ContractValidateException( @@ -287,6 +288,18 @@ private void validateValue(Map.Entry entry) throws ContractValidateE } break; } + case (24): { + if (entry.getValue() != 1) { + throw new ContractValidateException( + "This value[ALLOW_TVM_CONSTANTINOPLE] is only allowed to be 1"); + } + if (dbManager.getDynamicPropertiesStore().getAllowTvmTransferTrc10() == 0) { + throw new ContractValidateException( + "[ALLOW_TVM_TRANSFER_TRC10] proposal must be approved " + + "before [ALLOW_TVM_CONSTANTINOPLE] can be proposed"); + } + break; + } default: break; } diff --git a/src/main/java/org/tron/core/config/Parameter.java b/src/main/java/org/tron/core/config/Parameter.java index 52795ec36e4..8a71a9ca525 100644 --- a/src/main/java/org/tron/core/config/Parameter.java +++ b/src/main/java/org/tron/core/config/Parameter.java @@ -96,6 +96,7 @@ enum ChainParameters { ALLOW_ADAPTIVE_ENERGY, // 1, 21 UPDATE_ACCOUNT_PERMISSION_FEE, // 100, 22 MULTI_SIGN_FEE, // 1, 23 + ALLOW_TVM_CONSTANTINOPLE, // 1, 24 // ONE_DAY_NET_LIMIT, // MAX_FROZEN_TIME, // MIN_FROZEN_TIME, diff --git a/src/main/java/org/tron/core/config/args/Args.java b/src/main/java/org/tron/core/config/args/Args.java index b51cfeef360..0cece72625f 100644 --- a/src/main/java/org/tron/core/config/args/Args.java +++ b/src/main/java/org/tron/core/config/args/Args.java @@ -337,6 +337,10 @@ public class Args { @Setter private long allowTvmTransferTrc10; //committee parameter + @Getter + @Setter + private long allowTvmConstantinople; //committee parameter + @Getter @Setter private int tcpNettyWorkThreadNum; @@ -485,6 +489,7 @@ public static void clearParam() { INSTANCE.allowCreationOfContracts = 0; INSTANCE.allowAdaptiveEnergy = 0; INSTANCE.allowTvmTransferTrc10 = 0; + INSTANCE.allowTvmConstantinople = 0; INSTANCE.allowDelegateResource = 0; INSTANCE.allowSameTokenName = 0; INSTANCE.tcpNettyWorkThreadNum = 0; @@ -814,6 +819,10 @@ public static void setParam(final String[] args, final String confFileName) { config.hasPath("committee.allowTvmTransferTrc10") ? config .getInt("committee.allowTvmTransferTrc10") : 0; + INSTANCE.allowTvmConstantinople = + config.hasPath("committee.allowTvmConstantinople") ? config + .getInt("committee.allowTvmConstantinople") : 0; + INSTANCE.tcpNettyWorkThreadNum = config.hasPath("node.tcpNettyWorkThreadNum") ? config .getInt("node.tcpNettyWorkThreadNum") : 0; diff --git a/src/main/java/org/tron/core/db/DynamicPropertiesStore.java b/src/main/java/org/tron/core/db/DynamicPropertiesStore.java index 9d7ed7340fe..90248fc5b61 100755 --- a/src/main/java/org/tron/core/db/DynamicPropertiesStore.java +++ b/src/main/java/org/tron/core/db/DynamicPropertiesStore.java @@ -161,6 +161,7 @@ private static class DynamicResourceProperties { //This value is only allowed to be 0, 1, -1 private static final byte[] ALLOW_TVM_TRANSFER_TRC10 = "ALLOW_TVM_TRANSFER_TRC10".getBytes(); + private static final byte[] ALLOW_TVM_CONSTANTINOPLE = "ALLOW_TVM_CONSTANTINOPLE".getBytes(); private static final byte[] AVAILABLE_CONTRACT_TYPE = "AVAILABLE_CONTRACT_TYPE".getBytes(); private static final byte[] ACTIVE_DEFAULT_OPERATIONS = "ACTIVE_DEFAULT_OPERATIONS".getBytes(); @@ -416,8 +417,6 @@ private DynamicPropertiesStore(@Value("properties") String dbName) { this.saveMultiSignFee(1000000L); } - - try { this.getExchangeCreateFee(); } catch (IllegalArgumentException e) { @@ -490,6 +489,11 @@ private DynamicPropertiesStore(@Value("properties") String dbName) { this.saveAllowTvmTransferTrc10(Args.getInstance().getAllowTvmTransferTrc10()); } + try { + this.getAllowTvmConstantinople(); + } catch (IllegalArgumentException e) { + this.saveAllowTvmConstantinople(Args.getInstance().getAllowTvmConstantinople()); + } try { this.getAvailableContractType(); } catch (IllegalArgumentException e) { @@ -506,7 +510,6 @@ private DynamicPropertiesStore(@Value("properties") String dbName) { this.saveActiveDefaultOperations(bytes); } - try { this.getAllowSameTokenName(); } catch (IllegalArgumentException e) { @@ -1086,7 +1089,6 @@ public long getMultiSignFee() { } - public void saveExchangeCreateFee(long fee) { this.put(EXCHANGE_CREATE_FEE, new BytesCapsule(ByteArray.fromLong(fee))); @@ -1256,6 +1258,19 @@ public long getAllowTvmTransferTrc10() { () -> new IllegalArgumentException("not found ALLOW_TVM_TRANSFER_TRC10")); } + public void saveAllowTvmConstantinople(long value) { + this.put(ALLOW_TVM_CONSTANTINOPLE, + new BytesCapsule(ByteArray.fromLong(value))); + } + + public long getAllowTvmConstantinople() { + return Optional.ofNullable(getUnchecked(ALLOW_TVM_CONSTANTINOPLE)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElseThrow( + () -> new IllegalArgumentException("not found ALLOW_TVM_CONSTANTINOPLE")); + } + public void saveAvailableContractType(byte[] value) { this.put(AVAILABLE_CONTRACT_TYPE, new BytesCapsule(value)); @@ -1282,7 +1297,6 @@ public byte[] getActiveDefaultOperations() { } - public boolean supportDR() { return getAllowDelegateResource() == 1L; } diff --git a/src/main/java/org/tron/core/witness/ProposalController.java b/src/main/java/org/tron/core/witness/ProposalController.java index fa208251855..3dca1e09848 100644 --- a/src/main/java/org/tron/core/witness/ProposalController.java +++ b/src/main/java/org/tron/core/witness/ProposalController.java @@ -202,6 +202,10 @@ public void setDynamicParameters(ProposalCapsule proposalCapsule) { manager.getDynamicPropertiesStore().saveMultiSignFee(entry.getValue()); break; } + case (24): { + manager.getDynamicPropertiesStore().saveAllowTvmConstantinople(entry.getValue()); + break; + } default: break; } From 2acc81b3884835e22cbb09bff4bcaf95533b6b93 Mon Sep 17 00:00:00 2001 From: Heng Zhang Date: Fri, 22 Mar 2019 17:37:40 +0800 Subject: [PATCH 25/37] refactor: filter the decision by allowTvmConstantinople proposal --- .../tron/common/runtime/config/VMConfig.java | 20 ++++++++++++++----- .../java/org/tron/common/runtime/vm/VM.java | 10 +++++++++- src/main/java/org/tron/core/db/Manager.java | 1 + 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/tron/common/runtime/config/VMConfig.java b/src/main/java/org/tron/common/runtime/config/VMConfig.java index 856bba22b3f..6dd500d310c 100644 --- a/src/main/java/org/tron/common/runtime/config/VMConfig.java +++ b/src/main/java/org/tron/common/runtime/config/VMConfig.java @@ -17,13 +17,10 @@ */ package org.tron.common.runtime.config; -import lombok.Getter; import lombok.Setter; import org.tron.common.utils.ForkController; import org.tron.core.config.Parameter.ForkBlockVersionConsts; -import org.tron.core.config.Parameter.ForkBlockVersionEnum; import org.tron.core.config.args.Args; -import org.tron.core.db.Manager; /** * For developer only @@ -48,6 +45,9 @@ public class VMConfig { @Setter private static boolean ALLOW_TVM_TRANSFER_TRC10 = false; + @Setter + private static boolean ALLOW_TVM_CONSTANTINOPLE = false; + @Setter private static boolean ALLOW_MULTI_SIGN = false; @@ -75,11 +75,17 @@ public static void initVmHardFork() { ENERGY_LIMIT_HARD_FORK = ForkController.instance().pass(ForkBlockVersionConsts.ENERGY_LIMIT); //VERSION_3_5_HARD_FORK = ForkController.instance().pass(ForkBlockVersionEnum.VERSION_3_5); } + public static void initAllowMultiSign(long allow) { - ALLOW_MULTI_SIGN = allow ==1 ; + ALLOW_MULTI_SIGN = allow == 1; } - public static void initAllowTvmTransferTrc10(long allow) { ALLOW_TVM_TRANSFER_TRC10 = allow == 1; + public static void initAllowTvmTransferTrc10(long allow) { + ALLOW_TVM_TRANSFER_TRC10 = allow == 1; + } + + public static void initAllowTvmConstantinople(long allow) { + ALLOW_TVM_CONSTANTINOPLE = allow == 1; } public static boolean getEnergyLimitHardFork() { @@ -90,6 +96,10 @@ public static boolean allowTvmTransferTrc10() { return ALLOW_TVM_TRANSFER_TRC10; } + public static boolean allowTvmConstantinople() { + return ALLOW_TVM_CONSTANTINOPLE; + } + public static boolean allowMultiSign() { return ALLOW_MULTI_SIGN; } 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 a7b473b3713..7e3cd987999 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -8,6 +8,9 @@ import static org.tron.common.runtime.vm.OpCode.CALLTOKENVALUE; import static org.tron.common.runtime.vm.OpCode.PUSH1; import static org.tron.common.runtime.vm.OpCode.REVERT; +import static org.tron.common.runtime.vm.OpCode.SAR; +import static org.tron.common.runtime.vm.OpCode.SHL; +import static org.tron.common.runtime.vm.OpCode.SHR; import static org.tron.common.runtime.vm.OpCode.TOKENBALANCE; import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY; @@ -90,7 +93,12 @@ public void step(Program program) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); } } - + + if (!VMConfig.allowTvmConstantinople()) { + if (op == SHL || op == SHR || op == SAR ) { + throw Program.Exception.invalidOpCode(program.getCurrentOp()); + } + } program.setLastOp(op.val()); program.verifyStackSize(op.require()); program.verifyStackOverflow(op.require(), op.ret()); //Check not exceeding stack limits diff --git a/src/main/java/org/tron/core/db/Manager.java b/src/main/java/org/tron/core/db/Manager.java index 06610e8eb3f..27612153e40 100644 --- a/src/main/java/org/tron/core/db/Manager.java +++ b/src/main/java/org/tron/core/db/Manager.java @@ -1216,6 +1216,7 @@ public boolean processTransaction(final TransactionCapsule trxCap, BlockCapsule VMConfig.initVmHardFork(); VMConfig.initAllowMultiSign(dynamicPropertiesStore.getAllowMultiSign()); VMConfig.initAllowTvmTransferTrc10(dynamicPropertiesStore.getAllowTvmTransferTrc10()); + VMConfig.initAllowTvmConstantinople(dynamicPropertiesStore.getAllowTvmConstantinople()); trace.init(blockCap, eventPluginLoaded); trace.checkIsConstant(); trace.exec(); From c4ce3cbafce7f24e91c8d60ef0502e532ef699b4 Mon Sep 17 00:00:00 2001 From: ashu Date: Mon, 25 Mar 2019 11:45:49 +0800 Subject: [PATCH 26/37] Fix salt value --- src/test/java/org/tron/common/runtime/vm/Create2Test.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/src/test/java/org/tron/common/runtime/vm/Create2Test.java index c90be55eee8..7b20f9f54fa 100644 --- a/src/test/java/org/tron/common/runtime/vm/Create2Test.java +++ b/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -92,6 +92,7 @@ public void testCreate2() // Trigger contract method: deploy(bytes,uint) + long salt = 100L; String hexInput = AbiUtil.parseMethod(methodSign, Arrays.asList(testCode, salt)); TVMTestResult result = TVMTestUtils .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), @@ -100,7 +101,7 @@ public void testCreate2() byte[] returnValue = result.getRuntime().getResult().getHReturn(); byte[] actualContract = MUtil.convertToTronAddress(Arrays.copyOfRange(returnValue, 12, 32)); - byte[] expectedContract = Wallet.generateContractAddress2(address, Hex.decode(testCode), new DataWord(100L).getData()); + byte[] expectedContract = Wallet.generateContractAddress2(address, Hex.decode(testCode), new DataWord(salt).getData()); // check deployed contract Assert.assertEquals(actualContract, expectedContract); From 5ce3d719e533c1d4b0a3b8b2b8657921e4e856c1 Mon Sep 17 00:00:00 2001 From: Heng Zhang Date: Mon, 25 Mar 2019 12:16:22 +0800 Subject: [PATCH 27/37] fix: ProposalCreateActuatorTest --- src/main/java/org/tron/common/runtime/vm/VM.java | 16 ++++++++++------ .../actuator/ProposalCreateActuatorTest.java | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) 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 7e3cd987999..a2277dfa8b8 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -93,9 +93,9 @@ public void step(Program program) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); } } - + if (!VMConfig.allowTvmConstantinople()) { - if (op == SHL || op == SHR || op == SAR ) { + if (op == SHL || op == SHR || op == SAR) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); } } @@ -605,8 +605,9 @@ public void step(Program program) { DataWord word2 = program.stackPop(); final DataWord result = word2.shiftLeft(word1); - if (logger.isInfoEnabled()) + if (logger.isInfoEnabled()) { hint = "" + result.value(); + } program.stackPush(result); program.step(); @@ -617,8 +618,9 @@ public void step(Program program) { DataWord word2 = program.stackPop(); final DataWord result = word2.shiftRight(word1); - if (logger.isInfoEnabled()) + if (logger.isInfoEnabled()) { hint = "" + result.value(); + } program.stackPush(result); program.step(); @@ -629,8 +631,9 @@ public void step(Program program) { DataWord word2 = program.stackPop(); final DataWord result = word2.shiftRightSigned(word1); - if (logger.isInfoEnabled()) + if (logger.isInfoEnabled()) { hint = "" + result.value(); + } program.stackPush(result); program.step(); @@ -1403,7 +1406,8 @@ public void play(Program program) { throw e; } catch (RuntimeException e) { if (StringUtils.isEmpty(e.getMessage())) { - logger.warn("Unknown Exception occurred, tx id: {}", Hex.toHexString(program.getRootTransactionId()), e); + logger.warn("Unknown Exception occurred, tx id: {}", + Hex.toHexString(program.getRootTransactionId()), e); program.setRuntimeFailure(new RuntimeException("Unknown Exception")); } else { program.setRuntimeFailure(e); diff --git a/src/test/java/org/tron/core/actuator/ProposalCreateActuatorTest.java b/src/test/java/org/tron/core/actuator/ProposalCreateActuatorTest.java index ea4697073cb..af43c4d0ec8 100644 --- a/src/test/java/org/tron/core/actuator/ProposalCreateActuatorTest.java +++ b/src/test/java/org/tron/core/actuator/ProposalCreateActuatorTest.java @@ -232,7 +232,7 @@ public void noWitness() { @Test public void invalidPara() { HashMap paras = new HashMap<>(); - paras.put(24L, 10000L); + paras.put(25L, 10000L); ProposalCreateActuator actuator = new ProposalCreateActuator(getContract(OWNER_ADDRESS_FIRST, paras), dbManager); TransactionResultCapsule ret = new TransactionResultCapsule(); From bc185cd6d4a8187ed6140b7dafe946063bcf29c1 Mon Sep 17 00:00:00 2001 From: ashu Date: Mon, 25 Mar 2019 15:10:06 +0800 Subject: [PATCH 28/37] Add constantinople hard fork --- .../java/org/tron/common/runtime/RuntimeImpl.java | 15 +++++++++------ src/main/java/org/tron/common/runtime/vm/VM.java | 3 ++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/tron/common/runtime/RuntimeImpl.java b/src/main/java/org/tron/common/runtime/RuntimeImpl.java index 12aa6fa1c34..cabc7eb8189 100644 --- a/src/main/java/org/tron/common/runtime/RuntimeImpl.java +++ b/src/main/java/org/tron/common/runtime/RuntimeImpl.java @@ -64,6 +64,7 @@ import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.Transaction.Result.contractResult; +import sun.security.krb5.Config; @Slf4j(topic = "VM") public class RuntimeImpl implements Runtime { @@ -466,10 +467,11 @@ && isCheckTransaction()) { deposit.createContract(contractAddress, new ContractCapsule(newSmartContract)); byte[] code = newSmartContract.getBytecode().toByteArray(); - // TODO add hardfork -// byte[] precompiledCode = ProgramPrecompile.getCode(code); -// deposit.saveCode(contractAddress, precompiledCode); - + if (VMConfig.allowTvmConstantinople()) { + deposit.saveCode(contractAddress, code); + } else { + deposit.saveCode(contractAddress, ProgramPrecompile.getCode(code)); + } // transfer from callerAddress to contractAddress according to callValue if (callValue > 0) { transfer(this.deposit, callerAddress, contractAddress, callValue); @@ -637,8 +639,9 @@ public void go() { } } else { result.spendEnergy(saveCodeEnergy); - // TODO add hardfork - deposit.saveCode(program.getContractAddress().getNoLeadZeroesData(), code); + if (VMConfig.allowTvmConstantinople()) { + deposit.saveCode(program.getContractAddress().getNoLeadZeroesData(), code); + } } } 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 24c82e7dc82..155f80c991f 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -6,6 +6,7 @@ import static org.tron.common.runtime.vm.OpCode.CALLTOKEN; import static org.tron.common.runtime.vm.OpCode.CALLTOKENID; import static org.tron.common.runtime.vm.OpCode.CALLTOKENVALUE; +import static org.tron.common.runtime.vm.OpCode.CREATE2; import static org.tron.common.runtime.vm.OpCode.PUSH1; import static org.tron.common.runtime.vm.OpCode.REVERT; import static org.tron.common.runtime.vm.OpCode.SAR; @@ -95,7 +96,7 @@ public void step(Program program) { } if (!VMConfig.allowTvmConstantinople()) { - if (op == SHL || op == SHR || op == SAR) { + if (op == SHL || op == SHR || op == SAR || op == CREATE2) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); } } From 05c64a88679a14e1c53889e4ea546db04f232549 Mon Sep 17 00:00:00 2001 From: ashu Date: Mon, 25 Mar 2019 15:11:49 +0800 Subject: [PATCH 29/37] Test add hard fork --- src/test/java/org/tron/common/runtime/vm/Create2Test.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/src/test/java/org/tron/common/runtime/vm/Create2Test.java index 7b20f9f54fa..4c81a798bc5 100644 --- a/src/test/java/org/tron/common/runtime/vm/Create2Test.java +++ b/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -8,6 +8,7 @@ import org.testng.Assert; import org.tron.common.runtime.TVMTestResult; import org.tron.common.runtime.TVMTestUtils; +import org.tron.common.runtime.config.VMConfig; import org.tron.common.runtime.utils.MUtil; import org.tron.common.utils.ByteUtil; import org.tron.core.Wallet; @@ -72,7 +73,7 @@ triggercontract Txxxxxxxxxxx deploy(bytes,uint256) bytes,uint256 false 100000000 @Test public void testCreate2() throws ContractExeException, ReceiptCheckErrException, VMIllegalException, ContractValidateException { - + VMConfig.initAllowTvmConstantinople(1); String contractName = "Factory_0"; byte[] address = Hex.decode(OWNER_ADDRESS); String ABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"code\",\"type\":\"bytes\"},{\"name\":\"salt\",\"type\":\"uint256\"}],\"name\":\"deploy\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"salt\",\"type\":\"uint256\"}],\"name\":\"Deployed\",\"type\":\"event\"}]"; From f4741926bac7b4f205b43b0ccf342ac50f9e3081 Mon Sep 17 00:00:00 2001 From: ashu Date: Mon, 25 Mar 2019 18:26:55 +0800 Subject: [PATCH 30/37] Remove `of` method of DataWord --- .../java/org/tron/common/runtime/vm/DataWord.java | 14 -------------- 1 file changed, 14 deletions(-) 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 50ee5d59bda..a0f7841d8e4 100644 --- a/src/main/java/org/tron/common/runtime/vm/DataWord.java +++ b/src/main/java/org/tron/common/runtime/vm/DataWord.java @@ -74,20 +74,6 @@ private DataWord(ByteBuffer buffer) { this.data = targetByteBuffer.array(); } - public static DataWord of(byte[] data) { - if (data == null || data.length == 0) { - return DataWord.ZERO(); - } - - int leadingZeroBits = numberOfLeadingZeros(data); - int valueBits = 8 * data.length - leadingZeroBits; - if (valueBits <= 8) { - if (data[data.length - 1] == 0) return DataWord.ZERO(); - if (data[data.length - 1] == 1) return DataWord.ONE(); - } - return new DataWord(java.util.Arrays.copyOf(data, data.length)); - } - public static DataWord of(byte num) { byte[] bb = new byte[WORD_SIZE]; bb[31] = num; From 41db26406e2d365cb46eff1e7a05d9f876182114 Mon Sep 17 00:00:00 2001 From: ashu Date: Tue, 26 Mar 2019 11:51:34 +0800 Subject: [PATCH 31/37] Add fork logic --- src/main/java/org/tron/common/runtime/RuntimeImpl.java | 6 +----- src/main/java/org/tron/core/Wallet.java | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/tron/common/runtime/RuntimeImpl.java b/src/main/java/org/tron/common/runtime/RuntimeImpl.java index cabc7eb8189..a08fb07c413 100644 --- a/src/main/java/org/tron/common/runtime/RuntimeImpl.java +++ b/src/main/java/org/tron/common/runtime/RuntimeImpl.java @@ -21,7 +21,6 @@ import org.tron.common.logsfilter.EventPluginLoader; import org.tron.common.logsfilter.trigger.ContractTrigger; import org.tron.common.runtime.config.VMConfig; -import org.tron.common.runtime.utils.MUtil; import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.EnergyCost; import org.tron.common.runtime.vm.LogInfoTriggerParser; @@ -64,7 +63,6 @@ import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.Transaction.Result.contractResult; -import sun.security.krb5.Config; @Slf4j(topic = "VM") public class RuntimeImpl implements Runtime { @@ -467,9 +465,7 @@ && isCheckTransaction()) { deposit.createContract(contractAddress, new ContractCapsule(newSmartContract)); byte[] code = newSmartContract.getBytecode().toByteArray(); - if (VMConfig.allowTvmConstantinople()) { - deposit.saveCode(contractAddress, code); - } else { + if (!VMConfig.allowTvmConstantinople()) { deposit.saveCode(contractAddress, ProgramPrecompile.getCode(code)); } // transfer from callerAddress to contractAddress according to callValue diff --git a/src/main/java/org/tron/core/Wallet.java b/src/main/java/org/tron/core/Wallet.java index 6b5c6a65c95..4c37710febe 100755 --- a/src/main/java/org/tron/core/Wallet.java +++ b/src/main/java/org/tron/core/Wallet.java @@ -1320,6 +1320,7 @@ public Transaction triggerContract(TriggerSmartContract triggerSmartContract, VMConfig.initAllowTvmTransferTrc10( dbManager.getDynamicPropertiesStore().getAllowTvmTransferTrc10()); VMConfig.initAllowMultiSign(dbManager.getDynamicPropertiesStore().getAllowMultiSign()); + VMConfig.initAllowTvmConstantinople(dbManager.getDynamicPropertiesStore().getAllowTvmConstantinople()); runtime.execute(); runtime.go(); runtime.finalization(); From 9aa7df8a84c4c47a6ee7badfabca177da8dc6054 Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 27 Mar 2019 12:48:50 +0800 Subject: [PATCH 32/37] Add init_code hash --- src/main/java/org/tron/core/Wallet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/tron/core/Wallet.java b/src/main/java/org/tron/core/Wallet.java index 4c37710febe..ef050d3be90 100755 --- a/src/main/java/org/tron/core/Wallet.java +++ b/src/main/java/org/tron/core/Wallet.java @@ -284,7 +284,7 @@ public static byte[] generateContractAddress(byte[] ownerAddress, byte[] txRawDa // for `CREATE2` public static byte[] generateContractAddress2(byte[] address, byte[] code, byte[] salt) { - byte[] mergedData = ByteUtil.merge(address, code, salt); + byte[] mergedData = ByteUtil.merge(address, salt, Hash.sha3(code)); return Hash.sha3omit12(mergedData); } From 0211005e73b722403adc777363c49383f3226244 Mon Sep 17 00:00:00 2001 From: llwslc Date: Wed, 27 Mar 2019 14:26:19 +0800 Subject: [PATCH 33/37] add opcode EXTCODEHASH --- .../tron/common/runtime/vm/EnergyCost.java | 5 + .../org/tron/common/runtime/vm/OpCode.java | 5 + .../java/org/tron/common/runtime/vm/VM.java | 10 ++ .../common/runtime/vm/program/Program.java | 6 + .../common/runtime/vm/ExtCodeHashTest.java | 105 ++++++++++++++++++ 5 files changed, 131 insertions(+) create mode 100644 src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java 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 3a7396c00d3..22cf4164bfe 100644 --- a/src/main/java/org/tron/common/runtime/vm/EnergyCost.java +++ b/src/main/java/org/tron/common/runtime/vm/EnergyCost.java @@ -61,6 +61,7 @@ public class EnergyCost { private final int EC_RECOVER = 3000; private final int EXT_CODE_SIZE = 20; private final int EXT_CODE_COPY = 20; + private final int EXT_CODE_HASH = 400; private final int NEW_ACCT_SUICIDE = 0; public int getSTEP() { @@ -271,6 +272,10 @@ public int getEXT_CODE_COPY() { return EXT_CODE_COPY; } + 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/OpCode.java b/src/main/java/org/tron/common/runtime/vm/OpCode.java index 539aaa98db1..ae8e79ecae7 100644 --- a/src/main/java/org/tron/common/runtime/vm/OpCode.java +++ b/src/main/java/org/tron/common/runtime/vm/OpCode.java @@ -219,6 +219,11 @@ public enum OpCode { * environment to memory with given offset */ EXTCODECOPY(0x3c, 4, 0, OpCode.Tier.ExtTier), + /** + * (0x3f) Returns the keccak256 hash of + * a contract’s code + */ + EXTCODEHASH(0x3f, 1,1 , OpCode.Tier.ExtTier), /* Block Information */ 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 155f80c991f..9b7f6595a54 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -198,6 +198,9 @@ public void step(Program program) { memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 4)), stack.get(stack.size() - 4).longValueSafe(), op); break; + case EXTCODEHASH: + energyCost = energyCosts.getEXT_CODE_HASH(); + break; case CALL: case CALLCODE: case DELEGATECALL: @@ -905,6 +908,13 @@ public void step(Program program) { program.step(); } break; + case EXTCODEHASH:{ + DataWord address = program.stackPop(); + byte[] codeHash = program.getCodeHashAt(address); + program.stackPush(codeHash); + program.step(); + } + break; case GASPRICE: { DataWord energyPrice = new DataWord(0); 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 82d25343949..5ef15cb2854 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 @@ -45,6 +45,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; import org.spongycastle.util.encoders.Hex; +import org.tron.common.crypto.Hash; import org.tron.common.runtime.config.VMConfig; import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.EnergyCost; @@ -847,6 +848,11 @@ public byte[] getCodeAt(DataWord address) { return nullToEmpty(code); } + public byte[] getCodeHashAt(DataWord address) { + byte[] code = getCodeAt(address); + return Hash.sha3(code); + } + public DataWord getContractAddress() { return invoke.getContractAddress().clone(); } diff --git a/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java b/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java new file mode 100644 index 00000000000..03e8d8c3f5e --- /dev/null +++ b/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java @@ -0,0 +1,105 @@ +package org.tron.common.runtime.vm; + +import java.math.BigInteger; +import java.util.Arrays; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; +import org.testng.Assert; +import org.tron.common.runtime.TVMTestResult; +import org.tron.common.runtime.TVMTestUtils; +import org.tron.common.runtime.config.VMConfig; +import org.tron.core.Wallet; +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.Transaction; +import stest.tron.wallet.common.client.utils.AbiUtil; +import stest.tron.wallet.common.client.utils.DataWord; + +@Slf4j +public class ExtCodeHashTest extends VMTestBase { +/* +pragma solidity ^0.5.0; +contract TestExtCodeHash { + + function getCodeHashByAddr(address _addr) public view returns (bytes32 _hash) { + assembly { + _hash := extcodehash(_addr) + } + } + function getCodeHashByUint(uint256 _addr) public view returns (bytes32 _hash) { + assembly { + _hash := extcodehash(_addr) + } + } +} +*/ + + @Test + public void testExtCodeHash() + throws ContractExeException, ReceiptCheckErrException, VMIllegalException, ContractValidateException { + VMConfig.initAllowTvmConstantinople(1); + String contractName = "TestExtCodeHash"; + byte[] address = Hex.decode(OWNER_ADDRESS); + String ABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"_addr\",\"type\":\"uint256\"}],\"name\":\"getCodeHashByUint\",\"outputs\":[{\"name\":\"_hash\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getCodeHashByAddr\",\"outputs\":[{\"name\":\"_hash\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"; + String factoryCode = "608060405234801561001057600080fd5b5061010d806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80637b77fd191460375780637d5e422d146076575b600080fd5b606060048036036020811015604b57600080fd5b810190808035906020019092919050505060cb565b6040518082815260200191505060405180910390f35b60b560048036036020811015608a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505060d6565b6040518082815260200191505060405180910390f35b6000813f9050919050565b6000813f905091905056fea165627a7a723058200f30933f006db4e1adeee12c030b87e720dad3cb169769159fc56ec25d9af66f0029"; + long value = 0; + long fee = 100000000; + long consumeUserResourcePercent = 0; + + // deploy contract + Transaction trx = TVMTestUtils.generateDeploySmartContractAndGetTransaction( + contractName, address, ABI, factoryCode, value, fee, consumeUserResourcePercent, null); + byte[] factoryAddress = Wallet.generateContractAddress(trx); + runtime = TVMTestUtils.processTransactionAndReturnRuntime(trx, rootDeposit, null); + Assert.assertNull(runtime.getRuntimeError()); + + // Trigger contract method: getCodeHashByAddr(address) + String methodByAddr = "getCodeHashByAddr(address)"; + String codeAddrStr = "27k66nycZATHzBasFT9782nTsYWqVtxdtAc"; + String hexInput = AbiUtil.parseMethod(methodByAddr, Arrays.asList(codeAddrStr)); + TVMTestResult result = TVMTestUtils + .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), + factoryAddress, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNull(result.getRuntime().getRuntimeError()); + + byte[] returnValue = result.getRuntime().getResult().getHReturn(); + // check deployed contract + Assert.assertEquals(Hex.toHexString(returnValue), "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + + + // trigger deployed contract + String methodByUint = "getCodeHashByUint(uint256)"; + byte[] fullHexAddr = new DataWord(factoryAddress).getData(); + hexInput = AbiUtil.parseMethod(methodByUint, Hex.toHexString(fullHexAddr), true); + result = TVMTestUtils + .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), + factoryAddress, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNull(result.getRuntime().getRuntimeError()); + + returnValue = result.getRuntime().getResult().getHReturn(); + // check deployed contract + Assert.assertEquals(Hex.toHexString(returnValue), "0837cd5e284138b633cd976ea6fcb719d61d7bc33d946ec5a2d0c7da419a0bd4"); + + + // trigger deployed contract + BigInteger bigIntAddr = new DataWord(factoryAddress).ssValue(); + String bigIntAddrChange = BigInteger.valueOf(2).pow(160).add(bigIntAddr).toString(16); + fullHexAddr = new DataWord(bigIntAddrChange).getData(); + hexInput = AbiUtil.parseMethod(methodByUint, Hex.toHexString(fullHexAddr), true); + result = TVMTestUtils + .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), + factoryAddress, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNull(result.getRuntime().getRuntimeError()); + + returnValue = result.getRuntime().getResult().getHReturn(); + // check deployed contract + Assert.assertEquals(Hex.toHexString(returnValue), "0837cd5e284138b633cd976ea6fcb719d61d7bc33d946ec5a2d0c7da419a0bd4"); + + } + +} + + From 5a5bd298932b978fa46f1746dd9524173c4bf36c Mon Sep 17 00:00:00 2001 From: llwslc Date: Wed, 27 Mar 2019 15:02:15 +0800 Subject: [PATCH 34/37] add extcodehash fork logic --- src/main/java/org/tron/common/runtime/vm/VM.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 9b7f6595a54..61b2c5eeb2e 100644 --- a/src/main/java/org/tron/common/runtime/vm/VM.java +++ b/src/main/java/org/tron/common/runtime/vm/VM.java @@ -7,6 +7,7 @@ import static org.tron.common.runtime.vm.OpCode.CALLTOKENID; import static org.tron.common.runtime.vm.OpCode.CALLTOKENVALUE; import static org.tron.common.runtime.vm.OpCode.CREATE2; +import static org.tron.common.runtime.vm.OpCode.EXTCODEHASH; import static org.tron.common.runtime.vm.OpCode.PUSH1; import static org.tron.common.runtime.vm.OpCode.REVERT; import static org.tron.common.runtime.vm.OpCode.SAR; @@ -96,7 +97,7 @@ public void step(Program program) { } if (!VMConfig.allowTvmConstantinople()) { - if (op == SHL || op == SHR || op == SAR || op == CREATE2) { + if (op == SHL || op == SHR || op == SAR || op == CREATE2 || op == EXTCODEHASH) { throw Program.Exception.invalidOpCode(program.getCurrentOp()); } } From 77a67cbf7727a16300225f633f770ce7b6bd42a1 Mon Sep 17 00:00:00 2001 From: llwslc Date: Wed, 27 Mar 2019 15:13:27 +0800 Subject: [PATCH 35/37] test case DataWord to BigInteger method typo --- src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java b/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java index 03e8d8c3f5e..f9491dccbdc 100644 --- a/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java +++ b/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java @@ -85,7 +85,7 @@ public void testExtCodeHash() // trigger deployed contract - BigInteger bigIntAddr = new DataWord(factoryAddress).ssValue(); + BigInteger bigIntAddr = new DataWord(factoryAddress).sValue(); String bigIntAddrChange = BigInteger.valueOf(2).pow(160).add(bigIntAddr).toString(16); fullHexAddr = new DataWord(bigIntAddrChange).getData(); hexInput = AbiUtil.parseMethod(methodByUint, Hex.toHexString(fullHexAddr), true); From 2259037c607bb3d7873e3f8892d9d57de748bfaa Mon Sep 17 00:00:00 2001 From: ashu Date: Wed, 27 Mar 2019 17:30:16 +0800 Subject: [PATCH 36/37] Fix code style of ABIUtil.java --- .../wallet/common/client/utils/AbiUtil.java | 56 ++++--------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java b/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java index 041363f71ee..67903588bf9 100644 --- a/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java +++ b/src/test/java/stest/tron/wallet/common/client/utils/AbiUtil.java @@ -14,15 +14,12 @@ public class AbiUtil { - static Pattern paramTypeBytes = Pattern.compile("^bytes([0-9]*)$"); - static Pattern paramTypeNumber = Pattern.compile("^(u?int)([0-9]*)$"); - static Pattern paramTypeArray = Pattern.compile("^(.*)\\[([0-9]*)\\]$"); - // + private static Pattern paramTypeBytes = Pattern.compile("^bytes([0-9]*)$"); + private static Pattern paramTypeNumber = Pattern.compile("^(u?int)([0-9]*)$"); + private static Pattern paramTypeArray = Pattern.compile("^(.*)\\[([0-9]*)]$"); + static abstract class Coder { boolean dynamic = false; - String name; - String type; - // DataWord[] encode abstract byte[] encode(String value); abstract byte[] decode(); @@ -38,11 +35,7 @@ public static String[] getTypes(String methodSign) { return typeString.split(","); } - public static String geMethodId(String methodSign) { - return null; - } - - public static Coder getParamCoder(String type) { + private static Coder getParamCoder(String type) { switch (type) { case "address": @@ -57,11 +50,13 @@ public static Coder getParamCoder(String type) { return new CoderNumber(); } - if (paramTypeBytes.matcher(type).find()) + if (paramTypeBytes.matcher(type).find()) { return new CoderFixedBytes(); + } - if (paramTypeNumber.matcher(type).find()) + if (paramTypeNumber.matcher(type).find()) { return new CoderNumber(); + } Matcher m = paramTypeArray.matcher(type); if (m.find()) { @@ -169,20 +164,6 @@ byte[] decode() { } } - static class CoderToken extends Coder { - - @Override - byte[] encode(String value) { - String hex = Hex.toHexString(new DataWord(value.getBytes()).getData()); - return new CoderFixedBytes().encode(hex); - } - - @Override - byte[] decode() { - return new byte[0]; - } - } - static class CoderDynamicBytes extends Coder { CoderDynamicBytes() { @@ -251,7 +232,7 @@ byte[] decode() { } } - public static byte[] encodeDynamicBytes(String value, boolean hex) { + private static byte[] encodeDynamicBytes(String value, boolean hex) { byte[] data; if (hex) { if (value.startsWith("0x")) { @@ -264,7 +245,7 @@ public static byte[] encodeDynamicBytes(String value, boolean hex) { return encodeDynamicBytes(data); } - public static byte[] encodeDynamicBytes(byte[] data) { + private static byte[] encodeDynamicBytes(byte[] data) { List ret = new ArrayList<>(); ret.add(new DataWord(data.length)); @@ -290,7 +271,7 @@ public static byte[] encodeDynamicBytes(byte[] data) { return retBytes; } - public static byte[] encodeDynamicBytes(String value) { + private static byte[] encodeDynamicBytes(String value) { byte[] data = value.getBytes(); List ret = new ArrayList<>(); ret.add(new DataWord(data.length)); @@ -412,12 +393,7 @@ public static String parseMethod(String methodSign, List parameters) { return parseMethod(methodSign, StringUtils.join(inputArr, ',')); } - public static byte[] getTronAddress(DataWord address) { - return ByteUtil.merge(new byte[] {41}, address.getLast20Bytes()); - } - public static void main(String[] args) { -// String method = "test(address,string,int)"; String method = "test(string,int2,string)"; String params = "asdf,3123,adf"; @@ -440,16 +416,8 @@ public static void main(String[] args) { System.out.println(parseMethod(method2, listString)); String bytesValue1 = "\"0112313\",112313"; - String bytesValue2 = "123123123"; System.out.println(parseMethod(byteMethod1, bytesValue1)); -// System.out.println(parseMethod(byteMethod1, bytesValue2)); - -// String method3 = "voteForSingleWitness(address,uint256)"; -// String method3 = "voteForSingleWitness(address)"; -// String params3 = "\"TNNqZuYhMfQvooC4kJwTsMJEQVU3vWGa5u\""; -// -// System.out.println(parseMethod(method3, params3)); } From 512170b3dcaab0549dffc418bce96034cbb856b6 Mon Sep 17 00:00:00 2001 From: llwslc Date: Wed, 27 Mar 2019 17:45:41 +0800 Subject: [PATCH 37/37] change return of getCodeHashAt --- .../tron/common/runtime/vm/program/Program.java | 9 +++++++-- .../tron/common/runtime/vm/ExtCodeHashTest.java | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) 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 5ef15cb2854..67afd1f75c2 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 @@ -849,8 +849,13 @@ public byte[] getCodeAt(DataWord address) { } public byte[] getCodeHashAt(DataWord address) { - byte[] code = getCodeAt(address); - return Hash.sha3(code); + AccountCapsule existingAddr = getContractState().getAccount(convertToTronAddress(address.getLast20Bytes())); + if (existingAddr != null) { + byte[] code = getCodeAt(address); + return Hash.sha3(code); + } else { + return EMPTY_BYTE_ARRAY; + } } public DataWord getContractAddress() { diff --git a/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java b/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java index f9491dccbdc..4e8fbbe9ca6 100644 --- a/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java +++ b/src/test/java/org/tron/common/runtime/vm/ExtCodeHashTest.java @@ -58,8 +58,8 @@ public void testExtCodeHash() // Trigger contract method: getCodeHashByAddr(address) String methodByAddr = "getCodeHashByAddr(address)"; - String codeAddrStr = "27k66nycZATHzBasFT9782nTsYWqVtxdtAc"; - String hexInput = AbiUtil.parseMethod(methodByAddr, Arrays.asList(codeAddrStr)); + String nonexistentAccount = "27k66nycZATHzBasFT9782nTsYWqVtxdtAc"; + String hexInput = AbiUtil.parseMethod(methodByAddr, Arrays.asList(nonexistentAccount)); TVMTestResult result = TVMTestUtils .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), factoryAddress, Hex.decode(hexInput), 0, fee, manager, null); @@ -67,6 +67,18 @@ public void testExtCodeHash() byte[] returnValue = result.getRuntime().getResult().getHReturn(); // check deployed contract + Assert.assertEquals(Hex.toHexString(returnValue), "0000000000000000000000000000000000000000000000000000000000000000"); + + // trigger deployed contract + String existentAccount = "27WtBq2KoSy5v8VnVZBZHHJcDuWNiSgjbE3"; + hexInput = AbiUtil.parseMethod(methodByAddr, Arrays.asList(existentAccount)); + result = TVMTestUtils + .triggerContractAndReturnTVMTestResult(Hex.decode(OWNER_ADDRESS), + factoryAddress, Hex.decode(hexInput), 0, fee, manager, null); + Assert.assertNull(result.getRuntime().getRuntimeError()); + + returnValue = result.getRuntime().getResult().getHReturn(); + // check deployed contract Assert.assertEquals(Hex.toHexString(returnValue), "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");