saving TransactionReceipt, bumping eth:48

This commit is contained in:
romanman 2014-12-07 21:30:18 +01:00
parent 254c51704a
commit d804cbb722
12 changed files with 310 additions and 51 deletions

View File

@ -114,6 +114,12 @@ public class BlockchainImpl implements Blockchain {
return blockStore.getBlockByNumber(blockNr);
}
@Override
public TransactionReceipt getTransactionReceiptByHash(byte[] hash){
return blockStore.getTransactionReceiptByHash(hash);
}
@Override
public Block getBlockByHash(byte[] hash){
return blockStore.getBlockByHash(hash);
@ -210,13 +216,14 @@ public class BlockchainImpl implements Blockchain {
}
this.processBlock(block);
track.commit();
repository.flush(); // saving to the disc
stateLogger.info("applied reward for block: [{}] \n state: [{}]",
block.getNumber(),
Hex.toHexString(repository.getRoot()));
track.commit();
repository.flush(); // saving to the disc
// Remove all wallet transactions as they already approved by the net
worldManager.getWallet().removeTransactions(block.getTransactionsList());
@ -303,46 +310,53 @@ public class BlockchainImpl implements Blockchain {
}
private void processBlock(Block block) {
private void processBlock(Block block) {
List<TransactionReceipt> receipts = new ArrayList<>();
if(isValid(block)) {
if (!block.isGenesis()) {
if (!CONFIG.blockChainOnly()) {
Wallet wallet = worldManager.getWallet();
wallet.addTransactions(block.getTransactionsList());
this.applyBlock(block);
receipts = this.applyBlock(block);
wallet.processBlock(block);
}
}
this.storeBlock(block);
this.storeBlock(block, receipts);
} else {
logger.warn("Invalid block with nr: {}", block.getNumber());
}
}
private void applyBlock(Block block) {
private List<TransactionReceipt> applyBlock(Block block) {
int i = 0;
int i = 1;
long totalGasUsed = 0;
List<TransactionReceipt> reciepts = new ArrayList<>();
for (Transaction tx : block.getTransactionsList()) {
stateLogger.info("apply block: [{}] tx: [{}] ", block.getNumber(), i);
TransactionReceipt receipt = applyTransaction(block, tx);
totalGasUsed += receipt.getCumulativeGasLong();
receipt.setCumulativeGas(totalGasUsed);
track.commit();
receipt.setCumulativeGas(totalGasUsed);
receipt.setPostTxState(repository.getRoot());
receipt.setTransaction(tx);
stateLogger.info("block: [{}] executed tx: [{}] \n state: [{}]", block.getNumber(), i,
Hex.toHexString(repository.getRoot()));
stateLogger.info("[{}] ", receipt.toString());
if (stateLogger.isInfoEnabled())
stateLogger.info("tx[{}].receipt: [{}] ",i, Hex.toHexString(receipt.getEncoded()));
if(block.getNumber() >= CONFIG.traceStartBlock())
repository.dumpState(block, totalGasUsed, i++, tx.getHash());
// todo: (!!!). save the receipt
// todo: cache all the receipts and
// todo: save them to the disc together
// todo: with the block afterwards
reciepts.add(receipt);
}
this.addReward(block);
@ -352,10 +366,12 @@ public class BlockchainImpl implements Blockchain {
if(block.getNumber() >= CONFIG.traceStartBlock())
repository.dumpState(block, totalGasUsed, 0, null);
return reciepts;
}
/**
* Add reward to block- and every uncle coinbase
* Add reward to block- and every uncle coinbase
* assuming the entire block is valid.
*
* @param block object containing the header and uncles
@ -377,7 +393,7 @@ public class BlockchainImpl implements Blockchain {
}
@Override
public void storeBlock(Block block) {
public void storeBlock(Block block, List<TransactionReceipt> receipts) {
/* Debug check to see if the state is still as expected */
if(logger.isWarnEnabled()) {
@ -394,7 +410,7 @@ public class BlockchainImpl implements Blockchain {
}
}
blockStore.saveBlock(block);
blockStore.saveBlock(block, receipts);
this.setBestBlock(block);
if (logger.isDebugEnabled())
@ -538,18 +554,18 @@ public class BlockchainImpl implements Blockchain {
if (CONFIG.playVM())
vm.play(program);
// todo: recepit save logs
// todo: receipt calc and save blooms
program.saveProgramTraceToFile(Hex.toHexString(tx.getHash()));
ProgramResult result = program.getResult();
applyProgramResult(result, gasDebit, gasPrice, trackTx,
senderAddress, receiverAddress, coinbase, isContractCreation);
gasUsed = result.getGasUsed();
List<LogInfo> logs = result.getLogInfoList();
receipt.setLogInfoList(logs);
} catch (RuntimeException e) {
trackTx.rollback();
receipt.setCumulativeGas(tx.getGasLimit());
return receipt;
}

View File

@ -18,6 +18,8 @@ public class Bloom {
byte[] data = new byte[64];
public Bloom() {
}
public Bloom(byte[] data){
this.data = data;

View File

@ -1,14 +1,17 @@
package org.ethereum.core;
import org.ethereum.util.RLP;
import org.ethereum.util.*;
import org.ethereum.vm.LogInfo;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
/**
* The transaction receipt is a tuple of three items
* comprising the transaction, together with the post-transaction state,
@ -19,10 +22,12 @@ import java.util.List;
*/
public class TransactionReceipt {
private byte[] postTxState;
private byte[] cumulativeGas;
private Bloom bloomFilter;
private List<LogInfo> logInfoList;
private Transaction transaction;
private byte[] postTxState = EMPTY_BYTE_ARRAY;
private byte[] cumulativeGas = EMPTY_BYTE_ARRAY;
private Bloom bloomFilter = new Bloom();
private List<LogInfo> logInfoList = new ArrayList<LogInfo>();
/* Tx Receipt in encoded form */
private byte[] rlpEncoded;
@ -30,6 +35,30 @@ public class TransactionReceipt {
public TransactionReceipt() {
}
public TransactionReceipt(byte[] rlp) {
RLPList params = RLP.decode2(rlp);
RLPList receipt = (RLPList) params.get(0);
RLPItem postTxStateRLP = (RLPItem) receipt.get(0);
RLPItem cumulativeGasRLP = (RLPItem) receipt.get(1);
RLPItem bloomRLP = (RLPItem) receipt.get(2);
RLPList logs = (RLPList) receipt.get(3);
postTxState = postTxStateRLP.getRLPData();
cumulativeGas = cumulativeGasRLP.getRLPData();
bloomFilter = new Bloom(bloomRLP.getRLPData());
for (int i = 0; i < logs.size(); i++) {
RLPElement logRLP = logs.get(i);
LogInfo logInfo = new LogInfo(logRLP.getRLPData());
logInfoList.add(logInfo);
}
rlpEncoded = rlp;
}
public TransactionReceipt(byte[] postTxState, byte[] cumulativeGas,
Bloom bloomFilter, List<LogInfo> logInfoList) {
this.postTxState = postTxState;
@ -69,14 +98,19 @@ public class TransactionReceipt {
byte[] cumulativeGasRLP = RLP.encodeElement(this.cumulativeGas);
byte[] bloomRLP = RLP.encodeElement(this.bloomFilter.data);
byte[][] logInfoListE = new byte[logInfoList.size()][];
byte[] logInfoListRLP = null;
if (logInfoList != null) {
byte[][] logInfoListE = new byte[logInfoList.size()][];
int i = 0;
for (LogInfo logInfo : logInfoList){
logInfoListE[i] = logInfo.getEncoded();
++i;
int i = 0;
for (LogInfo logInfo : logInfoList) {
logInfoListE[i] = logInfo.getEncoded() ;
++i;
}
logInfoListRLP = RLP.encodeList(logInfoListE);
} else{
logInfoListRLP = RLP.encodeList();
}
byte[] logInfoListRLP = RLP.encodeList(logInfoListE);
rlpEncoded = RLP.encodeList(postTxStateRLP, cumulativeGasRLP, bloomRLP, logInfoListRLP);
@ -95,12 +129,23 @@ public class TransactionReceipt {
this.cumulativeGas = cumulativeGas;
}
public void setBloomFilter(Bloom bloomFilter) {
this.bloomFilter = bloomFilter;
}
public void setLogInfoList(List<LogInfo> logInfoList) {
if (logInfoList == null) return;
this.rlpEncoded = null;
this.logInfoList = logInfoList;
for (LogInfo loginfo : logInfoList){
bloomFilter.or(loginfo.getBloom());
}
}
public void setTransaction(Transaction transaction){
this.transaction = transaction;
}
public Transaction getTransaction(){
return transaction;
}
@Override

View File

@ -1,6 +1,7 @@
package org.ethereum.db;
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;
@ -79,11 +80,20 @@ public class BlockStore {
@Transactional
public void saveBlock(Block block) {
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);
}
@ -132,4 +142,17 @@ public class BlockStore {
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);
}
}

View File

@ -0,0 +1,49 @@
package org.ethereum.db;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import java.math.BigInteger;
/**
* www.etherj.com
*
* @author: Roman Mandeleil
* Created on: 14/11/2014 09:59
*/
@Entity
@Table(name = "transaction_receipt")
public class TransactionReceiptVO {
@Id
byte[] hash;
@Lob
byte[] rlp;
public TransactionReceiptVO() {
}
public TransactionReceiptVO(byte[] hash, byte[] rlp) {
this.hash = hash;
this.rlp = rlp;
}
public byte[] getHash() {
return hash;
}
public void setHash(byte[] hash) {
this.hash = hash;
}
public byte[] getRlp() {
return rlp;
}
public void setRlp(byte[] rlp) {
this.rlp = rlp;
}
}

View File

@ -6,6 +6,7 @@ import java.util.Map;
import org.ethereum.core.Block;
import org.ethereum.core.Chain;
import org.ethereum.core.TransactionReceipt;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.net.BlockQueue;
import org.ethereum.core.Genesis;
@ -17,7 +18,7 @@ public interface Blockchain {
public long getSize();
public void add(Block block);
public void tryToConnect(Block block);
public void storeBlock(Block block);
public void storeBlock(Block block, List<TransactionReceipt> receipts);
public Block getBlockByNumber(long blockNr);
public void setBestBlock(Block block);
public Block getBestBlock();
@ -30,6 +31,9 @@ public interface Blockchain {
public void setTotalDifficulty(BigInteger totalDifficulty);
public byte[] getBestBlockHash();
public List<byte[]> getListOfHashesStartFrom(byte[] hash, int qty);
TransactionReceipt getTransactionReceiptByHash(byte[] hash);
public Block getBlockByHash(byte[] hash);
public List<Chain> getAltChains();
public List<Block> getGarbage();

View File

@ -1,6 +1,8 @@
package org.ethereum.facade;
import org.ethereum.config.SystemProperties;
import org.ethereum.net.eth.EthHandler;
import org.ethereum.net.shh.ShhHandler;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -99,6 +101,9 @@ public class EthereumFactory {
public static Ethereum createEthereum(){
logger.info("capability eth version: [{}]", EthHandler.VERSION);
logger.info("capability shh version: [{}]", ShhHandler.VERSION);
if (context == null){
context = new AnnotationConfigApplicationContext(EthereumFactory.class);
factory = context.getBean(EthereumFactory.class);

View File

@ -19,10 +19,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import static org.ethereum.config.SystemProperties.CONFIG;
@ -158,7 +155,7 @@ public class WorldManager {
repository.addBalance(Hex.decode(address), Genesis.PREMINE_AMOUNT);
}
blockStore.saveBlock(Genesis.getInstance());
blockStore.saveBlock(Genesis.getInstance(), new ArrayList<TransactionReceipt>());
blockchain.setBestBlock(Genesis.getInstance());
blockchain.setTotalDifficulty(BigInteger.ZERO);

View File

@ -42,7 +42,7 @@ import static org.ethereum.net.message.StaticMessages.GET_TRANSACTIONS_MESSAGE;
@Scope("prototype")
public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
public final static byte VERSION = 47;
public final static byte VERSION = 48;
public final static byte NETWORK_ID = 0x0;
private final static Logger logger = LoggerFactory.getLogger("net");

View File

@ -4,6 +4,9 @@ import org.ethereum.core.BlockHeader;
import org.ethereum.core.Bloom;
import org.ethereum.crypto.HashUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPElement;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
import java.nio.ByteBuffer;
@ -20,17 +23,38 @@ import java.util.List;
public class LogInfo {
byte[] address;
byte[] address = new byte[]{};
List<DataWord> topics = new ArrayList<DataWord>();
byte[] data;
byte[] data = new byte[]{};
/* Log info in encoded form */
private byte[] rlpEncoded;
public LogInfo(byte[] rlp){
RLPList params = RLP.decode2(rlp);
RLPList logInfo = (RLPList) params.get(0);
RLPItem address = (RLPItem)logInfo.get(0);
RLPList topics = (RLPList)logInfo.get(1);
RLPItem data = (RLPItem)logInfo.get(2);
this.address = address.getRLPData() != null ? address.getRLPData() : new byte[]{};
this.data = data.getRLPData() != null ? data.getRLPData() : new byte[]{};
for (int i = 0; i < topics.size(); ++i){
byte[] topic = topics.get(i).getRLPData();
this.topics.add(new DataWord(topic));
}
rlpEncoded = rlp;
}
public LogInfo(byte[] address, List<DataWord> topics, byte[] data) {
this.address = address;
this.topics = (topics == null) ? new ArrayList<DataWord>() : topics;
this.data = data;
this.address = (address != null) ? address : new byte[]{} ;
this.topics = (topics != null) ? topics : new ArrayList<DataWord>();
this.data = (data != null) ? data : new byte[]{} ;
}
public byte[] getAddress() {
@ -48,7 +72,6 @@ public class LogInfo {
/* [address, [topic, topic ...] data] */
public byte[] getEncoded() {
byte[] addressEncoded = RLP.encodeElement(this.address);
byte[][] topicsEncoded = null;
@ -57,7 +80,7 @@ public class LogInfo {
int i = 0;
for( DataWord topic : topics ){
byte[] topicData = topic.getData();
topicsEncoded[i] = topicData;
topicsEncoded[i] = RLP.encodeElement(topicData);
++i;
}
}
@ -72,7 +95,6 @@ public class LogInfo {
byte[] topicData = topic.getData();
ret.or(Bloom.create(HashUtil.sha3(topicData)));
}
return ret;
}

View File

@ -0,0 +1,53 @@
package test.ethereum.core;
import org.ethereum.vm.LogInfo;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import static org.junit.Assert.*;
/**
* www.etherj.com
*
* @author: Roman Mandeleil
* Created on: 05/12/2014 16:26
*/
public class LogInfoTest {
private static final Logger logger = LoggerFactory.getLogger("test");
@Test // rlp decode
public void test_1(){
// LogInfo{address=d5ccd26ba09ce1d85148b5081fa3ed77949417be, topics=[000000000000000000000000459d3a7595df9eba241365f4676803586d7d199c 436f696e73000000000000000000000000000000000000000000000000000000 ], data=}
byte[] rlp = Hex.decode("f85a94d5ccd26ba09ce1d85148b5081fa3ed77949417bef842a0000000000000000000000000459d3a7595df9eba241365f4676803586d7d199ca0436f696e7300000000000000000000000000000000000000000000000000000080");
LogInfo logInfo = new LogInfo(rlp);
assertEquals("d5ccd26ba09ce1d85148b5081fa3ed77949417be",
Hex.toHexString(logInfo.getAddress()));
assertEquals("", Hex.toHexString(logInfo.getData()));
assertEquals("000000000000000000000000459d3a7595df9eba241365f4676803586d7d199c",
logInfo.getTopics().get(0).toString());
assertEquals("436f696e73000000000000000000000000000000000000000000000000000000",
logInfo.getTopics().get(1).toString());
logger.info("{}", logInfo);
}
@Test // rlp decode
public void test_2(){
LogInfo log = new LogInfo(Hex.decode("d5ccd26ba09ce1d85148b5081fa3ed77949417be"), null, null);
assertEquals("d794d5ccd26ba09ce1d85148b5081fa3ed77949417bec080", Hex.toHexString(log.getEncoded()));
logger.info("{}", log);
}
}

View File

@ -0,0 +1,43 @@
package test.ethereum.core;
import org.ethereum.core.TransactionReceipt;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import static org.junit.Assert.assertEquals;
/**
* www.etherj.com
*
* @author: Roman Mandeleil
* Created on: 05/12/2014 16:26
*/
public class TransactionReceiptTest {
private static final Logger logger = LoggerFactory.getLogger("test");
@Test // rlp decode
public void test_1(){
byte[] rlp = Hex.decode("f8c4a0966265cc49fa1f10f0445f035258d116563931022a3570a640af5d73a214a8da822b6fb84000000010000000010000000000008000000000000000000000000000000000000000000000000000000000020000000000000014000000000400000000000440f85cf85a94d5ccd26ba09ce1d85148b5081fa3ed77949417bef842a0000000000000000000000000459d3a7595df9eba241365f4676803586d7d199ca0436f696e7300000000000000000000000000000000000000000000000000000080");
TransactionReceipt txReceipt = new TransactionReceipt(rlp);
assertEquals(1, txReceipt.getLogInfoList().size());
assertEquals("966265cc49fa1f10f0445f035258d116563931022a3570a640af5d73a214a8da",
Hex.toHexString(txReceipt.getPostTxState()));
assertEquals("2b6f",
Hex.toHexString(txReceipt.getCumulativeGas()));
assertEquals("00000010000000010000000000008000000000000000000000000000000000000000000000000000000000020000000000000014000000000400000000000440",
Hex.toHexString(txReceipt.getBloomFilter().getData()));
logger.info("{}", txReceipt);
}
}