package org.ethereum.core;
import org.ethereum.config.CommonConfig;
import org.ethereum.config.SystemProperties;
import org.ethereum.config.blockchain.FrontierConfig;
import org.ethereum.core.genesis.GenesisLoader;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.datasource.inmem.HashMapDB;
import org.ethereum.datasource.NoDeleteSource;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.IndexedBlockStore;
import org.ethereum.db.RepositoryRoot;
import org.ethereum.listener.EthereumListenerAdapter;
import org.ethereum.mine.Ethash;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.blockchain.SolidityContract;
import org.ethereum.util.blockchain.StandaloneBlockchain;
import org.ethereum.validator.DependentBlockHeaderRuleAdapter;
import org.ethereum.vm.LogInfo;
import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl;
import org.junit.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
/**
* Created by Anton Nashatyrev on 29.12.2015.
*/
public class ImportLightTest {
@BeforeClass
public static void setup() {
SystemProperties.getDefault().setBlockchainConfig(StandaloneBlockchain.getEasyMiningConfig());
}
@AfterClass
public static void cleanup() {
SystemProperties.resetToDefault();
}
@Test
public void simpleFork() {
StandaloneBlockchain sb = new StandaloneBlockchain();
Block b1 = sb.createBlock();
Block b2_ = sb.createBlock();
Block b3_ = sb.createForkBlock(b2_);
Block b2 = sb.createForkBlock(b1);
Block b3 = sb.createForkBlock(b2);
Block b4 = sb.createForkBlock(b3);
Block b5 = sb.createForkBlock(b4);
}
@Test
@Ignore
public void importBlocks() throws Exception {
Logger logger = LoggerFactory.getLogger("VM");
logger.info("#######################################");
BlockchainImpl blockchain = createBlockchain(GenesisLoader.loadGenesis(
getClass().getResourceAsStream("/genesis/frontier.json")));
Scanner scanner = new Scanner(new FileInputStream("D:\\ws\\ethereumj\\work\\blocks-rec.dmp"));
while (scanner.hasNext()) {
String blockHex = scanner.next();
Block block = new Block(Hex.decode(blockHex));
ImportResult result = blockchain.tryToConnect(block);
if (result != ImportResult.EXIST && result != ImportResult.IMPORTED_BEST) {
throw new RuntimeException(result + ": " + block + "");
}
System.out.println("Imported " + block.getShortDescr());
}
}
@Ignore // periodically get different roots ?
@Test
public void putZeroValue() {
StandaloneBlockchain sb = new StandaloneBlockchain();
SolidityContract a = sb.submitNewContract("contract A { uint public a; function set() { a = 0;}}");
a.callFunction("set");
Block block = sb.createBlock();
System.out.println(Hex.toHexString(block.getStateRoot()));
Assert.assertEquals("cad42169cafc7855c25b8889df83faf38e493fb6e95b2c9c8e155dbc340160d6", Hex.toHexString(block.getStateRoot()));
}
@Test
public void simpleRebranch() {
StandaloneBlockchain sb = new StandaloneBlockchain();
Block b0 = sb.getBlockchain().getBestBlock();
ECKey addr1 = ECKey.fromPrivate(HashUtil.sha3("1".getBytes()));
BigInteger bal2 = sb.getBlockchain().getRepository().getBalance(sb.getSender().getAddress());
sb.sendEther(addr1.getAddress(), BigInteger.valueOf(100));
Block b1 = sb.createBlock();
sb.sendEther(addr1.getAddress(), BigInteger.valueOf(100));
Block b2 = sb.createBlock();
sb.sendEther(addr1.getAddress(), BigInteger.valueOf(100));
Block b3 = sb.createBlock();
BigInteger bal1 = sb.getBlockchain().getRepository().getBalance(addr1.getAddress());
Assert.assertEquals(BigInteger.valueOf(300), bal1);
sb.sendEther(addr1.getAddress(), BigInteger.valueOf(200));
Block b1_ = sb.createForkBlock(b0);
sb.sendEther(addr1.getAddress(), BigInteger.valueOf(200));
Block b2_ = sb.createForkBlock(b1_);
sb.sendEther(addr1.getAddress(), BigInteger.valueOf(200));
Block b3_ = sb.createForkBlock(b2_);
sb.sendEther(addr1.getAddress(), BigInteger.valueOf(200));
Block b4_ = sb.createForkBlock(b3_);
BigInteger bal1_ = sb.getBlockchain().getRepository().getBalance(addr1.getAddress());
Assert.assertEquals(BigInteger.valueOf(800), bal1_);
// BigInteger bal2_ = sb.getBlockchain().getRepository().getBalance(sb.getSender().getAddress());
// Assert.assertEquals(bal2, bal2_);
}
@Test
public void createFork() throws Exception {
// importing forked chain
BlockchainImpl blockchain = createBlockchain(GenesisLoader.loadGenesis(
getClass().getResourceAsStream("/genesis/genesis-light.json")));
blockchain.setMinerCoinbase(Hex.decode("ee0250c19ad59305b2bdb61f34b45b72fe37154f"));
Block parent = blockchain.getBestBlock();
System.out.println("Mining #1 ...");
Block b1 = blockchain.createNewBlock(parent, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b1.getNumber()).mineLight(b1).get();
ImportResult importResult = blockchain.tryToConnect(b1);
System.out.println("Best: " + blockchain.getBestBlock().getShortDescr());
Assert.assertTrue(importResult == ImportResult.IMPORTED_BEST);
System.out.println("Mining #2 ...");
Block b2 = blockchain.createNewBlock(b1, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b2.getNumber()).mineLight(b2).get();
importResult = blockchain.tryToConnect(b2);
System.out.println("Best: " + blockchain.getBestBlock().getShortDescr());
Assert.assertTrue(importResult == ImportResult.IMPORTED_BEST);
System.out.println("Mining #3 ...");
Block b3 = blockchain.createNewBlock(b2, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b3.getNumber()).mineLight(b3).get();
importResult = blockchain.tryToConnect(b3);
System.out.println("Best: " + blockchain.getBestBlock().getShortDescr());
Assert.assertTrue(importResult == ImportResult.IMPORTED_BEST);
System.out.println("Mining #2' ...");
Block b2_ = blockchain.createNewBlock(b1, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
b2_.setExtraData(new byte[]{77, 77}); // setting extra data to differ from block #2
Ethash.getForBlock(SystemProperties.getDefault(), b2_.getNumber()).mineLight(b2_).get();
importResult = blockchain.tryToConnect(b2_);
System.out.println("Best: " + blockchain.getBestBlock().getShortDescr());
Assert.assertTrue(importResult == ImportResult.IMPORTED_NOT_BEST);
System.out.println("Mining #3' ...");
Block b3_ = blockchain.createNewBlock(b2_, Collections.EMPTY_LIST, Collections.singletonList(b2.getHeader()));
Ethash.getForBlock(SystemProperties.getDefault(), b3_.getNumber()).mineLight(b3_).get();
importResult = blockchain.tryToConnect(b3_);
System.out.println("Best: " + blockchain.getBestBlock().getShortDescr());
Assert.assertTrue(importResult == ImportResult.IMPORTED_NOT_BEST);
}
@Test
public void invalidBlockTest() throws Exception {
// testing that bad block import effort doesn't affect the repository state
BlockchainImpl blockchain = createBlockchain(GenesisLoader.loadGenesis(
getClass().getResourceAsStream("/genesis/genesis-light.json")));
blockchain.setMinerCoinbase(Hex.decode("ee0250c19ad59305b2bdb61f34b45b72fe37154f"));
Block parent = blockchain.getBestBlock();
ECKey senderKey = ECKey.fromPrivate(Hex.decode("3ec771c31cac8c0dba77a69e503765701d3c2bb62435888d4ffa38fed60c445c"));
byte[] receiverAddr = Hex.decode("31e2e1ed11951c7091dfba62cd4b7145e947219c");
System.out.println("Mining #1 ...");
Transaction tx = new Transaction(ByteUtil.intToBytesNoLeadZeroes(0),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{77}, new byte[0]);
tx.sign(senderKey.getPrivKeyBytes());
Block b1bad = blockchain.createNewBlock(parent, Collections.singletonList(tx), Collections.EMPTY_LIST);
// making the block bad
b1bad.getStateRoot()[0] = 0;
b1bad.setStateRoot(b1bad.getStateRoot()); // invalidate block
Ethash.getForBlock(SystemProperties.getDefault(), b1bad.getNumber()).mineLight(b1bad).get();
ImportResult importResult = blockchain.tryToConnect(b1bad);
Assert.assertTrue(importResult == ImportResult.INVALID_BLOCK);
Block b1 = blockchain.createNewBlock(parent, Collections.singletonList(tx), Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b1.getNumber()).mineLight(b1).get();
importResult = blockchain.tryToConnect(b1);
Assert.assertTrue(importResult == ImportResult.IMPORTED_BEST);
}
@Test
public void doubleTransactionTest() throws Exception {
// Testing that blocks containing tx with invalid nonce are rejected
BlockchainImpl blockchain = createBlockchain(GenesisLoader.loadGenesis(
getClass().getResourceAsStream("/genesis/genesis-light.json")));
blockchain.setMinerCoinbase(Hex.decode("ee0250c19ad59305b2bdb61f34b45b72fe37154f"));
Block parent = blockchain.getBestBlock();
ECKey senderKey = ECKey.fromPrivate(Hex.decode("3ec771c31cac8c0dba77a69e503765701d3c2bb62435888d4ffa38fed60c445c"));
byte[] receiverAddr = Hex.decode("31e2e1ed11951c7091dfba62cd4b7145e947219c");
System.out.println("Mining #1 ...");
Transaction tx = new Transaction(ByteUtil.intToBytesNoLeadZeroes(0),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{77}, new byte[0]);
tx.sign(senderKey);
Block b1 = blockchain.createNewBlock(parent, Collections.singletonList(tx), Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b1.getNumber()).mineLight(b1).get();
ImportResult importResult = blockchain.tryToConnect(b1);
Assert.assertTrue(importResult == ImportResult.IMPORTED_BEST);
System.out.println("Mining #2 (bad) ...");
Block b2 = blockchain.createNewBlock(b1, Collections.singletonList(tx), Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b2.getNumber()).mineLight(b2).get();
importResult = blockchain.tryToConnect(b2);
Assert.assertTrue(importResult == ImportResult.INVALID_BLOCK);
System.out.println("Mining #2 (bad) ...");
Transaction tx1 = new Transaction(ByteUtil.intToBytesNoLeadZeroes(1),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{77}, new byte[0]);
tx1.sign(senderKey);
b2 = blockchain.createNewBlock(b1, Arrays.asList(tx1, tx1), Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b2.getNumber()).mineLight(b2).get();
importResult = blockchain.tryToConnect(b2);
Assert.assertTrue(importResult == ImportResult.INVALID_BLOCK);
System.out.println("Mining #2 ...");
Transaction tx2 = new Transaction(ByteUtil.intToBytesNoLeadZeroes(2),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{77}, new byte[0]);
tx2.sign(senderKey);
b2 = blockchain.createNewBlock(b1, Arrays.asList(tx1, tx2), Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b2.getNumber()).mineLight(b2).get();
importResult = blockchain.tryToConnect(b2);
Assert.assertTrue(importResult == ImportResult.IMPORTED_BEST);
System.out.println("Mining #2 (fork) ...");
tx1 = new Transaction(ByteUtil.intToBytesNoLeadZeroes(1),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{88}, new byte[0]);
tx1.sign(senderKey);
Block b2f = blockchain.createNewBlock(b1, Collections.singletonList(tx1), Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b2f.getNumber()).mineLight(b2f).get();
importResult = blockchain.tryToConnect(b2f);
Assert.assertTrue(importResult == ImportResult.IMPORTED_NOT_BEST);
System.out.println("Mining #3 ...");
tx1 = new Transaction(ByteUtil.intToBytesNoLeadZeroes(3),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{88}, new byte[0]);
tx1.sign(senderKey);
tx2 = new Transaction(ByteUtil.intToBytesNoLeadZeroes(4),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{88}, new byte[0]);
tx2.sign(senderKey);
Transaction tx3 = new Transaction(ByteUtil.intToBytesNoLeadZeroes(5),
ByteUtil.longToBytesNoLeadZeroes(50_000_000_000L),
ByteUtil.longToBytesNoLeadZeroes(0xfffff),
receiverAddr, new byte[]{88}, new byte[0]);
tx3.sign(senderKey);
Block b3 = blockchain.createNewBlock(b2, Arrays.asList(tx1, tx2, tx3), Collections.EMPTY_LIST);
Ethash.getForBlock(SystemProperties.getDefault(), b3.getNumber()).mineLight(b3).get();
importResult = blockchain.tryToConnect(b3);
Assert.assertTrue(importResult == ImportResult.IMPORTED_BEST);
}
@Test
public void invalidBlockTotalDiff() throws Exception {
// Check that importing invalid block doesn't affect totalDifficulty
BlockchainImpl blockchain = createBlockchain(GenesisLoader.loadGenesis(
getClass().getResourceAsStream("/genesis/genesis-light.json")));
blockchain.setMinerCoinbase(Hex.decode("ee0250c19ad59305b2bdb61f34b45b72fe37154f"));
Block parent = blockchain.getBestBlock();
System.out.println("Mining #1 ...");
BigInteger totalDifficulty = blockchain.getTotalDifficulty();
Block b1 = blockchain.createNewBlock(parent, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
b1.setStateRoot(new byte[32]);
Ethash.getForBlock(SystemProperties.getDefault(), b1.getNumber()).mineLight(b1).get();
ImportResult importResult = blockchain.tryToConnect(b1);
Assert.assertTrue(importResult == ImportResult.INVALID_BLOCK);
Assert.assertEquals(totalDifficulty, blockchain.getTotalDifficulty());
}
@Test
public void simpleDbTest() {
StandaloneBlockchain bc = new StandaloneBlockchain();
SolidityContract parent = bc.submitNewContract("contract A {" +
" uint public a;" +
" function set(uint a_) { a = a_;}" +
"}");
bc.createBlock();
parent.callFunction("set", 123);
bc.createBlock();
Object ret = parent.callConstFunction("a")[0];
System.out.println("Ret = " + ret);
}
@Test
public void createContractFork() throws Exception {
// #1 (Parent) --> #2 --> #3 (Child) ----------------------> #4 (call Child)
// \-------------------------------> #2' (forked Child)
//
// Testing the situation when the Child contract is created by the Parent contract
// first on the main chain with one parameter (#3) and then on the fork with another parameter (#2')
// so their storages are different. Check that original Child storage is not broken
// on the main chain (#4)
String contractSrc =
"contract Child {" +
" int a;" +
" int b;" +
" int public c;" +
" function Child(int i) {" +
" a = 333 + i;" +
" b = 444 + i;" +
" }" +
" function sum() {" +
" c = a + b;" +
" }" +
"}" +
"contract Parent {" +
" address public child;" +
" function createChild(int a) returns (address) {" +
" child = new Child(a);" +
" return child;" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain();
SolidityContract parent = bc.submitNewContract(contractSrc, "Parent");
Block b1 = bc.createBlock();
Block b2 = bc.createBlock();
parent.callFunction("createChild", 100);
Block b3 = bc.createBlock();
byte[] childAddress = (byte[]) parent.callConstFunction("child")[0];
parent.callFunction("createChild", 200);
Block b2_ = bc.createForkBlock(b1);
SolidityContract child = bc.createExistingContractFromSrc(contractSrc, "Child", childAddress);
child.callFunction("sum");
Block b4 = bc.createBlock();
Assert.assertEquals(BigInteger.valueOf(100 + 333 + 100 + 444), child.callConstFunction("c")[0]);
}
@Test
public void createContractFork1() throws Exception {
// Test creation of the contract on forked branch with different storage
String contractSrc =
"contract A {" +
" int public a;" +
" function A() {" +
" a = 333;" +
" }" +
"}" +
"contract B {" +
" int public a;" +
" function B() {" +
" a = 111;" +
" }" +
"}";
{
StandaloneBlockchain bc = new StandaloneBlockchain();
Block b1 = bc.createBlock();
Block b2 = bc.createBlock();
SolidityContract a = bc.submitNewContract(contractSrc, "A");
Block b3 = bc.createBlock();
SolidityContract b = bc.submitNewContract(contractSrc, "B");
Block b2_ = bc.createForkBlock(b1);
Assert.assertEquals(BigInteger.valueOf(333), a.callConstFunction("a")[0]);
Assert.assertEquals(BigInteger.valueOf(111), b.callConstFunction(b2_, "a")[0]);
Block b3_ = bc.createForkBlock(b2_);
Block b4_ = bc.createForkBlock(b3_);
Assert.assertEquals(BigInteger.valueOf(111), a.callConstFunction("a")[0]);
Assert.assertEquals(BigInteger.valueOf(333), a.callConstFunction(b3, "a")[0]);
}
}
@Test
public void createValueTest() throws IOException, InterruptedException {
// checks that correct msg.value is passed when contract internally created with value
String contract =
"pragma solidity ^0.4.3;\n" +
"contract B {\n" +
" uint public valReceived;\n" +
" \n" +
" function B() payable {\n" +
" valReceived = msg.value;\n" +
" }\n" +
"}\n" +
"contract A {\n" +
" function () payable { }\n" +
" address public child;\n" +
" function create() payable {\n" +
" child = (new B).value(20)();\n" +
" }\n" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain().withAutoblock(true);
SolidityContract a = bc.submitNewContract(contract, "A");
bc.sendEther(a.getAddress(), BigInteger.valueOf(10_000));
a.callFunction(10, "create");
byte[] childAddress = (byte[]) a.callConstFunction("child")[0];
SolidityContract b = bc.createExistingContractFromSrc(contract, "B", childAddress);
BigInteger val = (BigInteger) b.callConstFunction("valReceived")[0];
Assert.assertEquals(20, val.longValue());
}
@Test
public void contractCodeForkTest() throws IOException, InterruptedException {
String contractA =
"contract A {" +
" function call() returns (uint) {" +
" return 111;" +
" }" +
"}";
String contractB =
"contract B {" +
" function call() returns (uint) {" +
" return 222222;" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain();
Block b1 = bc.createBlock();
SolidityContract a = bc.submitNewContract(contractA);
Block b2 = bc.createBlock();
Assert.assertEquals(BigInteger.valueOf(111), a.callConstFunction("call")[0]);
SolidityContract b = bc.submitNewContract(contractB);
Block b2_ = bc.createForkBlock(b1);
Block b3 = bc.createForkBlock(b2);
Assert.assertEquals(BigInteger.valueOf(111), a.callConstFunction("call")[0]);
Assert.assertEquals(BigInteger.valueOf(111), a.callConstFunction(b2, "call")[0]);
Assert.assertEquals(BigInteger.valueOf(222222), b.callConstFunction(b2_, "call")[0]);
}
@Test
public void operateNotExistingContractTest() throws IOException, InterruptedException {
// checking that addr.balance doesn't cause the account to be created
// and the subsequent call to that non-existent address costs 25K gas
byte[] addr = Hex.decode("0101010101010101010101010101010101010101");
String contractA =
"pragma solidity ^0.4.3;" +
"contract B { function dummy() {}}" +
"contract A {" +
" function callBalance() returns (uint) {" +
" address addr = 0x" + Hex.toHexString(addr) + ";" +
" uint bal = addr.balance;" +
" }" +
" function callMethod() returns (uint) {" +
" address addr = 0x" + Hex.toHexString(addr) + ";" +
" B b = B(addr);" +
" b.dummy();" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain()
.withGasPrice(1)
.withGasLimit(5_000_000L);
SolidityContract a = bc.submitNewContract(contractA, "A");
bc.createBlock();
{
BigInteger balance1 = getSenderBalance(bc);
a.callFunction("callBalance");
bc.createBlock();
BigInteger balance2 = getSenderBalance(bc);
long spent = balance1.subtract(balance2).longValue();
// checking balance of not existed address should take
// less that gas limit
Assert.assertEquals(21508, spent);
}
{
BigInteger balance1 = getSenderBalance(bc);
a.callFunction("callMethod");
bc.createBlock();
BigInteger balance2 = getSenderBalance(bc);
long spent = balance1.subtract(balance2).longValue();
// invalid jump error occurred
// all gas wasted
// (for history: it is worked fine in ^0.3.1)
Assert.assertEquals(5_000_000L, spent);
}
}
private BigInteger getSenderBalance(StandaloneBlockchain bc) {
return bc.getBlockchain().getRepository().getBalance(bc.getSender().getAddress());
}
@Test
public void spendGasSimpleTest() throws IOException, InterruptedException {
// check the caller spend value for tx
StandaloneBlockchain bc = new StandaloneBlockchain().withGasPrice(1);
BigInteger balance1 = bc.getBlockchain().getRepository().getBalance(bc.getSender().getAddress());
bc.sendEther(new byte[20], BigInteger.ZERO);
bc.createBlock();
BigInteger balance2 = bc.getBlockchain().getRepository().getBalance(bc.getSender().getAddress());
long spent = balance1.subtract(balance2).longValue();
Assert.assertNotEquals(0, spent);
}
@Test
public void deepRecursionTest() throws Exception {
String contractA =
"contract A {" +
" function recursive(){" +
" this.recursive();" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain().withGasLimit(5_000_000);
SolidityContract a = bc.submitNewContract(contractA, "A");
bc.createBlock();
a.callFunction("recursive");
bc.createBlock();
// no StackOverflowException
}
@Test
public void prevBlockHashOnFork() throws Exception {
String contractA =
"contract A {" +
" bytes32 public blockHash;" +
" function a(){" +
" blockHash = block.blockhash(block.number - 1);" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain();
SolidityContract a = bc.submitNewContract(contractA);
Block b1 = bc.createBlock();
Block b2 = bc.createBlock();
Block b3 = bc.createBlock();
Block b4 = bc.createBlock();
Block b5 = bc.createBlock();
Block b6 = bc.createBlock();
Block b2_ = bc.createForkBlock(b1);
a.callFunction("a");
Block b3_ = bc.createForkBlock(b2_);
Object hash = a.callConstFunction(b3_, "blockHash")[0];
Assert.assertArrayEquals((byte[]) hash, b2_.getHash());
// no StackOverflowException
}
@Test
public void rollbackInternalTx() throws Exception {
String contractA =
"contract A {" +
" uint public a;" +
" uint public b;" +
" function f() {" +
" b = 1;" +
" this.call(bytes4(sha3('exception()')));" +
" a = 2;" +
" }" +
" function exception() {" +
" b = 2;" +
" throw;" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain();
SolidityContract a = bc.submitNewContract(contractA);
bc.createBlock();
a.callFunction("f");
bc.createBlock();
Object av = a.callConstFunction("a")[0];
Object bv = a.callConstFunction("b")[0];
assert BigInteger.valueOf(2).equals(av);
assert BigInteger.valueOf(1).equals(bv);
}
@Test()
public void selfdestructAttack() throws Exception {
String contractSrc = "" +
"pragma solidity ^0.4.3;" +
"contract B {" +
" function suicide(address benefic) {" +
" selfdestruct(benefic);" +
" }" +
"}" +
"contract A {" +
" uint public a;" +
" function f() {" +
" B b = new B();" +
" for (uint i = 0; i < 3500; i++) {" +
" b.suicide(address(i));" +
" }" +
" a = 2;" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain()
.withGasLimit(1_000_000_000L)
.withDbDelay(0);
SolidityContract a = bc.submitNewContract(contractSrc, "A");
bc.createBlock();
a.callFunction("f");
bc.createBlock();
String stateRoot = Hex.toHexString(bc.getBlockchain().getRepository().getRoot());
// Assert.assertEquals("82d5bdb6531e26011521da5601481c9dbef326aa18385f2945fd77bee288ca31", stateRoot);
Object av = a.callConstFunction("a")[0];
assert BigInteger.valueOf(2).equals(av);
assert bc.getTotalDbHits() < 8300; // reduce this assertion if you make further optimizations
}
@Test
@Ignore
public void threadRacePendingTest() throws Exception {
String contractA =
"contract A {" +
" uint[32] public somedata1;" +
" uint[32] public somedata2;" +
" function set1(uint idx, uint val){" +
" somedata1[idx] = val;" +
" }" +
" function set2(uint idx, uint val){" +
" somedata2[idx] = val;" +
" }" +
"}";
final StandaloneBlockchain bc = new StandaloneBlockchain();
final StandaloneBlockchain.SolidityContractImpl a = (StandaloneBlockchain.SolidityContractImpl) bc.submitNewContract(contractA);
bc.createBlock();
Block b = null;
int cnt = 1;
final CallTransaction.Function function = a.contract.getByName("set");
new Thread(new Runnable() {
@Override
public void run() {
int cnt = 1;
while (cnt++ > 0) {
try {
bc.generatePendingTransactions();
// byte[] encode = function.encode(cnt % 32, cnt);
// Transaction callTx1 = bc.createTransaction(new ECKey(), 0, a.getAddress(), BigInteger.ZERO, encode);
// bc.getPendingState().addPendingTransaction(callTx1);
// Transaction callTx2 = bc.createTransaction(, 0, a.getAddress(), BigInteger.ZERO, encode);
// bc.getPendingState().addPendingTransaction(callTx);
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
Block b_1 = null;
while(cnt++ > 0) {
long s = System.nanoTime();
a.callFunction("set1", cnt % 32, cnt);
a.callFunction("set2", cnt % 32, cnt);
bc.sendEther(new byte[32], BigInteger.ONE);
a.callFunction("set1", (cnt + 1) % 32, cnt + 1);
a.callFunction("set2", (cnt + 1) % 32, cnt + 1);
bc.sendEther(new byte[32], BigInteger.ONE);
Block prev = b;
if (cnt % 5 == 0) {
b = bc.createForkBlock(b_1);
} else {
b = bc.createBlock();
}
b_1 = prev;
if (cnt % 3 == 0) {
bc.getBlockchain().flush();
}
long t = System.nanoTime() - s;
System.out.println("" + String.format(Locale.US, "%1$.2f", t / 1_000_000d) + ", " + b.getDifficultyBI() + ", " + b.getShortDescr());
}
// SolidityContract a = bc.submitNewContract(contractA);
// Block b1 = bc.createBlock();
// Block b2 = bc.createBlock();
// Block b3 = bc.createBlock();
// Block b4 = bc.createBlock();
// Block b5 = bc.createBlock();
// Block b6 = bc.createBlock();
// Block b2_ = bc.createForkBlock(b1);
// a.callFunction("a");
// Block b3_ = bc.createForkBlock(b2_);
// Object hash = a.callConstFunction(b3_, "blockHash")[0];
//
// System.out.println(Hex.toHexString((byte[]) hash));
// System.out.println(Hex.toHexString(b2_.getHash()));
// no StackOverflowException
}
@Test
public void suicideInFailedCall() throws Exception {
// check that if a contract is suicide in call which is failed (thus suicide is reverted)
// the refund for this suicide is not added
String contractA =
"contract B {" +
" function f(){" +
" suicide(msg.sender);" +
" }" +
"}" +
"contract A {" +
" function f(){" +
" this.call(bytes4(sha3('bad()')));" +
" }" +
" function bad() {" +
" B b = new B();" +
" b.call(bytes4(sha3('f()')));" +
" throw;" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain().withGasLimit(5_000_000);
SolidityContract a = bc.submitNewContract(contractA, "A");
bc.createBlock();
final BigInteger[] refund = new BigInteger[1];
bc.addEthereumListener(new EthereumListenerAdapter() {
@Override
public void onTransactionExecuted(TransactionExecutionSummary summary) {
refund[0] = summary.getGasRefund();
}
});
a.callFunction("f");
bc.createBlock();
Assert.assertEquals(BigInteger.ZERO, refund[0]);
// no StackOverflowException
}
@Test
public void logInFailedCall() throws Exception {
// check that if a contract is suicide in call which is failed (thus suicide is reverted)
// the refund for this suicide is not added
String contractA =
"contract A {" +
" function f(){" +
" this.call(bytes4(sha3('bad()')));" +
" }" +
" function bad() {" +
" log0(1234);" +
" throw;" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain().withGasLimit(5_000_000);
SolidityContract a = bc.submitNewContract(contractA, "A");
bc.createBlock();
final List<LogInfo> logs = new ArrayList<>();
bc.addEthereumListener(new EthereumListenerAdapter() {
@Override
public void onTransactionExecuted(TransactionExecutionSummary summary) {
logs.addAll(summary.getLogs());
}
});
a.callFunction("f");
bc.createBlock();
Assert.assertEquals(0, logs.size());
// no StackOverflowException
}
@Test
public void ecRecoverTest() throws Exception {
// checks that ecrecover precompile contract rejects v > 255
String contractA =
"contract A {" +
" function f (bytes32 hash, bytes32 v, bytes32 r, bytes32 s) returns (address) {" +
" assembly {" +
" mstore(0x100, hash)" +
" mstore(0x120, v)" +
" mstore(0x140, r)" +
" mstore(0x160, s)" +
" callcode(0x50000, 0x01, 0x0, 0x100, 0x80, 0x200, 0x220)" + // call ecrecover
" return(0x200, 0x20)" +
" }" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain().withGasLimit(5_000_000);
SolidityContract a = bc.submitNewContract(contractA, "A");
bc.createBlock();
ECKey key = ECKey.fromPrivate(BigInteger.ONE);
byte[] hash = new byte[32];
ECKey.ECDSASignature signature = key.sign(hash);
Object[] ret = a.callConstFunction("f", hash,
ByteUtil.merge(new byte[31], new byte[]{signature.v}),
ByteUtil.bigIntegerToBytes(signature.r, 32),
ByteUtil.bigIntegerToBytes(signature.s, 32));
Assert.assertArrayEquals(key.getAddress(), (byte[]) ret[0]);
ret = a.callConstFunction("f", hash,
ByteUtil.merge(new byte[] {1}, new byte[30], new byte[]{signature.v}),
ByteUtil.bigIntegerToBytes(signature.r, 32),
ByteUtil.bigIntegerToBytes(signature.s, 32));
Assert.assertArrayEquals(new byte[20], (byte[]) ret[0]);
}
@Test
public void functionTypeTest() throws IOException, InterruptedException {
String contractA =
"contract A {" +
" int public res;" +
" function calc(int b, function (int a) external returns (int) f) external returns (int) {" +
" return f(b);" +
" }" +
" function fInc(int a) external returns (int) { return a + 1;}" +
" function fDec(int a) external returns (int) { return a - 1;}" +
" function test() {" +
" res = this.calc(111, this.fInc);" +
" }" +
"}";
StandaloneBlockchain bc = new StandaloneBlockchain();
SolidityContract a = bc.submitNewContract(contractA);
bc.createBlock();
a.callFunction("test");
bc.createBlock();
Assert.assertEquals(a.callConstFunction("res")[0], BigInteger.valueOf(112));
BigInteger r1 = (BigInteger) a.callConstFunction("calc", 222, a.getFunction("fInc"))[0];
Assert.assertEquals(223, r1.intValue());
BigInteger r2 = (BigInteger) a.callConstFunction("calc", 222, a.getFunction("fDec"))[0];
Assert.assertEquals(221, r2.intValue());
}
public static BlockchainImpl createBlockchain(Genesis genesis) {
IndexedBlockStore blockStore = new IndexedBlockStore();
blockStore.init(new HashMapDB<byte[]>(), new HashMapDB<byte[]>());
RepositoryRoot repository = new RepositoryRoot(new NoDeleteSource<>(new HashMapDB<byte[]>()));
ProgramInvokeFactoryImpl programInvokeFactory = new ProgramInvokeFactoryImpl();
EthereumListenerAdapter listener = new EthereumListenerAdapter();
BlockchainImpl blockchain = new BlockchainImpl(blockStore, repository)
.withParentBlockHeaderValidator(new CommonConfig().parentHeaderValidator());
blockchain.setParentHeaderValidator(new DependentBlockHeaderRuleAdapter());
blockchain.setProgramInvokeFactory(programInvokeFactory);
blockchain.byTest = true;
PendingStateImpl pendingState = new PendingStateImpl(listener, blockchain);
pendingState.setBlockchain(blockchain);
blockchain.setPendingState(pendingState);
Repository track = repository.startTracking();
Genesis.populateRepository(track, genesis);
track.commit();
repository.commit();
blockStore.saveBlock(genesis, genesis.getCumulativeDifficulty(), true);
blockchain.setBestBlock(genesis);
blockchain.setTotalDifficulty(genesis.getCumulativeDifficulty());
return blockchain;
}
}