Bloom filters, Logs, Transaction receipts.

This commit is contained in:
romanman 2014-11-23 21:14:00 +01:00
parent 23a7f75266
commit 55c7a9c088
13 changed files with 316 additions and 61 deletions

View File

@ -217,6 +217,8 @@ public class BlockchainImpl implements Blockchain {
this.processBlock(block); this.processBlock(block);
track.commit(); track.commit();
repository.flush(); // saving to the disc
// Remove all wallet transactions as they already approved by the net // Remove all wallet transactions as they already approved by the net
worldManager.getWallet().removeTransactions(block.getTransactionsList()); worldManager.getWallet().removeTransactions(block.getTransactionsList());
@ -326,9 +328,21 @@ public class BlockchainImpl implements Blockchain {
long totalGasUsed = 0; long totalGasUsed = 0;
for (Transaction tx : block.getTransactionsList()) { for (Transaction tx : block.getTransactionsList()) {
stateLogger.debug("apply block: [{}] tx: [{}] ", block.getNumber(), i); stateLogger.debug("apply block: [{}] tx: [{}] ", block.getNumber(), i);
totalGasUsed += applyTransaction(block, tx);
if(block.getNumber() >= CONFIG.traceStartBlock()) TransactionReceipt receipt = applyTransaction(block, tx);
repository.dumpState(block, totalGasUsed, i++, tx.getHash()); totalGasUsed += receipt.getCumulativeGasLong();
receipt.setCumulativeGas(totalGasUsed);
track.commit();
receipt.setPostTxState(repository.getRoot());
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
} }
this.addReward(block); this.addReward(block);
@ -398,10 +412,12 @@ public class BlockchainImpl implements Blockchain {
* @param tx - the transaction to be applied * @param tx - the transaction to be applied
* @return gasUsed - the total amount of gas used for this transaction. * @return gasUsed - the total amount of gas used for this transaction.
*/ */
public long applyTransaction(Block block, Transaction tx) { public TransactionReceipt applyTransaction(Block block, Transaction tx) {
logger.info("applyTransaction: [{}]", Hex.toHexString(tx.getHash())); logger.info("applyTransaction: [{}]", Hex.toHexString(tx.getHash()));
TransactionReceipt receipt = new TransactionReceipt();
byte[] coinbase = block.getCoinbase(); byte[] coinbase = block.getCoinbase();
// VALIDATE THE SENDER // VALIDATE THE SENDER
@ -416,7 +432,9 @@ public class BlockchainImpl implements Blockchain {
if (stateLogger.isWarnEnabled()) if (stateLogger.isWarnEnabled())
stateLogger.warn("Invalid nonce account.nonce={} tx.nonce={}", stateLogger.warn("Invalid nonce account.nonce={} tx.nonce={}",
nonce, txNonce); nonce, txNonce);
return 0;
receipt.setCumulativeGas(0);
return receipt;
} }
// UPDATE THE NONCE // UPDATE THE NONCE
@ -474,7 +492,9 @@ public class BlockchainImpl implements Blockchain {
if (track.getBalance(senderAddress).compareTo(gasDebit) == -1) { if (track.getBalance(senderAddress).compareTo(gasDebit) == -1) {
logger.debug("No gas to start the execution: sender={}", logger.debug("No gas to start the execution: sender={}",
Hex.toHexString(senderAddress)); Hex.toHexString(senderAddress));
return 0;
receipt.setCumulativeGas(0);
return receipt;
} }
track.addBalance(senderAddress, gasDebit.negate()); track.addBalance(senderAddress, gasDebit.negate());
@ -529,7 +549,9 @@ public class BlockchainImpl implements Blockchain {
} catch (RuntimeException e) { } catch (RuntimeException e) {
trackTx.rollback(); trackTx.rollback();
return new BigInteger(1, tx.getGasLimit()).longValue();
receipt.setCumulativeGas(tx.getGasLimit());
return receipt;
} }
trackTx.commit(); trackTx.commit();
} else { } else {
@ -543,7 +565,9 @@ public class BlockchainImpl implements Blockchain {
track.addBalance(coinbase, refund.negate()); track.addBalance(coinbase, refund.negate());
} }
} }
return gasUsed;
receipt.setCumulativeGas(gasUsed);
return receipt;
} }
/** /**

View File

@ -0,0 +1,75 @@
package org.ethereum.core;
import org.spongycastle.util.encoders.Hex;
/**
* www.etherj.com
*
* @author: Roman Mandeleil
* Created on: 20/11/2014 11:10
*
* http://www.herongyang.com/Java/Bit-String-Set-Bit-to-Byte-Array.html
*/
public class Bloom {
byte[] data = new byte[512];
public Bloom(byte[] data){
}
public static Bloom create(byte[] toBloom){
int mov1 = (toBloom[0] & 1) * 256 + (toBloom[1] & 255);
int mov2 = (toBloom[2] & 1) * 256 + (toBloom[3] & 255);
int mov3 = (toBloom[4] & 1) * 256 + (toBloom[5] & 255);
byte[] data = new byte[512];
Bloom bloom = new Bloom(data);
bloom.setBit(mov1, 1);
bloom.setBit(mov2, 1);
bloom.setBit(mov3, 1);
return bloom;
}
public void or(Bloom bloom){
for (int i = 0; i < data.length; ++i){
data[i] |= bloom.data[i];
}
}
public void setBit(int pos, int val) {
if (data.length - 1 < pos )
throw new Error("outside bloom limit, pos: " + pos);
int posByte = (pos - 1) / 8;
int posBit = (pos - 1) % 8;
byte oldByte = data[posByte];
oldByte = (byte) (((0xFF7F >> (( posBit + 1)) & oldByte) & 0x00FF));
byte newByte = (byte) ((val << ( 8 - ( posBit + 1))) | oldByte);
data[posByte] = newByte;
}
public int getBit(int pos) {
if (data.length - 1 < pos )
throw new Error("outside bloom limit, pos: " + pos);
int posByte = pos / 8;
int posBit = pos % 8;
byte valByte = data[posByte];
int valInt = valByte >> (8 - (posBit + 1)) & 0x0001;
return valInt;
}
@Override
public String toString() {
return Hex.toHexString(data);
}
}

View File

@ -1,60 +1,118 @@
package org.ethereum.core; package org.ethereum.core;
import org.ethereum.util.RLP; import org.ethereum.util.RLP;
import org.ethereum.vm.LogInfo;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
/** /**
* The transaction receipt is a tuple of three items * The transaction receipt is a tuple of three items
* comprising the transaction, together with the post-transaction state, * comprising the transaction, together with the post-transaction state,
* and the cumulative gas used in the block containing the transaction receipt * and the cumulative gas used in the block containing the transaction receipt
* as of immediately after the transaction has happened, * as of immediately after the transaction has happened,
* *
* ** not todo: the transaction receipt was removed from the game but don't remove it
* as it will be used in the very near future.
* *
*/ */
public class TransactionReceipt { public class TransactionReceipt {
private Transaction transaction;
private byte[] postTxState; private byte[] postTxState;
private byte[] cumulativeGas; private byte[] cumulativeGas;
private Bloom bloomFilter;
private List<LogInfo> logInfoList;
/* Tx Receipt in encoded form */ /* Tx Receipt in encoded form */
private byte[] rlpEncoded; private byte[] rlpEncoded;
public TransactionReceipt(Transaction transaction, byte[] postTxState, byte[] cumulativeGas) { public TransactionReceipt() {
this.transaction = transaction;
this.postTxState = postTxState;
this.cumulativeGas = cumulativeGas;
}
public Transaction getTransaction() {
return transaction;
}
public byte[] getPostTxState() {
return postTxState;
} }
public TransactionReceipt(byte[] postTxState, byte[] cumulativeGas,
Bloom bloomFilter, List<LogInfo> logInfoList) {
this.postTxState = postTxState;
this.cumulativeGas = cumulativeGas;
this.bloomFilter = bloomFilter;
this.logInfoList = logInfoList;
}
public byte[] getPostTxState() {
return postTxState;
}
public byte[] getCumulativeGas() {
return cumulativeGas;
}
public long getCumulativeGasLong() {
return new BigInteger(1, cumulativeGas).longValue();
}
public Bloom getBloomFilter() {
return bloomFilter;
}
public List<LogInfo> getLogInfoList() {
return logInfoList;
}
/* [postTxState, cumulativeGas, bloomFilter, logInfoList] */
public byte[] getEncoded() { public byte[] getEncoded() {
if(rlpEncoded != null) return rlpEncoded; if(rlpEncoded != null) return rlpEncoded;
byte[] transactionEl = transaction.getEncoded(); byte[] postTxStateRLP = RLP.encodeElement(this.postTxState);
byte[] postTxStateEl = RLP.encodeElement(this.postTxState); byte[] cumulativeGasRLP = RLP.encodeElement(this.cumulativeGas);
byte[] cumulativeGasEl = RLP.encodeElement(this.cumulativeGas); byte[] bloomRLP = RLP.encodeElement(this.bloomFilter.data);
rlpEncoded = RLP.encodeList(transactionEl, postTxStateEl, cumulativeGasEl); byte[][] logInfoListE = new byte[logInfoList.size()][];
int i = 0;
for (LogInfo logInfo : logInfoList){
logInfoListE[i] = logInfo.getEncoded();
++i;
}
byte[] logInfoListRLP = RLP.encodeList(logInfoListE);
rlpEncoded = RLP.encodeList(postTxStateRLP, cumulativeGasRLP, bloomRLP, logInfoListRLP);
return rlpEncoded; return rlpEncoded;
} }
public void setPostTxState(byte[] postTxState) {
this.postTxState = postTxState;
}
public void setCumulativeGas(long cumulativeGas) {
this.cumulativeGas = BigIntegers.asUnsignedByteArray( BigInteger.valueOf( cumulativeGas ) );
}
public void setCumulativeGas(byte[] cumulativeGas) {
this.cumulativeGas = cumulativeGas;
}
public void setBloomFilter(Bloom bloomFilter) {
this.bloomFilter = bloomFilter;
}
public void setLogInfoList(List<LogInfo> logInfoList) {
this.logInfoList = logInfoList;
}
@Override @Override
public String toString() { public String toString() {
// todo: fix that
return "TransactionReceipt[" + return "TransactionReceipt[" +
"\n " + transaction +
"\n , postTxState=" + Hex.toHexString(postTxState) + "\n , postTxState=" + Hex.toHexString(postTxState) +
"\n , cumulativeGas=" + Hex.toHexString(cumulativeGas) + "\n , cumulativeGas=" + Hex.toHexString(cumulativeGas) +
"\n , bloom=" + bloomFilter.toString() +
"\n , logs=" + logInfoList +
']'; ']';
} }

View File

@ -126,9 +126,14 @@ public class RepositoryImpl implements Repository {
accountState.setDeleted(false); accountState.setDeleted(false);
accountState.setDirty(false); accountState.setDirty(false);
} }
}
@Override
public void flush(){
worldState.sync(); worldState.sync();
} }
@Override @Override
public void rollback() { public void rollback() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View File

@ -178,6 +178,12 @@ public class RepositoryTrack implements Repository {
} }
@Override
public void flush(){
throw new UnsupportedOperationException();
}
@Override @Override
public void commit() { public void commit() {
logger.debug("commit changes"); logger.debug("commit changes");

View File

@ -146,6 +146,9 @@ public interface Repository {
*/ */
public Repository startTracking(); public Repository startTracking();
public void flush();
/** /**
* Store all the temporary changes made * Store all the temporary changes made
* to the repository in the actual database * to the repository in the actual database

View File

@ -16,6 +16,7 @@ import java.util.List;
import org.ethereum.util.RLP; import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList; import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
/** /**
* Recursive Length Prefix (RLP) encoding. * Recursive Length Prefix (RLP) encoding.
@ -865,9 +866,14 @@ public class RLP {
public static byte[] encodeList(byte[]... elements) { public static byte[] encodeList(byte[]... elements) {
if (elements == null){
return new byte[] {(byte)OFFSET_SHORT_LIST };
}
int totalLength = 0; int totalLength = 0;
for (int i = 0; i < elements.length; ++i) { for (int i = 0;
totalLength += elements[i].length; elements != null && i < elements.length; ++i) {
totalLength += elements[i].length;
} }
byte[] data; byte[] data;

View File

@ -1,7 +1,10 @@
package org.ethereum.vm; package org.ethereum.vm;
import org.ethereum.core.BlockHeader;
import org.ethereum.util.RLP;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -15,12 +18,15 @@ import java.util.List;
public class LogInfo { public class LogInfo {
byte[] address; byte[] address;
List<byte[]> topics; List<byte[]> topics = new ArrayList<>();
byte[] data; byte[] data;
/* Log info in encoded form */
private byte[] rlpEncoded;
public LogInfo(byte[] address, List<byte[]> topics, byte[] data) { public LogInfo(byte[] address, List<byte[]> topics, byte[] data) {
this.address = address; this.address = address;
this.topics = topics; this.topics = (topics == null) ? new ArrayList<byte[]>() : topics;
this.data = data; this.data = data;
} }
@ -36,6 +42,26 @@ public class LogInfo {
return data; return data;
} }
/* [address, [topic, topic ...] data] */
public byte[] getEncoded() {
byte[] addressEncoded = RLP.encodeElement(this.address);
byte[][] topicsEncoded = null;
if (topics != null){
topicsEncoded = new byte[topics.size()][];
int i = 0;
for( byte[] topic : topics ){
topicsEncoded[i] = topic;
++i;
}
}
byte[] dataEncoded = RLP.encodeElement(data);
return RLP.encodeList(addressEncoded, RLP.encodeList(topicsEncoded), dataEncoded);
}
@Override @Override
public String toString() { public String toString() {

View File

@ -0,0 +1,47 @@
package test.ethereum.core;
import org.ethereum.core.Bloom;
import org.ethereum.crypto.SHA3Helper;
import org.junit.Test;
import org.spongycastle.util.encoders.Hex;
/**
* www.etherj.com
*
* @author: Roman Mandeleil
* Created on: 20/11/2014 11:29
*/
public class BloomTest {
@Test
public void test1(){
byte[] key = SHA3Helper.sha3(Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826"));
Bloom bloom = Bloom.create(key);
System.out.println(bloom);
}
@Test
public void test2(){
byte[] key = Hex.decode("0954D2BEF0CA79C1A988AE5FF3072C2AEA90F3967A9596065123F2A15AA37EF3");
Bloom bloom = Bloom.create(key);
System.out.println(bloom);
}
@Test
public void test3(){
byte[] key = SHA3Helper.sha3(Hex.decode("22341AE42D6DD7384BC8584E50419EA3AC75B83F "));
Bloom bloom = Bloom.create(key);
System.out.println(bloom);
}
}

View File

@ -34,32 +34,6 @@ public class StateTest {
assertEquals(GENESIS_STATE_ROOT, Hex.toHexString(trie.getRootHash())); assertEquals(GENESIS_STATE_ROOT, Hex.toHexString(trie.getRootHash()));
} }
@Test // right way to calc tx trie hash
public void testCalculatePostTxState() {
/* txTrieHash */
String expected = "a77691cf47bec9021d3f027fc8ef2d51b758b600a79967154354b8e37108896f";
Transaction tx = new Transaction(
new byte[]{},
Hex.decode("09184E72A000"),
Hex.decode("03E8"),
Hex.decode("0000000000000000000000000000000000000000"),
Hex.decode("03e8"),
Hex.decode("60016000546006601160003960066000f261778e600054")
);
byte[] cowPrivKey = Hex.decode("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4");
tx.sign(cowPrivKey);
byte[] postTxState = Hex.decode("7fa5bd00f6e03b5a5718560f1e25179b227167585a3c3da06a48f554365fb527");
byte[] cumGas = Hex.decode("0272");
TransactionReceipt tr = new TransactionReceipt(tx, postTxState, cumGas);
Trie trie = new TrieImpl(new MockDB());
trie.update(RLP.encodeInt(0), tr.getEncoded());
String txTrieRoot = Hex.toHexString(trie.getRootHash());
assertEquals(expected, txTrieRoot);
}
@Test // calc state after applying first tx on genesis @Test // calc state after applying first tx on genesis
public void test2() { public void test2() {

View File

@ -8,7 +8,12 @@ import java.math.BigInteger;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.List;
import org.ethereum.core.Bloom;
import org.ethereum.core.TransactionReceipt;
import org.ethereum.vm.LogInfo;
import test.ethereum.TestContext; import test.ethereum.TestContext;
import org.ethereum.core.Transaction; import org.ethereum.core.Transaction;
import org.ethereum.crypto.ECKey; import org.ethereum.crypto.ECKey;
@ -89,7 +94,7 @@ public class TransactionTest {
System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey())); System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey()));
System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress())); System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress()));
Assert.assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
Hex.toHexString(key.getAddress())); Hex.toHexString(key.getAddress()));
System.out.println(tx.toString()); System.out.println(tx.toString());
@ -130,7 +135,7 @@ public class TransactionTest {
System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey())); System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey()));
System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress())); System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress()));
Assert.assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
Hex.toHexString(key.getAddress())); Hex.toHexString(key.getAddress()));
} }
@ -277,4 +282,29 @@ public class TransactionTest {
System.out.println( Hex.toHexString(tx2.getSender())); System.out.println( Hex.toHexString(tx2.getSender()));
} }
@Test
public void encodeReceiptTest(){
String data = "f90244a0f5ff3fbd159773816a7c707a9b8cb6bb778b934a8f6466c7830ed970498f4b688301e848b902000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbda94cd2a3d9f938e13cd947ec05abc7fe734df8dd826c083a1a1a1";
byte[] stateRoot = Hex.decode("f5ff3fbd159773816a7c707a9b8cb6bb778b934a8f6466c7830ed970498f4b68");
byte[] gasUsed = Hex.decode("01E848");
Bloom bloom = new Bloom(Hex.decode("0000000000000000800000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
LogInfo logInfo1 = new LogInfo(
Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826"),
null,
Hex.decode("a1a1a1")
);
List<LogInfo> logs = new ArrayList<>();
logs.add(logInfo1);
TransactionReceipt receipt = new TransactionReceipt(stateRoot, gasUsed, bloom, logs);
assertEquals(data,
Hex.toHexString(receipt.getEncoded()));
}
} }

View File

@ -799,4 +799,5 @@ public class RLPTest {
System.out.println(Hex.toHexString(encodedData)); System.out.println(Hex.toHexString(encodedData));
} }
} }

View File

@ -19,7 +19,7 @@ log4j.logger.peermonitor = ERROR
log4j.logger.java.nio = ERROR log4j.logger.java.nio = ERROR
log4j.logger.io.netty = ERROR log4j.logger.io.netty = ERROR
log4j.logger.wire = ERROR log4j.logger.wire = ERROR
log4j.logger.VM = TRACE log4j.logger.VM = ERROR
log4j.logger.main = ERROR log4j.logger.main = ERROR
log4j.logger.trie = ERROR log4j.logger.trie = ERROR
log4j.logger.state = INFO log4j.logger.state = INFO