Fix BLOCKHASH op new rule
The blockhash retrieved by index on one of the recent 256 blocks. If the index is out of that window zero pushed into the stack as a result
This commit is contained in:
parent
84b1cef682
commit
59833f3fd6
|
@ -1,6 +1,6 @@
|
||||||
package org.ethereum.core;
|
package org.ethereum.core;
|
||||||
|
|
||||||
import org.ethereum.db.BlockStore;
|
import org.ethereum.db.BlockStoreImpl;
|
||||||
import org.ethereum.facade.Blockchain;
|
import org.ethereum.facade.Blockchain;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
import org.ethereum.listener.EthereumListener;
|
import org.ethereum.listener.EthereumListener;
|
||||||
|
@ -79,7 +79,7 @@ public class BlockchainImpl implements Blockchain {
|
||||||
private Repository track;
|
private Repository track;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private BlockStore blockStore;
|
private BlockStoreImpl blockStore;
|
||||||
|
|
||||||
private Block bestBlock;
|
private Block bestBlock;
|
||||||
private BigInteger totalDifficulty = BigInteger.ZERO;
|
private BigInteger totalDifficulty = BigInteger.ZERO;
|
||||||
|
@ -337,7 +337,8 @@ public class BlockchainImpl implements Blockchain {
|
||||||
for (Transaction tx : block.getTransactionsList()) {
|
for (Transaction tx : block.getTransactionsList()) {
|
||||||
stateLogger.info("apply block: [{}] tx: [{}] ", block.getNumber(), i);
|
stateLogger.info("apply block: [{}] tx: [{}] ", block.getNumber(), i);
|
||||||
|
|
||||||
TransactionExecutor executor = new TransactionExecutor(tx, block.getCoinbase(), track,
|
TransactionExecutor executor = new TransactionExecutor(tx, block.getCoinbase(),
|
||||||
|
track, blockStore,
|
||||||
programInvokeFactory, block);
|
programInvokeFactory, block);
|
||||||
executor.execute();
|
executor.execute();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.ethereum.core;
|
package org.ethereum.core;
|
||||||
|
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
import org.ethereum.vm.DataWord;
|
import org.ethereum.vm.DataWord;
|
||||||
import org.ethereum.vm.GasCost;
|
import org.ethereum.vm.GasCost;
|
||||||
|
@ -33,6 +34,8 @@ public class TransactionExecutor {
|
||||||
|
|
||||||
private Transaction tx;
|
private Transaction tx;
|
||||||
private Repository track;
|
private Repository track;
|
||||||
|
private BlockStore blockStore;
|
||||||
|
|
||||||
private ProgramInvokeFactory programInvokeFactory;
|
private ProgramInvokeFactory programInvokeFactory;
|
||||||
private byte[] coinbase;
|
private byte[] coinbase;
|
||||||
|
|
||||||
|
@ -41,12 +44,13 @@ public class TransactionExecutor {
|
||||||
private Block currentBlock;
|
private Block currentBlock;
|
||||||
|
|
||||||
|
|
||||||
public TransactionExecutor(Transaction tx, byte[] coinbase, Repository track,
|
public TransactionExecutor(Transaction tx, byte[] coinbase, Repository track,BlockStore blockStore,
|
||||||
ProgramInvokeFactory programInvokeFactory, Block currentBlock) {
|
ProgramInvokeFactory programInvokeFactory, Block currentBlock) {
|
||||||
|
|
||||||
this.tx = tx;
|
this.tx = tx;
|
||||||
this.coinbase = coinbase;
|
this.coinbase = coinbase;
|
||||||
this.track = track;
|
this.track = track;
|
||||||
|
this.blockStore = blockStore;
|
||||||
this.programInvokeFactory = programInvokeFactory;
|
this.programInvokeFactory = programInvokeFactory;
|
||||||
this.currentBlock = currentBlock;
|
this.currentBlock = currentBlock;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +184,7 @@ public class TransactionExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramInvoke programInvoke =
|
ProgramInvoke programInvoke =
|
||||||
programInvokeFactory.createProgramInvoke(tx, currentBlock, trackTx);
|
programInvokeFactory.createProgramInvoke(tx, currentBlock, trackTx, blockStore);
|
||||||
|
|
||||||
VM vm = new VM();
|
VM vm = new VM();
|
||||||
Program program = new Program(code, programInvoke);
|
Program program = new Program(code, programInvoke);
|
||||||
|
|
|
@ -1,179 +1,13 @@
|
||||||
package org.ethereum.db;
|
package org.ethereum.db;
|
||||||
|
|
||||||
import org.ethereum.core.Block;
|
import org.ethereum.core.Block;
|
||||||
import org.ethereum.core.TransactionReceipt;
|
|
||||||
|
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Roman Mandeleil
|
* @author: Roman Mandeleil
|
||||||
* @since 12.11.2014
|
* Created on: 08/01/2015 10:27
|
||||||
*/
|
*/
|
||||||
@Repository
|
|
||||||
@Transactional(propagation = Propagation.SUPPORTS)
|
|
||||||
public class BlockStore {
|
|
||||||
|
|
||||||
@Autowired
|
public interface BlockStore {
|
||||||
private SessionFactory sessionFactory;
|
|
||||||
|
|
||||||
@Autowired
|
public byte[] getBlockHashByNumber(long blockNumber);
|
||||||
ApplicationContext ctx;
|
|
||||||
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Block getBlockByNumber(long blockNumber) {
|
|
||||||
|
|
||||||
List result = sessionFactory.getCurrentSession().
|
|
||||||
createQuery("from BlockVO where number = :number").
|
|
||||||
setParameter("number", blockNumber).list();
|
|
||||||
|
|
||||||
if (result.size() == 0) return null;
|
|
||||||
BlockVO vo = (BlockVO) result.get(0);
|
|
||||||
|
|
||||||
return new Block(vo.rlp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Block getBlockByHash(byte[] hash) {
|
|
||||||
|
|
||||||
List result = sessionFactory.getCurrentSession().
|
|
||||||
createQuery("from BlockVO where hash = :hash").
|
|
||||||
setParameter("hash", hash).list();
|
|
||||||
|
|
||||||
if (result.size() == 0) return null;
|
|
||||||
BlockVO vo = (BlockVO) result.get(0);
|
|
||||||
|
|
||||||
return new Block(vo.rlp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public List<byte[]> getListOfHashesStartFrom(byte[] hash, int qty) {
|
|
||||||
|
|
||||||
List<byte[]> hashes = new ArrayList<>();
|
|
||||||
|
|
||||||
// find block number of that block hash
|
|
||||||
Block block = getBlockByHash(hash);
|
|
||||||
if (block == null) return hashes;
|
|
||||||
|
|
||||||
List<byte[]> result = sessionFactory.getCurrentSession().
|
|
||||||
createQuery("select hash from BlockVO where number <= :number and number >= :limit order by number desc").
|
|
||||||
setParameter("number", block.getNumber()).
|
|
||||||
setParameter("limit", block.getNumber() - qty).
|
|
||||||
setMaxResults(qty).list();
|
|
||||||
|
|
||||||
for (byte[] h : result)
|
|
||||||
hashes.add(h);
|
|
||||||
|
|
||||||
return hashes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public void deleteBlocksSince(long number) {
|
|
||||||
|
|
||||||
sessionFactory.getCurrentSession().
|
|
||||||
createQuery("delete from BlockVO where number > :number").
|
|
||||||
setParameter("number", number).
|
|
||||||
executeUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public void saveBlock(Block block, List<TransactionReceipt> receipts) {
|
|
||||||
|
|
||||||
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(),
|
|
||||||
block.getEncoded(), block.getCumulativeDifficulty());
|
|
||||||
|
|
||||||
for (TransactionReceipt receipt : receipts) {
|
|
||||||
|
|
||||||
byte[] hash = receipt.getTransaction().getHash();
|
|
||||||
byte[] rlp = receipt.getEncoded();
|
|
||||||
|
|
||||||
TransactionReceiptVO transactionReceiptVO = new TransactionReceiptVO(hash, rlp);
|
|
||||||
sessionFactory.getCurrentSession().persist(transactionReceiptVO);
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionFactory.getCurrentSession().persist(blockVO);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public BigInteger getTotalDifficultySince(long number) {
|
|
||||||
|
|
||||||
BigInteger result = (BigInteger) sessionFactory.getCurrentSession().
|
|
||||||
createQuery("select sum(cummulativeDifficulty) from BlockVO where number > :number").
|
|
||||||
setParameter("number", number).
|
|
||||||
uniqueResult();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public BigInteger getTotalDifficulty() {
|
|
||||||
|
|
||||||
BigInteger result = (BigInteger) sessionFactory.getCurrentSession().
|
|
||||||
createQuery("select sum(cummulativeDifficulty) from BlockVO").uniqueResult();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Block getBestBlock() {
|
|
||||||
|
|
||||||
Long bestNumber = (Long)
|
|
||||||
sessionFactory.getCurrentSession().createQuery("select max(number) from BlockVO").uniqueResult();
|
|
||||||
List result = sessionFactory.getCurrentSession().
|
|
||||||
createQuery("from BlockVO where number = :number").setParameter("number", bestNumber).list();
|
|
||||||
|
|
||||||
if (result.isEmpty()) return null;
|
|
||||||
BlockVO vo = (BlockVO) result.get(0);
|
|
||||||
|
|
||||||
return new Block(vo.rlp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public List<Block> getAllBlocks() {
|
|
||||||
|
|
||||||
List<BlockVO> result = sessionFactory.getCurrentSession().
|
|
||||||
createQuery("from BlockVO").list();
|
|
||||||
|
|
||||||
ArrayList<Block> blocks = new ArrayList<>();
|
|
||||||
for (BlockVO blockVO : result) {
|
|
||||||
blocks.add(new Block(blockVO.getRlp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public void reset() {
|
|
||||||
sessionFactory.getCurrentSession().
|
|
||||||
createQuery("delete from BlockVO").executeUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransactionReceipt getTransactionReceiptByHash(byte[] hash) {
|
|
||||||
|
|
||||||
List result = sessionFactory.getCurrentSession().
|
|
||||||
createQuery("from TransactionReceiptVO where hash = :hash").
|
|
||||||
setParameter("hash", hash).list();
|
|
||||||
|
|
||||||
if (result.size() == 0) return null;
|
|
||||||
TransactionReceiptVO vo = (TransactionReceiptVO) result.get(0);
|
|
||||||
|
|
||||||
return new TransactionReceipt(vo.rlp);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.ethereum.db;
|
||||||
|
|
||||||
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
import org.ethereum.util.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: Roman Mandeleil
|
||||||
|
* Created on: 08/01/2015 17:33
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class BlockStoreDummy implements BlockStore{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBlockHashByNumber(long blockNumber) {
|
||||||
|
|
||||||
|
byte[] data = String.valueOf(blockNumber).getBytes();
|
||||||
|
return HashUtil.sha3(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
package org.ethereum.db;
|
||||||
|
|
||||||
|
import org.ethereum.core.Block;
|
||||||
|
import org.ethereum.core.TransactionReceipt;
|
||||||
|
|
||||||
|
import org.ethereum.util.ByteUtil;
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Roman Mandeleil
|
||||||
|
* @since 12.11.2014
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
|
public class BlockStoreImpl implements BlockStore{
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SessionFactory sessionFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ApplicationContext ctx;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBlockHashByNumber(long blockNumber) {
|
||||||
|
|
||||||
|
Block block = getBlockByNumber(blockNumber);
|
||||||
|
if (block != null) return block.getHash();
|
||||||
|
return ByteUtil.EMPTY_BYTE_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Block getBlockByNumber(long blockNumber) {
|
||||||
|
|
||||||
|
List result = sessionFactory.getCurrentSession().
|
||||||
|
createQuery("from BlockVO where number = :number").
|
||||||
|
setParameter("number", blockNumber).list();
|
||||||
|
|
||||||
|
if (result.size() == 0) return null;
|
||||||
|
BlockVO vo = (BlockVO) result.get(0);
|
||||||
|
|
||||||
|
return new Block(vo.rlp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Block getBlockByHash(byte[] hash) {
|
||||||
|
|
||||||
|
List result = sessionFactory.getCurrentSession().
|
||||||
|
createQuery("from BlockVO where hash = :hash").
|
||||||
|
setParameter("hash", hash).list();
|
||||||
|
|
||||||
|
if (result.size() == 0) return null;
|
||||||
|
BlockVO vo = (BlockVO) result.get(0);
|
||||||
|
|
||||||
|
return new Block(vo.rlp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<byte[]> getListOfHashesStartFrom(byte[] hash, int qty) {
|
||||||
|
|
||||||
|
List<byte[]> hashes = new ArrayList<>();
|
||||||
|
|
||||||
|
// find block number of that block hash
|
||||||
|
Block block = getBlockByHash(hash);
|
||||||
|
if (block == null) return hashes;
|
||||||
|
|
||||||
|
List<byte[]> result = sessionFactory.getCurrentSession().
|
||||||
|
createQuery("select hash from BlockVO where number <= :number and number >= :limit order by number desc").
|
||||||
|
setParameter("number", block.getNumber()).
|
||||||
|
setParameter("limit", block.getNumber() - qty).
|
||||||
|
setMaxResults(qty).list();
|
||||||
|
|
||||||
|
for (byte[] h : result)
|
||||||
|
hashes.add(h);
|
||||||
|
|
||||||
|
return hashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteBlocksSince(long number) {
|
||||||
|
|
||||||
|
sessionFactory.getCurrentSession().
|
||||||
|
createQuery("delete from BlockVO where number > :number").
|
||||||
|
setParameter("number", number).
|
||||||
|
executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void saveBlock(Block block, List<TransactionReceipt> receipts) {
|
||||||
|
|
||||||
|
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(),
|
||||||
|
block.getEncoded(), block.getCumulativeDifficulty());
|
||||||
|
|
||||||
|
for (TransactionReceipt receipt : receipts) {
|
||||||
|
|
||||||
|
byte[] hash = receipt.getTransaction().getHash();
|
||||||
|
byte[] rlp = receipt.getEncoded();
|
||||||
|
|
||||||
|
TransactionReceiptVO transactionReceiptVO = new TransactionReceiptVO(hash, rlp);
|
||||||
|
sessionFactory.getCurrentSession().persist(transactionReceiptVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionFactory.getCurrentSession().persist(blockVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public BigInteger getTotalDifficultySince(long number) {
|
||||||
|
|
||||||
|
BigInteger result = (BigInteger) sessionFactory.getCurrentSession().
|
||||||
|
createQuery("select sum(cummulativeDifficulty) from BlockVO where number > :number").
|
||||||
|
setParameter("number", number).
|
||||||
|
uniqueResult();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public BigInteger getTotalDifficulty() {
|
||||||
|
|
||||||
|
BigInteger result = (BigInteger) sessionFactory.getCurrentSession().
|
||||||
|
createQuery("select sum(cummulativeDifficulty) from BlockVO").uniqueResult();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Block getBestBlock() {
|
||||||
|
|
||||||
|
Long bestNumber = (Long)
|
||||||
|
sessionFactory.getCurrentSession().createQuery("select max(number) from BlockVO").uniqueResult();
|
||||||
|
List result = sessionFactory.getCurrentSession().
|
||||||
|
createQuery("from BlockVO where number = :number").setParameter("number", bestNumber).list();
|
||||||
|
|
||||||
|
if (result.isEmpty()) return null;
|
||||||
|
BlockVO vo = (BlockVO) result.get(0);
|
||||||
|
|
||||||
|
return new Block(vo.rlp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<Block> getAllBlocks() {
|
||||||
|
|
||||||
|
List<BlockVO> result = sessionFactory.getCurrentSession().
|
||||||
|
createQuery("from BlockVO").list();
|
||||||
|
|
||||||
|
ArrayList<Block> blocks = new ArrayList<>();
|
||||||
|
for (BlockVO blockVO : result) {
|
||||||
|
blocks.add(new Block(blockVO.getRlp()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void reset() {
|
||||||
|
sessionFactory.getCurrentSession().
|
||||||
|
createQuery("delete from BlockVO").executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransactionReceipt getTransactionReceiptByHash(byte[] hash) {
|
||||||
|
|
||||||
|
List result = sessionFactory.getCurrentSession().
|
||||||
|
createQuery("from TransactionReceiptVO where hash = :hash").
|
||||||
|
setParameter("hash", hash).list();
|
||||||
|
|
||||||
|
if (result.size() == 0) return null;
|
||||||
|
TransactionReceiptVO vo = (TransactionReceiptVO) result.get(0);
|
||||||
|
|
||||||
|
return new TransactionReceipt(vo.rlp);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package org.ethereum.jsontestsuite;
|
||||||
|
|
||||||
import org.ethereum.core.Block;
|
import org.ethereum.core.Block;
|
||||||
import org.ethereum.core.Transaction;
|
import org.ethereum.core.Transaction;
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
import org.ethereum.util.ByteUtil;
|
import org.ethereum.util.ByteUtil;
|
||||||
import org.ethereum.vm.DataWord;
|
import org.ethereum.vm.DataWord;
|
||||||
|
@ -26,18 +27,19 @@ public class TestProgramInvokeFactory implements ProgramInvokeFactory {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository) {
|
public ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository, BlockStore blockStore) {
|
||||||
return generalInvoke(tx, repository);
|
return generalInvoke(tx, repository, blockStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, DataWord inValue, DataWord inGas,
|
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, DataWord inValue, DataWord inGas,
|
||||||
BigInteger balanceInt, byte[] dataIn, Repository repository) {
|
BigInteger balanceInt, byte[] dataIn,
|
||||||
|
Repository repository, BlockStore blockStore) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ProgramInvoke generalInvoke(Transaction tx, Repository repository) {
|
private ProgramInvoke generalInvoke(Transaction tx, Repository repository ,BlockStore blockStore) {
|
||||||
|
|
||||||
/*** ADDRESS op ***/
|
/*** ADDRESS op ***/
|
||||||
// YP: Get address of currently executing account.
|
// YP: Get address of currently executing account.
|
||||||
|
@ -88,7 +90,7 @@ public class TestProgramInvokeFactory implements ProgramInvokeFactory {
|
||||||
|
|
||||||
return new ProgramInvokeImpl(address, origin, caller, balance,
|
return new ProgramInvokeImpl(address, origin, caller, balance,
|
||||||
gasPrice, gas, callValue, data, lastHash, coinbase,
|
gasPrice, gas, callValue, data, lastHash, coinbase,
|
||||||
timestamp, number, difficulty, gaslimit, repository);
|
timestamp, number, difficulty, gaslimit, repository, blockStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.ethereum.jsontestsuite;
|
||||||
|
|
||||||
import org.ethereum.core.BlockchainImpl;
|
import org.ethereum.core.BlockchainImpl;
|
||||||
import org.ethereum.core.TransactionExecutor;
|
import org.ethereum.core.TransactionExecutor;
|
||||||
|
import org.ethereum.db.BlockStoreDummy;
|
||||||
import org.ethereum.db.ByteArrayWrapper;
|
import org.ethereum.db.ByteArrayWrapper;
|
||||||
import org.ethereum.db.ContractDetails;
|
import org.ethereum.db.ContractDetails;
|
||||||
import org.ethereum.db.RepositoryDummy;
|
import org.ethereum.db.RepositoryDummy;
|
||||||
|
@ -88,8 +89,9 @@ public class TestRunner {
|
||||||
blockchain.startTracking();
|
blockchain.startTracking();
|
||||||
|
|
||||||
Repository track = repository.startTracking();
|
Repository track = repository.startTracking();
|
||||||
TransactionExecutor executor = new TransactionExecutor(tx, coinbase, track,
|
TransactionExecutor executor =
|
||||||
invokeFactory, null);
|
new TransactionExecutor(tx, coinbase, track, new BlockStoreDummy(),
|
||||||
|
invokeFactory, blockchain.getBestBlock());
|
||||||
executor.execute();
|
executor.execute();
|
||||||
track.commit();
|
track.commit();
|
||||||
|
|
||||||
|
@ -179,7 +181,7 @@ public class TestRunner {
|
||||||
|
|
||||||
ProgramInvoke programInvoke = new ProgramInvokeImpl(address, origin, caller, balance,
|
ProgramInvoke programInvoke = new ProgramInvokeImpl(address, origin, caller, balance,
|
||||||
gasPrice, gas, callValue, msgData, lastHash, coinbase,
|
gasPrice, gas, callValue, msgData, lastHash, coinbase,
|
||||||
timestamp, number, difficulty, gaslimit, repository, true);
|
timestamp, number, difficulty, gaslimit, repository, null, true);
|
||||||
|
|
||||||
/* 3. Create Program - exec.code */
|
/* 3. Create Program - exec.code */
|
||||||
/* 4. run VM */
|
/* 4. run VM */
|
||||||
|
|
|
@ -6,7 +6,7 @@ import org.ethereum.core.Transaction;
|
||||||
import org.ethereum.core.TransactionReceipt;
|
import org.ethereum.core.TransactionReceipt;
|
||||||
import org.ethereum.core.Wallet;
|
import org.ethereum.core.Wallet;
|
||||||
import org.ethereum.crypto.HashUtil;
|
import org.ethereum.crypto.HashUtil;
|
||||||
import org.ethereum.db.BlockStore;
|
import org.ethereum.db.BlockStoreImpl;
|
||||||
import org.ethereum.facade.Blockchain;
|
import org.ethereum.facade.Blockchain;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
import org.ethereum.listener.EthereumListener;
|
import org.ethereum.listener.EthereumListener;
|
||||||
|
@ -63,7 +63,7 @@ public class WorldManager {
|
||||||
private PeerDiscovery peerDiscovery;
|
private PeerDiscovery peerDiscovery;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private BlockStore blockStore;
|
private BlockStoreImpl blockStore;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ChannelManager channelManager;
|
private ChannelManager channelManager;
|
||||||
|
|
|
@ -190,7 +190,7 @@ public enum OpCode {
|
||||||
* (0x40) Get hash of most recent
|
* (0x40) Get hash of most recent
|
||||||
* complete block
|
* complete block
|
||||||
*/
|
*/
|
||||||
PREVHASH(0x40, 0),
|
BLOCKHASH(0x40, 1),
|
||||||
/**
|
/**
|
||||||
* (0x41) Get the block’s coinbase address
|
* (0x41) Get the block’s coinbase address
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.ethereum.vm;
|
package org.ethereum.vm;
|
||||||
|
|
||||||
import org.ethereum.crypto.HashUtil;
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
import org.ethereum.db.ContractDetails;
|
import org.ethereum.db.ContractDetails;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
import org.ethereum.util.ByteUtil;
|
import org.ethereum.util.ByteUtil;
|
||||||
|
@ -350,7 +351,7 @@ public class Program {
|
||||||
// [5] COOK THE INVOKE AND EXECUTE
|
// [5] COOK THE INVOKE AND EXECUTE
|
||||||
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
|
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
|
||||||
this, new DataWord(newAddress), DataWord.ZERO, gasLimit,
|
this, new DataWord(newAddress), DataWord.ZERO, gasLimit,
|
||||||
newBalance, null, track);
|
newBalance, null, track, this.invokeData.getBlockStore());
|
||||||
|
|
||||||
ProgramResult result = null;
|
ProgramResult result = null;
|
||||||
|
|
||||||
|
@ -466,7 +467,7 @@ public class Program {
|
||||||
Repository trackRepository = result.getRepository().startTracking();
|
Repository trackRepository = result.getRepository().startTracking();
|
||||||
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
|
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
|
||||||
this, new DataWord(contextAddress), msg.getEndowment(),
|
this, new DataWord(contextAddress), msg.getEndowment(),
|
||||||
msg.getGas(), contextBalance, data, trackRepository);
|
msg.getGas(), contextBalance, data, trackRepository, this.invokeData.getBlockStore());
|
||||||
|
|
||||||
ProgramResult result = null;
|
ProgramResult result = null;
|
||||||
|
|
||||||
|
@ -574,6 +575,22 @@ public class Program {
|
||||||
return this.programAddress.clone();
|
return this.programAddress.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataWord getBlockHash(int index) {
|
||||||
|
|
||||||
|
// todo:
|
||||||
|
/// Hash of a block if within the last 256 blocks, or h256() otherwise.
|
||||||
|
// h256 blockhash(u256 _number) {return
|
||||||
|
// _number < currentBlock.number && _number >= (std::max<u256>(256, currentBlock.number) - 256) ?
|
||||||
|
// lastHashes[(unsigned)(currentBlock.number - 1 - _number)] :
|
||||||
|
// h256(); }
|
||||||
|
|
||||||
|
return index < this.getNumber().longValue() && index >= Math.max(256, this.getNumber().intValue()) - 256?
|
||||||
|
new DataWord(this.invokeData.getBlockStore().getBlockHashByNumber(index)):
|
||||||
|
DataWord.ZERO;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public DataWord getBalance(DataWord address) {
|
public DataWord getBalance(DataWord address) {
|
||||||
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
|
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.ethereum.vm;
|
package org.ethereum.vm;
|
||||||
|
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,12 +42,15 @@ public interface ProgramInvoke {
|
||||||
public DataWord getDifficulty();
|
public DataWord getDifficulty();
|
||||||
|
|
||||||
public DataWord getGaslimit();
|
public DataWord getGaslimit();
|
||||||
|
|
||||||
public Repository getRepository();
|
|
||||||
|
|
||||||
public boolean byTransaction();
|
public boolean byTransaction();
|
||||||
|
|
||||||
boolean byTestingSuite();
|
boolean byTestingSuite();
|
||||||
|
|
||||||
public int getCallDeep();
|
public int getCallDeep();
|
||||||
|
|
||||||
|
public Repository getRepository();
|
||||||
|
|
||||||
|
public BlockStore getBlockStore();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.ethereum.vm;
|
||||||
|
|
||||||
import org.ethereum.core.Block;
|
import org.ethereum.core.Block;
|
||||||
import org.ethereum.core.Transaction;
|
import org.ethereum.core.Transaction;
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -12,12 +13,13 @@ import java.math.BigInteger;
|
||||||
*/
|
*/
|
||||||
public interface ProgramInvokeFactory {
|
public interface ProgramInvokeFactory {
|
||||||
|
|
||||||
public ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository);
|
public ProgramInvoke createProgramInvoke(Transaction tx, Block block,
|
||||||
|
Repository repository, BlockStore blockStore);
|
||||||
|
|
||||||
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
|
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
|
||||||
DataWord inValue, DataWord inGas,
|
DataWord inValue, DataWord inGas,
|
||||||
BigInteger balanceInt, byte[] dataIn,
|
BigInteger balanceInt, byte[] dataIn,
|
||||||
Repository repository);
|
Repository repository, BlockStore blockStore);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package org.ethereum.vm;
|
||||||
|
|
||||||
import org.ethereum.core.Block;
|
import org.ethereum.core.Block;
|
||||||
import org.ethereum.core.Transaction;
|
import org.ethereum.core.Transaction;
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
|
import org.ethereum.db.BlockStoreImpl;
|
||||||
import org.ethereum.facade.Blockchain;
|
import org.ethereum.facade.Blockchain;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
import org.ethereum.util.ByteUtil;
|
import org.ethereum.util.ByteUtil;
|
||||||
|
@ -27,11 +29,15 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Blockchain blockchain;
|
private Blockchain blockchain;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BlockStoreImpl blockStore;
|
||||||
|
|
||||||
|
|
||||||
// Invocation by the wire tx
|
// Invocation by the wire tx
|
||||||
@Override
|
@Override
|
||||||
public ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository) {
|
public ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository,
|
||||||
|
BlockStore blockStore) {
|
||||||
|
|
||||||
// https://ethereum.etherpad.mozilla.org/26
|
// https://ethereum.etherpad.mozilla.org/26
|
||||||
Block lastBlock = blockchain.getBestBlock();
|
Block lastBlock = blockchain.getBestBlock();
|
||||||
|
@ -119,7 +125,7 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
|
||||||
ProgramInvoke programInvoke =
|
ProgramInvoke programInvoke =
|
||||||
new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue, data,
|
new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue, data,
|
||||||
lastHash, coinbase, timestamp, number, difficulty, gaslimit,
|
lastHash, coinbase, timestamp, number, difficulty, gaslimit,
|
||||||
repository);
|
repository, blockStore);
|
||||||
|
|
||||||
return programInvoke;
|
return programInvoke;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +137,7 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
|
||||||
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
|
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
|
||||||
DataWord inValue, DataWord inGas,
|
DataWord inValue, DataWord inGas,
|
||||||
BigInteger balanceInt, byte[] dataIn,
|
BigInteger balanceInt, byte[] dataIn,
|
||||||
Repository repository) {
|
Repository repository, BlockStore blockStore) {
|
||||||
|
|
||||||
DataWord address = toAddress;
|
DataWord address = toAddress;
|
||||||
DataWord origin = program.getOriginAddress();
|
DataWord origin = program.getOriginAddress();
|
||||||
|
@ -184,6 +190,6 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
|
||||||
|
|
||||||
return new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue,
|
return new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue,
|
||||||
data, lastHash, coinbase, timestamp, number, difficulty, gasLimit,
|
data, lastHash, coinbase, timestamp, number, difficulty, gasLimit,
|
||||||
repository, program.invokeData.getCallDeep() + 1);
|
repository, program.invokeData.getCallDeep() + 1, blockStore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.ethereum.vm;
|
package org.ethereum.vm;
|
||||||
|
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -13,6 +14,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class ProgramInvokeImpl implements ProgramInvoke {
|
public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
|
|
||||||
|
private BlockStore blockStore;
|
||||||
/**
|
/**
|
||||||
* TRANSACTION env **
|
* TRANSACTION env **
|
||||||
*/
|
*/
|
||||||
|
@ -39,7 +41,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
|
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
|
||||||
DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord
|
DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord
|
||||||
difficulty,
|
difficulty,
|
||||||
DataWord gaslimit, Repository repository, int callDeep) {
|
DataWord gaslimit, Repository repository, int callDeep, BlockStore blockStore) {
|
||||||
|
|
||||||
// Transaction env
|
// Transaction env
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
@ -62,15 +64,16 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.byTransaction = false;
|
this.byTransaction = false;
|
||||||
this.callDeep = callDeep;
|
this.callDeep = callDeep;
|
||||||
|
this.blockStore = blockStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] balance,
|
public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] balance,
|
||||||
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
|
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
|
||||||
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
|
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
|
||||||
long gaslimit,
|
long gaslimit,
|
||||||
Repository repository, boolean byTestingSuite) {
|
Repository repository, BlockStore blockStore,boolean byTestingSuite) {
|
||||||
this(address, origin, caller, balance, gasPrice, gas, callValue, msgData, lastHash, coinbase,
|
this(address, origin, caller, balance, gasPrice, gas, callValue, msgData, lastHash, coinbase,
|
||||||
timestamp, number, difficulty, gaslimit, repository);
|
timestamp, number, difficulty, gaslimit, repository, blockStore);
|
||||||
this.byTestingSuite = byTestingSuite;
|
this.byTestingSuite = byTestingSuite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +82,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
|
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
|
||||||
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
|
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
|
||||||
long gaslimit,
|
long gaslimit,
|
||||||
Repository repository) {
|
Repository repository, BlockStore blockStore) {
|
||||||
|
|
||||||
// Transaction env
|
// Transaction env
|
||||||
this.address = new DataWord(address);
|
this.address = new DataWord(address);
|
||||||
|
@ -100,6 +103,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
this.gaslimit = new DataWord(gaslimit);
|
this.gaslimit = new DataWord(gaslimit);
|
||||||
|
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
|
this.blockStore = blockStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ADDRESS op */
|
/* ADDRESS op */
|
||||||
|
@ -239,6 +243,11 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
return repository;
|
return repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockStore getBlockStore() {
|
||||||
|
return blockStore;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean byTransaction() {
|
public boolean byTransaction() {
|
||||||
return byTransaction;
|
return byTransaction;
|
||||||
|
|
|
@ -2,6 +2,8 @@ package org.ethereum.vm;
|
||||||
|
|
||||||
import org.ethereum.crypto.ECKey;
|
import org.ethereum.crypto.ECKey;
|
||||||
import org.ethereum.crypto.HashUtil;
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
import org.ethereum.db.BlockStore;
|
||||||
|
import org.ethereum.db.BlockStoreDummy;
|
||||||
import org.ethereum.db.RepositoryImpl;
|
import org.ethereum.db.RepositoryImpl;
|
||||||
import org.ethereum.facade.Repository;
|
import org.ethereum.facade.Repository;
|
||||||
|
|
||||||
|
@ -214,6 +216,11 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
return this.repository;
|
return this.repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockStore getBlockStore() {
|
||||||
|
return new BlockStoreDummy();
|
||||||
|
}
|
||||||
|
|
||||||
public void setRepository(Repository repository) {
|
public void setRepository(Repository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
|
@ -716,13 +716,16 @@ public class VM {
|
||||||
/**
|
/**
|
||||||
* Block Information
|
* Block Information
|
||||||
*/
|
*/
|
||||||
case PREVHASH: {
|
case BLOCKHASH: {
|
||||||
DataWord prevHash = program.getPrevHash();
|
|
||||||
|
int blockIndex = program.stackPop().intValue();
|
||||||
|
|
||||||
|
DataWord blockHash = program.getBlockHash(blockIndex);
|
||||||
|
|
||||||
if (logger.isInfoEnabled())
|
if (logger.isInfoEnabled())
|
||||||
hint = "prevHash: " + prevHash;
|
hint = "blockHash: " + blockHash;
|
||||||
|
|
||||||
program.stackPush(prevHash);
|
program.stackPush(blockHash);
|
||||||
program.step();
|
program.step();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -18,9 +18,8 @@ public class GitHubStateTest {
|
||||||
@Ignore
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
public void stSingleTest() throws ParseException {
|
public void stSingleTest() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("StateTests/stSystemOperationsTest.json");
|
String json = JSONReader.loadJSON("StateTests/stSystemOperationsTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonStateTest(json, "createNameRegistrator");
|
GitHubJSONTestSuite.runGitHubJsonStateTest(json, "CallToReturn1ForDynamicJump1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
|
@ -28,7 +27,7 @@ public class GitHubStateTest {
|
||||||
public void runWithExcludedTest() throws ParseException {
|
public void runWithExcludedTest() throws ParseException {
|
||||||
|
|
||||||
Set<String> excluded = new HashSet<>();
|
Set<String> excluded = new HashSet<>();
|
||||||
excluded.add("createNameRegistratorValueTooHigh");
|
excluded.add("CallToReturn1ForDynamicJump1");
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("StateTests/stSystemOperationsTest.json");
|
String json = JSONReader.loadJSON("StateTests/stSystemOperationsTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonStateTest(json, excluded);
|
GitHubJSONTestSuite.runGitHubJsonStateTest(json, excluded);
|
||||||
|
@ -89,6 +88,14 @@ public class GitHubStateTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void stBlockHashTest() throws ParseException {
|
||||||
|
|
||||||
|
String json = JSONReader.loadJSON("StateTests/stBlockHashTest.json");
|
||||||
|
GitHubJSONTestSuite.runGitHubJsonStateTest(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void stSystemOperationsTest() throws ParseException {
|
public void stSystemOperationsTest() throws ParseException {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue