From cc4fb4ab0b4971e13ae9e2812e90f2ee5147399e Mon Sep 17 00:00:00 2001 From: Anton Nahsatyrev Date: Tue, 28 Jun 2016 16:06:45 +0300 Subject: [PATCH] Update DAO rescue soft-fork rules --- .../org/ethereum/config/BlockchainConfig.java | 3 +- .../config/blockchain/AbstractConfig.java | 3 +- .../config/blockchain/HomesteadDAOConfig.java | 29 ++++++++++---- .../ethereum/core/TransactionExecutor.java | 2 +- .../java/org/ethereum/core/DAORescueTest.java | 38 +++++++++++++++---- 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java index 854bc8b6e5..4a89bb2d19 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/BlockchainConfig.java @@ -48,5 +48,6 @@ public interface BlockchainConfig { * @param repositoryTrack The repository track changed by transaction * @return null if all is fine or String validation error */ - String validateTransactionChanges(BlockStore blockStore, Block curBlock, RepositoryTrack repositoryTrack); + String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transaction tx, + RepositoryTrack repositoryTrack); } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java index a0bb5615bd..04d21c6a1c 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java @@ -82,7 +82,8 @@ public boolean acceptTransactionSignature(Transaction tx) { } @Override - public String validateTransactionChanges(BlockStore blockStore, Block curBlock, RepositoryTrack repositoryTrack) { + public String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transaction tx, + RepositoryTrack repositoryTrack) { return null; } } diff --git a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/HomesteadDAOConfig.java b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/HomesteadDAOConfig.java index b904211a2c..b3870fde55 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/blockchain/HomesteadDAOConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/blockchain/HomesteadDAOConfig.java @@ -4,6 +4,7 @@ import org.ethereum.core.AccountState; import org.ethereum.core.Block; import org.ethereum.core.BlockHeader; +import org.ethereum.core.Transaction; import org.ethereum.db.BlockStore; import org.ethereum.db.ByteArrayWrapper; import org.ethereum.db.RepositoryTrack; @@ -19,36 +20,43 @@ */ public class HomesteadDAOConfig extends HomesteadConfig { - public static final long DAO_RESCUE_BLOCK = 1_760_000; + public static final long DAO_RESCUE_BLOCK = 1_800_000; public static final long DAO_RESCUE_GAS_LIMIT_TRIGGER = 4_000_000; public static final byte[] DAO_CODE_HASH = Hex.decode("6a5d24750f78441e56fec050dc52fe8e911976485b7472faac7464a176a67caa"); + public static final byte[][] WHITELISTED_RECIPIENTS = new byte[][] { + Hex.decode("Da4a4626d3E16e094De3225A751aAb7128e96526"), + Hex.decode("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334") + }; private final long daoRescueBlock; private final long daoRescueGasLimitTrigger; private final byte[] daoCodeHash; + private final byte[][] whitelist; private Boolean rescue = null; public HomesteadDAOConfig() { - this(DAO_RESCUE_BLOCK, DAO_RESCUE_GAS_LIMIT_TRIGGER, DAO_CODE_HASH); + this(DAO_RESCUE_BLOCK, DAO_RESCUE_GAS_LIMIT_TRIGGER, DAO_CODE_HASH, WHITELISTED_RECIPIENTS); } public HomesteadDAOConfig(Constants constants) { - this(constants, DAO_RESCUE_BLOCK, DAO_RESCUE_GAS_LIMIT_TRIGGER, DAO_CODE_HASH); + this(constants, DAO_RESCUE_BLOCK, DAO_RESCUE_GAS_LIMIT_TRIGGER, DAO_CODE_HASH, WHITELISTED_RECIPIENTS); } - public HomesteadDAOConfig(long daoRescueBlock, long daoRescueGasLimitTrigger, byte[] daoCodeHash) { + public HomesteadDAOConfig(long daoRescueBlock, long daoRescueGasLimitTrigger, byte[] daoCodeHash, byte[][] whitelist) { this.daoRescueBlock = daoRescueBlock; this.daoRescueGasLimitTrigger = daoRescueGasLimitTrigger; this.daoCodeHash = daoCodeHash; + this.whitelist = whitelist; } public HomesteadDAOConfig(Constants constants, long daoRescueBlock, long daoRescueGasLimitTrigger, - byte[] daoCodeHash) { + byte[] daoCodeHash, byte[][] whitelist) { super(constants); this.daoRescueBlock = daoRescueBlock; this.daoRescueGasLimitTrigger = daoRescueGasLimitTrigger; this.daoCodeHash = daoCodeHash; + this.whitelist = whitelist; } private boolean shouldRescueDAO(BlockStore bs, Block curBlock) { @@ -74,7 +82,8 @@ private boolean shouldRescueDAO(BlockStore bs, Block curBlock) { } @Override - public String validateTransactionChanges(BlockStore blockStore, Block curBlock, RepositoryTrack repositoryTrack) { + public String validateTransactionChanges(BlockStore blockStore, Block curBlock, Transaction tx, + RepositoryTrack repositoryTrack) { if (shouldRescueDAO(blockStore, curBlock)) { Set changedAddresses = repositoryTrack.getFullAddressSet(); for (ByteArrayWrapper address : changedAddresses) { @@ -84,7 +93,13 @@ public String validateTransactionChanges(BlockStore blockStore, Block curBlock, BigInteger newBalance = repositoryTrack.getBalance(address.getData()); BigInteger oldBalance = repositoryTrack.getOriginRepository().getBalance(address.getData()); if (newBalance.compareTo(oldBalance) < 0) { - return String.format("RescueDAO: DAO balance decrease %s > %s", oldBalance, newBalance); + for (byte[] whiteRecipient : whitelist) { + if (FastByteComparisons.equal(tx.getReceiveAddress(), whiteRecipient)) { + return null; + } + } + return String.format("RescueDAO: DAO balance decrease %s > %s, recipient is not whitelisted: %s", + oldBalance, newBalance, Hex.toHexString(tx.getReceiveAddress())); } } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 945aa22389..a95b627469 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -302,7 +302,7 @@ public void finalization() { if (!readyToExecute) return; String err = config.getBlockchainConfig().getConfigForBlock(currentBlock.getNumber()). - validateTransactionChanges(blockStore, currentBlock, (RepositoryTrack) cacheTrack); + validateTransactionChanges(blockStore, currentBlock, tx, (RepositoryTrack) cacheTrack); if (err != null) { execError(err); m_endGas = toBI(tx.getGasLimit()); diff --git a/ethereumj-core/src/test/java/org/ethereum/core/DAORescueTest.java b/ethereumj-core/src/test/java/org/ethereum/core/DAORescueTest.java index fa2bb7daca..359316ece8 100644 --- a/ethereumj-core/src/test/java/org/ethereum/core/DAORescueTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/core/DAORescueTest.java @@ -41,6 +41,11 @@ public BigInteger getMINIMUM_DIFFICULTY() { "function robDao(address daoAddr) {" + " TestDAO(daoAddr).withdraw();" + "}" + + "}" + + "contract WhiteHat {" + + "function saveDao(address daoAddr) {" + + " TestDAO(daoAddr).withdraw();" + + "}" + "}"; @BeforeClass @@ -50,11 +55,13 @@ public static void setup() throws Exception { StandaloneBlockchain bc = new StandaloneBlockchain().withAutoblock(true); SolidityContract dao = bc.submitNewContract(daoEmulator, "TestDAO"); final byte[] codeHash = bc.getBlockchain().getRepository().getAccountState(dao.getAddress()).getCodeHash(); + SolidityContract white = bc.submitNewContract(daoEmulator, "WhiteHat"); + final byte[] whiteAddr = white.getAddress(); SystemProperties.getDefault().setBlockchainConfig(new AbstractNetConfig() { { add(0, new HomesteadConfig(easyMiningConst)); - add(5, new HomesteadDAOConfig(easyMiningConst, 5, 0x1_000_000_001L, codeHash)); + add(5, new HomesteadDAOConfig(easyMiningConst, 5, 0x1_000_000_001L, codeHash, new byte[][] {whiteAddr})); } }); } @@ -69,6 +76,7 @@ public void testForkAgreed() { StandaloneBlockchain bc = new StandaloneBlockchain() .withAutoblock(false); SolidityContract dao = bc.submitNewContract(daoEmulator, "TestDAO"); + SolidityContract white = bc.submitNewContract(daoEmulator, "WhiteHat"); bc.createBlock(); // #1 SolidityContract daoRobber = bc.submitNewContract(daoEmulator, "DAORobber"); bc.sendEther(dao.getAddress(), BigInteger.valueOf(1000)); @@ -91,10 +99,12 @@ public void testForkAgreed() { dao.callFunction("withdraw"); bc.createBlock(); // #5 invalid daoRobber.callFunction("robDao", Hex.toHexString(dao.getAddress())); - bc.createBlock(); // #6 invalid + bc.createBlock();// #6 invalid + white.callFunction("saveDao", Hex.toHexString(dao.getAddress())); + bc.createBlock();// #6 invalid long balance = bc.getBlockchain().getRepository().getBalance(dao.getAddress()).longValue(); - Assert.assertEquals(960, balance); + Assert.assertEquals(950, balance); for (int i = 0; i < 10; i++) { dao.callFunction(10, "deposit"); @@ -109,6 +119,12 @@ public void testForkAgreed() { bc.createBlock(); Assert.assertEquals(BigInteger.valueOf(balance), bc.getBlockchain().getRepository().getBalance(dao.getAddress())); + + white.callFunction("saveDao", Hex.toHexString(dao.getAddress())); + bc.createBlock();// #6 invalid + balance -= 10; + Assert.assertEquals(BigInteger.valueOf(balance), + bc.getBlockchain().getRepository().getBalance(dao.getAddress())); } } @@ -118,6 +134,7 @@ public void testForkIgnored() { .withAutoblock(false) .withBlockGasIncrease(100); SolidityContract dao = bc.submitNewContract(daoEmulator, "TestDAO"); + SolidityContract white = bc.submitNewContract(daoEmulator, "WhiteHat"); bc.createBlock(); // #1 SolidityContract daoRobber = bc.submitNewContract(daoEmulator, "DAORobber"); bc.sendEther(dao.getAddress(), BigInteger.valueOf(1000)); @@ -155,6 +172,7 @@ public void testForkUncertain() { StandaloneBlockchain bc = new StandaloneBlockchain() .withAutoblock(false); SolidityContract dao = bc.submitNewContract(daoEmulator, "TestDAO"); + SolidityContract white = bc.submitNewContract(daoEmulator, "WhiteHat"); bc.createBlock(); // #1 SolidityContract daoRobber = bc.submitNewContract(daoEmulator, "DAORobber"); bc.sendEther(dao.getAddress(), BigInteger.valueOf(1000)); @@ -170,23 +188,27 @@ public void testForkUncertain() { dao.callFunction("withdraw"); daoRobber.callFunction("robDao", Hex.toHexString(dao.getAddress())); - bc.createForkBlock(b5y); // #6 invalid + Block b6y = bc.createForkBlock(b5y);// #6 Assert.assertEquals(BigInteger.valueOf(1000), bc.getBlockchain().getRepository().getBalance(dao.getAddress())); + white.callFunction("saveDao", Hex.toHexString(dao.getAddress())); + Block b7y = bc.createForkBlock(b6y);// #6 + Assert.assertEquals(BigInteger.valueOf(990), + bc.getBlockchain().getRepository().getBalance(dao.getAddress())); dao.callFunction("withdraw"); daoRobber.callFunction("robDao", Hex.toHexString(dao.getAddress())); Block b6n = bc.createForkBlock(b5n);// #6' Block b7n = bc.createForkBlock(b6n); // #7' + Block b8n = bc.createForkBlock(b7n); // #8' Assert.assertEquals(BigInteger.valueOf(980), bc.getBlockchain().getRepository().getBalance(dao.getAddress())); - Block b6 = bc.createForkBlock(b5y);// #6 - Block b7 = bc.createForkBlock(b6);// #7 (main) - Block b8 = bc.createForkBlock(b7);// #7 (main) + Block b8 = bc.createForkBlock(b7y);// #7 (main) + Block b9 = bc.createForkBlock(b8);// #7 (main) - Assert.assertEquals(BigInteger.valueOf(1000), + Assert.assertEquals(BigInteger.valueOf(990), bc.getBlockchain().getRepository().getBalance(dao.getAddress())); } }