Strip trailing whitespace
This commit is contained in:
parent
0827fb5c8f
commit
e5fcf5b48e
12
README.md
12
README.md
|
@ -1,7 +1,7 @@
|
||||||
[![ScreenShot](http://i.imgur.com/lJw1Tui.jpg)]
|
[![ScreenShot](http://i.imgur.com/lJw1Tui.jpg)]
|
||||||
|
|
||||||
|
|
||||||
# Welcome to ethereumj
|
# Welcome to ethereumj
|
||||||
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ethereum/ethereumj?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ethereum/ethereumj?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[![Build Status](https://travis-ci.org/ethereum/ethereumj.svg?branch=master)](https://travis-ci.org/ethereum/ethereumj) [![Coverage Status](https://coveralls.io/repos/ethereum/ethereumj/badge.png?branch=master)](https://coveralls.io/r/ethereum/ethereumj?branch=master)
|
[![Build Status](https://travis-ci.org/ethereum/ethereumj.svg?branch=master)](https://travis-ci.org/ethereum/ethereumj) [![Coverage Status](https://coveralls.io/repos/ethereum/ethereumj/badge.png?branch=master)](https://coveralls.io/r/ethereum/ethereumj?branch=master)
|
||||||
[![Stories in Progress](https://badge.waffle.io/ethereum/ethereumj.png?title=In%20Progress&label=in_progress)](https://waffle.io/ethereum/ethereumj)
|
[![Stories in Progress](https://badge.waffle.io/ethereum/ethereumj.png?title=In%20Progress&label=in_progress)](https://waffle.io/ethereum/ethereumj)
|
||||||
|
@ -10,18 +10,18 @@
|
||||||
|
|
||||||
The ethereumj library is a Java implementation of the Ethereum protocol.
|
The ethereumj library is a Java implementation of the Ethereum protocol.
|
||||||
|
|
||||||
This repository currently contains:
|
This repository currently contains:
|
||||||
* [core](ethereumj-core): the core library which can be included into your own Java project.
|
* [core](ethereumj-core): the core library which can be included into your own Java project.
|
||||||
* [studio](ethereumj-studio): a simple graphic interface for Ethereum functionality and set of showcases for core usage.
|
* [studio](ethereumj-studio): a simple graphic interface for Ethereum functionality and set of showcases for core usage.
|
||||||
|
|
||||||
For an early peek, have a looki at this [video](https://youtu.be/D5ok7jh7AOg)
|
For an early peek, have a looki at this [video](https://youtu.be/D5ok7jh7AOg)
|
||||||
|
|
||||||
# Todo
|
# Todo
|
||||||
|
|
||||||
The Ethereum protocol is currenty heavily in development, thus so is this implementation.
|
The Ethereum protocol is currenty heavily in development, thus so is this implementation.
|
||||||
You can find a todo-list right [here](TODO.md) and the [milestone schedule](https://github.com/ethereum/ethereumj/milestones).
|
You can find a todo-list right [here](TODO.md) and the [milestone schedule](https://github.com/ethereum/ethereumj/milestones).
|
||||||
For questions you can reach us in #ethereumj on Freenode.
|
For questions you can reach us in #ethereumj on Freenode.
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
To start you can visit [Ethereum.org](https://www.ethereum.org) and if you are looking for more information on the concept, the [ethereum white paper](https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-White-Paper) is a good place to start as it outlines the vision. For the more technical minded, a protocol description can be found in the [yellow paper](http://gavwood.com/Paper.pdf) by Gavin Wood.
|
To start you can visit [Ethereum.org](https://www.ethereum.org) and if you are looking for more information on the concept, the [ethereum white paper](https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-White-Paper) is a good place to start as it outlines the vision. For the more technical minded, a protocol description can be found in the [yellow paper](http://gavwood.com/Paper.pdf) by Gavin Wood.
|
||||||
|
@ -34,6 +34,6 @@ https://bintray.com/ethereum/maven/org.ethereum/view
|
||||||
|
|
||||||
For building ethereumj-core or ethereumj-studio look in their own individual README
|
For building ethereumj-core or ethereumj-studio look in their own individual README
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
This software is released under the MIT license, read it [here](LICENSE)
|
This software is released under the MIT license, read it [here](LICENSE)
|
||||||
|
|
28
TODO.md
28
TODO.md
|
@ -4,26 +4,26 @@
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
- [ ] **GUI screen** a screen that will hold table with full state representation
|
- [ ] **GUI screen** a screen that will hold table with full state representation
|
||||||
- [ ] **SerpentCompiler** compile create(gas, mem_start, import("examples/mul2.se"))
|
- [ ] **SerpentCompiler** compile create(gas, mem_start, import("examples/mul2.se"))
|
||||||
https://github.com/ethereum/wiki/wiki/Serpent
|
https://github.com/ethereum/wiki/wiki/Serpent
|
||||||
- [ ] **SerpentCompiler** compile return(array) correct
|
- [ ] **SerpentCompiler** compile return(array) correct
|
||||||
- [ ] **ProgramPlayDialog** support internal calls
|
- [ ] **ProgramPlayDialog** support internal calls
|
||||||
- [ ] **Performance:** BigInteger math change for constant arrays implementation
|
- [ ] **Performance:** BigInteger math change for constant arrays implementation
|
||||||
economy for memory allocation
|
economy for memory allocation
|
||||||
- [ ] **Command Line:** add the headless run option
|
- [ ] **Command Line:** add the headless run option
|
||||||
- [ ] **SerpentCompiler** Serpent new syntax:
|
- [ ] **SerpentCompiler** Serpent new syntax:
|
||||||
(@> @< @/ @%) - unsigned operations
|
(@> @< @/ @%) - unsigned operations
|
||||||
> < / % - default are all signed operations
|
> < / % - default are all signed operations
|
||||||
+= -= *= /= %= @/= @%= - short form operations
|
+= -= *= /= %= @/= @%= - short form operations
|
||||||
share - code section
|
share - code section
|
||||||
|
|
||||||
- [ ] **LLL_to_ASM compiler** list style language to EVM assembly compiler:
|
- [ ] **LLL_to_ASM compiler** list style language to EVM assembly compiler:
|
||||||
- [ ] **Use home-directory** Create .ethereumj in home-directory for blockchain, state & details database. Make configurable in system.properties so developer can choose user.dir without the creation of .ethereumj directory.
|
- [ ] **Use home-directory** Create .ethereumj in home-directory for blockchain, state & details database. Make configurable in system.properties so developer can choose user.dir without the creation of .ethereumj directory.
|
||||||
|
|
||||||
##### UnitTest:
|
##### UnitTest:
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
- [ ] **VM complex:** CREATE testing
|
- [ ] **VM complex:** CREATE testing
|
||||||
- [ ] **VM complex:** SUICIDE testing
|
- [ ] **VM complex:** SUICIDE testing
|
||||||
- [ ] **SerpentCompiler** compile return(array) correct
|
- [ ] **SerpentCompiler** compile return(array) correct
|
||||||
- [ ] **WorldManager** apply transactions
|
- [ ] **WorldManager** apply transactions
|
||||||
|
@ -36,14 +36,14 @@ share - code section
|
||||||
- [X] ** Block Queue ** separate net layer and block processing layer, net layer should continue get
|
- [X] ** Block Queue ** separate net layer and block processing layer, net layer should continue get
|
||||||
blocks in time the vm layer process them (not stuck for it)
|
blocks in time the vm layer process them (not stuck for it)
|
||||||
- [X] **Build:** extract core module and studio application
|
- [X] **Build:** extract core module and studio application
|
||||||
- [x] **VM execution:** support CALL op
|
- [x] **VM execution:** support CALL op
|
||||||
- [x] **VM execution:** support CALL op with in/out data
|
- [x] **VM execution:** support CALL op with in/out data
|
||||||
- [x] **VM execution:** support CREATE op
|
- [x] **VM execution:** support CREATE op
|
||||||
- [x] **SerpentCompiler** compile create(gas, mem_start, mem_size)
|
- [x] **SerpentCompiler** compile create(gas, mem_start, mem_size)
|
||||||
|
|
||||||
- [x] **VM complex:** CALL testing for in arrays
|
- [x] **VM complex:** CALL testing for in arrays
|
||||||
- [x] **VM complex:** CALL testing for out result
|
- [x] **VM complex:** CALL testing for out result
|
||||||
- [x] **State management** trie for storage hash calculation
|
- [x] **State management** trie for storage hash calculation
|
||||||
and update hash into AccountState
|
and update hash into AccountState
|
||||||
- [x] **VM execution:** SUICIDE op adjust
|
- [x] **VM execution:** SUICIDE op adjust
|
||||||
- [x] **Testing by JSON files:** follow cpp client performs test case by getting json file contains the test describe
|
- [x] **Testing by JSON files:** follow cpp client performs test case by getting json file contains the test describe
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
The core library API for Ethereum project can be included
|
The core library API for Ethereum project can be included
|
||||||
into any other Java/Scala project by simple maven
|
into any other Java/Scala project by simple maven
|
||||||
script include:
|
script include:
|
||||||
|
|
||||||
```
|
```
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -13,20 +13,20 @@ script include:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
EthereumJ release repository can be found here:
|
EthereumJ release repository can be found here:
|
||||||
* https://bintray.com/ethereum/maven/org.ethereum/view
|
* https://bintray.com/ethereum/maven/org.ethereum/view
|
||||||
|
|
||||||
|
|
||||||
The showcase for ethereumj-core usage can be found in [ethereumj-studio](../ethereumj-studio)
|
The showcase for ethereumj-core usage can be found in [ethereumj-studio](../ethereumj-studio)
|
||||||
|
|
||||||
###### :small_blue_diamond: Build instructions (maven)
|
###### :small_blue_diamond: Build instructions (maven)
|
||||||
1. build_1: [no test run] , [released to local repository] : ~> ` mvn clean install -Dmaven.test.skip=true `
|
1. build_1: [no test run] , [released to local repository] : ~> ` mvn clean install -Dmaven.test.skip=true `
|
||||||
2. build_2: [include test run] , [released to local repository] : ~> ` mvn clean install `
|
2. build_2: [include test run] , [released to local repository] : ~> ` mvn clean install `
|
||||||
|
|
||||||
###### :small_blue_diamond: release instructions (ant) (!) credential required
|
###### :small_blue_diamond: release instructions (ant) (!) credential required
|
||||||
1. ` mvn install ` - which release the lib to a local repositroy
|
1. ` mvn install ` - which release the lib to a local repositroy
|
||||||
2. after the release - ` ant -f bintray-publish-version.xml `
|
2. after the release - ` ant -f bintray-publish-version.xml `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -446,5 +446,5 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</pluginManagement>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import org.springframework.stereotype.Component;
|
||||||
/**
|
/**
|
||||||
* Utility class to retrieve property values from the system.properties files
|
* Utility class to retrieve property values from the system.properties files
|
||||||
*
|
*
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 22/05/2014 19:22
|
* Created on: 22/05/2014 19:22
|
||||||
*/
|
*/
|
||||||
public class SystemProperties {
|
public class SystemProperties {
|
||||||
|
@ -46,7 +46,7 @@ public class SystemProperties {
|
||||||
private static Boolean DEFAULT_VM_TRACE = false;
|
private static Boolean DEFAULT_VM_TRACE = false;
|
||||||
private static String DEFAULT_VM_TRACE_DIR = "dmp";
|
private static String DEFAULT_VM_TRACE_DIR = "dmp";
|
||||||
private static int DEFAULT_PEER_LISTEN_PORT = 30303;
|
private static int DEFAULT_PEER_LISTEN_PORT = 30303;
|
||||||
|
|
||||||
/* Testing */
|
/* Testing */
|
||||||
private static Boolean DEFAULT_VMTEST_LOAD_LOCAL = false;
|
private static Boolean DEFAULT_VMTEST_LOAD_LOCAL = false;
|
||||||
|
|
||||||
|
@ -281,17 +281,17 @@ public class SystemProperties {
|
||||||
logger.info("Key: " + key + ", Value: " + value);
|
logger.info("Key: " + key + ", Value: " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Testing
|
* Testing
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public boolean vmTestLoadLocal() {
|
public boolean vmTestLoadLocal() {
|
||||||
if (prop.isEmpty() || !prop.containsKey("GitHubTests.VMTest.loadLocal")) return DEFAULT_VMTEST_LOAD_LOCAL;
|
if (prop.isEmpty() || !prop.containsKey("GitHubTests.VMTest.loadLocal")) return DEFAULT_VMTEST_LOAD_LOCAL;
|
||||||
return Boolean.parseBoolean(prop.getProperty("GitHubTests.VMTest.loadLocal"));
|
return Boolean.parseBoolean(prop.getProperty("GitHubTests.VMTest.loadLocal"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
SystemProperties systemProperties = new SystemProperties();
|
SystemProperties systemProperties = new SystemProperties();
|
||||||
systemProperties.print();
|
systemProperties.print();
|
||||||
|
|
|
@ -12,32 +12,32 @@ import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
|
||||||
public class AccountState {
|
public class AccountState {
|
||||||
|
|
||||||
private byte[] rlpEncoded;
|
private byte[] rlpEncoded;
|
||||||
|
|
||||||
/* A value equal to the number of transactions sent
|
/* A value equal to the number of transactions sent
|
||||||
* from this address, or, in the case of contract accounts,
|
* from this address, or, in the case of contract accounts,
|
||||||
* the number of contract-creations made by this account */
|
* the number of contract-creations made by this account */
|
||||||
private BigInteger nonce;
|
private BigInteger nonce;
|
||||||
|
|
||||||
/* A scalar value equal to the number of Wei owned by this address */
|
/* A scalar value equal to the number of Wei owned by this address */
|
||||||
private BigInteger balance;
|
private BigInteger balance;
|
||||||
|
|
||||||
/* A 256-bit hash of the root node of a trie structure
|
/* A 256-bit hash of the root node of a trie structure
|
||||||
* that encodes the storage contents of the contract,
|
* that encodes the storage contents of the contract,
|
||||||
* itself a simple mapping between byte arrays of size 32.
|
* itself a simple mapping between byte arrays of size 32.
|
||||||
* The hash is formally denoted σ[a] s .
|
* The hash is formally denoted σ[a] s .
|
||||||
*
|
*
|
||||||
* Since I typically wish to refer not to the trie’s root hash
|
* Since I typically wish to refer not to the trie’s root hash
|
||||||
* but to the underlying set of key/value pairs stored within,
|
* but to the underlying set of key/value pairs stored within,
|
||||||
* I define a convenient equivalence TRIE (σ[a] s ) ≡ σ[a] s .
|
* I define a convenient equivalence TRIE (σ[a] s ) ≡ σ[a] s .
|
||||||
* It shall be understood that σ[a] s is not a ‘physical’ member
|
* It shall be understood that σ[a] s is not a ‘physical’ member
|
||||||
* of the account and does not contribute to its later serialisation */
|
* of the account and does not contribute to its later serialisation */
|
||||||
private byte[] stateRoot = EMPTY_TRIE_HASH;
|
private byte[] stateRoot = EMPTY_TRIE_HASH;
|
||||||
|
|
||||||
/* The hash of the EVM code of this contract—this is the code
|
/* The hash of the EVM code of this contract—this is the code
|
||||||
* that gets executed should this address receive a message call;
|
* that gets executed should this address receive a message call;
|
||||||
* it is immutable and thus, unlike all other fields, cannot be changed
|
* it is immutable and thus, unlike all other fields, cannot be changed
|
||||||
* after construction. All such code fragments are contained in
|
* after construction. All such code fragments are contained in
|
||||||
* the state database under their corresponding hashes for later
|
* the state database under their corresponding hashes for later
|
||||||
* retrieval */
|
* retrieval */
|
||||||
private byte[] codeHash = EMPTY_DATA_HASH;
|
private byte[] codeHash = EMPTY_DATA_HASH;
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ public class AccountState {
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
this.balance = balance;
|
this.balance = balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountState(byte[] rlpData) {
|
public AccountState(byte[] rlpData) {
|
||||||
this.rlpEncoded = rlpData;
|
this.rlpEncoded = rlpData;
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ public class AccountState {
|
||||||
this.balance = balance.subtract(value);
|
this.balance = balance.subtract(value);
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
if(rlpEncoded == null) {
|
if(rlpEncoded == null) {
|
||||||
byte[] nonce = RLP.encodeBigInteger(this.nonce);
|
byte[] nonce = RLP.encodeBigInteger(this.nonce);
|
||||||
|
|
|
@ -16,10 +16,10 @@ import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The block in Ethereum is the collection of relevant pieces of information
|
* The block in Ethereum is the collection of relevant pieces of information
|
||||||
* (known as the blockheader), H, together with information corresponding to
|
* (known as the blockheader), H, together with information corresponding to
|
||||||
* the comprised transactions, R, and a set of other blockheaders U that are known
|
* the comprised transactions, R, and a set of other blockheaders U that are known
|
||||||
* to have a parent equal to the present block’s parent’s parent
|
* to have a parent equal to the present block’s parent’s parent
|
||||||
* (such blocks are known as uncles).
|
* (such blocks are known as uncles).
|
||||||
*
|
*
|
||||||
* www.ethereumJ.com
|
* www.ethereumJ.com
|
||||||
|
@ -30,7 +30,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
public class Block {
|
public class Block {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("block");
|
private static final Logger logger = LoggerFactory.getLogger("block");
|
||||||
|
|
||||||
public static BigInteger BLOCK_REWARD = BigInteger.valueOf(1500000000000000000L);
|
public static BigInteger BLOCK_REWARD = BigInteger.valueOf(1500000000000000000L);
|
||||||
public static BigInteger UNCLE_REWARD = BLOCK_REWARD.multiply(
|
public static BigInteger UNCLE_REWARD = BLOCK_REWARD.multiply(
|
||||||
BigInteger.valueOf(15)).divide(BigInteger.valueOf(16));
|
BigInteger.valueOf(15)).divide(BigInteger.valueOf(16));
|
||||||
|
@ -38,28 +38,28 @@ public class Block {
|
||||||
.divide(BigInteger.valueOf(32));
|
.divide(BigInteger.valueOf(32));
|
||||||
|
|
||||||
private BlockHeader header;
|
private BlockHeader header;
|
||||||
|
|
||||||
/* Transactions */
|
/* Transactions */
|
||||||
private List<Transaction> transactionsList = new CopyOnWriteArrayList<>();
|
private List<Transaction> transactionsList = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
/* Uncles */
|
/* Uncles */
|
||||||
private List<BlockHeader> uncleList = new CopyOnWriteArrayList<>();
|
private List<BlockHeader> uncleList = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
/* Private */
|
/* Private */
|
||||||
|
|
||||||
private byte[] rlpEncoded;
|
private byte[] rlpEncoded;
|
||||||
private boolean parsed = false;
|
private boolean parsed = false;
|
||||||
|
|
||||||
private Trie txsState;
|
private Trie txsState;
|
||||||
|
|
||||||
/* Constructors */
|
/* Constructors */
|
||||||
|
|
||||||
public Block(byte[] rawData) {
|
public Block(byte[] rawData) {
|
||||||
logger.debug("new from [" + Hex.toHexString(rawData) + "]");
|
logger.debug("new from [" + Hex.toHexString(rawData) + "]");
|
||||||
this.rlpEncoded = rawData;
|
this.rlpEncoded = rawData;
|
||||||
this.parsed = false;
|
this.parsed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase, byte[] logsBloom,
|
public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase, byte[] logsBloom,
|
||||||
byte[] difficulty, long number, long gasLimit,
|
byte[] difficulty, long number, long gasLimit,
|
||||||
long gasUsed, long timestamp, byte[] extraData, byte[] nonce,
|
long gasUsed, long timestamp, byte[] extraData, byte[] nonce,
|
||||||
|
@ -85,11 +85,11 @@ public class Block {
|
||||||
|
|
||||||
RLPList params = RLP.decode2(rlpEncoded);
|
RLPList params = RLP.decode2(rlpEncoded);
|
||||||
RLPList block = (RLPList) params.get(0);
|
RLPList block = (RLPList) params.get(0);
|
||||||
|
|
||||||
// Parse Header
|
// Parse Header
|
||||||
RLPList header = (RLPList) block.get(0);
|
RLPList header = (RLPList) block.get(0);
|
||||||
this.header = new BlockHeader(header);
|
this.header = new BlockHeader(header);
|
||||||
|
|
||||||
// Parse Transactions
|
// Parse Transactions
|
||||||
RLPList txTransactions = (RLPList) block.get(1);
|
RLPList txTransactions = (RLPList) block.get(1);
|
||||||
this.parseTxs(this.header.getTxTrieRoot(), txTransactions);
|
this.parseTxs(this.header.getTxTrieRoot(), txTransactions);
|
||||||
|
@ -131,7 +131,7 @@ public class Block {
|
||||||
return FastByteComparisons.compareTo(result, 0, 32, target, 0, 32) < 0;
|
return FastByteComparisons.compareTo(result, 0, 32, target, 0, 32) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public byte[] getParentHash() {
|
public byte[] getParentHash() {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
return this.header.getParentHash();
|
return this.header.getParentHash();
|
||||||
|
@ -151,7 +151,7 @@ public class Block {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
return this.header.getStateRoot();
|
return this.header.getStateRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStateRoot(byte[] stateRoot) {
|
public void setStateRoot(byte[] stateRoot) {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
this.header.setStateRoot(stateRoot);
|
this.header.setStateRoot(stateRoot);
|
||||||
|
@ -180,12 +180,12 @@ public class Block {
|
||||||
}
|
}
|
||||||
return calcDifficulty;
|
return calcDifficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimestamp() {
|
public long getTimestamp() {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
return this.header.getTimestamp();
|
return this.header.getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getNumber() {
|
public long getNumber() {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
return this.header.getNumber();
|
return this.header.getNumber();
|
||||||
|
@ -210,7 +210,7 @@ public class Block {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
return this.header.getNonce();
|
return this.header.getNonce();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNonce(byte[] nonce) {
|
public void setNonce(byte[] nonce) {
|
||||||
this.header.setNonce(nonce);
|
this.header.setNonce(nonce);
|
||||||
rlpEncoded = null;
|
rlpEncoded = null;
|
||||||
|
@ -228,7 +228,7 @@ public class Block {
|
||||||
|
|
||||||
private StringBuffer toStringBuff = new StringBuffer();
|
private StringBuffer toStringBuff = new StringBuffer();
|
||||||
// [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root,
|
// [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root,
|
||||||
// difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp,
|
// difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp,
|
||||||
// extradata, nonce]
|
// extradata, nonce]
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -260,7 +260,7 @@ public class Block {
|
||||||
toStringBuff.append("BlockData [");
|
toStringBuff.append("BlockData [");
|
||||||
toStringBuff.append("hash=" + ByteUtil.toHexString(this.getHash())).append("");
|
toStringBuff.append("hash=" + ByteUtil.toHexString(this.getHash())).append("");
|
||||||
toStringBuff.append(header.toFlatString());
|
toStringBuff.append(header.toFlatString());
|
||||||
|
|
||||||
for (Transaction tx : getTransactionsList()) {
|
for (Transaction tx : getTransactionsList()) {
|
||||||
toStringBuff.append("\n");
|
toStringBuff.append("\n");
|
||||||
toStringBuff.append(tx.toString());
|
toStringBuff.append(tx.toString());
|
||||||
|
@ -318,7 +318,7 @@ public class Block {
|
||||||
this.getHeader().setUnclesHash( SHA3Helper.sha3( getUnclesEncoded() ));
|
this.getHeader().setUnclesHash( SHA3Helper.sha3( getUnclesEncoded() ));
|
||||||
rlpEncoded = null;
|
rlpEncoded = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
if(rlpEncoded == null) {
|
if(rlpEncoded == null) {
|
||||||
byte[] header = this.header.getEncoded();
|
byte[] header = this.header.getEncoded();
|
||||||
|
@ -328,7 +328,7 @@ public class Block {
|
||||||
}
|
}
|
||||||
return rlpEncoded;
|
return rlpEncoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncodedWithoutNonce() {
|
public byte[] getEncodedWithoutNonce() {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
byte[] header = this.header.getEncodedWithoutNonce();
|
byte[] header = this.header.getEncodedWithoutNonce();
|
||||||
|
|
|
@ -11,23 +11,23 @@ import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
|
||||||
import static org.ethereum.util.ByteUtil.toHexString;
|
import static org.ethereum.util.ByteUtil.toHexString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block header is a value object containing
|
* Block header is a value object containing
|
||||||
* the basic information of a block
|
* the basic information of a block
|
||||||
*/
|
*/
|
||||||
public class BlockHeader {
|
public class BlockHeader {
|
||||||
|
|
||||||
|
|
||||||
/* The SHA3 256-bit hash of the parent block, in its entirety */
|
/* The SHA3 256-bit hash of the parent block, in its entirety */
|
||||||
private byte[] parentHash;
|
private byte[] parentHash;
|
||||||
/* The SHA3 256-bit hash of the uncles list portion of this block */
|
/* The SHA3 256-bit hash of the uncles list portion of this block */
|
||||||
private byte[] unclesHash;
|
private byte[] unclesHash;
|
||||||
/* The 160-bit address to which all fees collected from the
|
/* The 160-bit address to which all fees collected from the
|
||||||
* successful mining of this block be transferred; formally */
|
* successful mining of this block be transferred; formally */
|
||||||
private byte[] coinbase;
|
private byte[] coinbase;
|
||||||
/* The SHA3 256-bit hash of the root node of the state trie,
|
/* The SHA3 256-bit hash of the root node of the state trie,
|
||||||
* after all transactions are executed and finalisations applied */
|
* after all transactions are executed and finalisations applied */
|
||||||
private byte[] stateRoot;
|
private byte[] stateRoot;
|
||||||
/* The SHA3 256-bit hash of the root node of the trie structure
|
/* The SHA3 256-bit hash of the root node of the trie structure
|
||||||
* populated with each transaction in the transaction
|
* populated with each transaction in the transaction
|
||||||
* list portion, the trie is populate by [key, val] --> [rlp(index), rlp(tx_reciepe)]
|
* list portion, the trie is populate by [key, val] --> [rlp(index), rlp(tx_reciepe)]
|
||||||
* of the block */
|
* of the block */
|
||||||
|
@ -41,33 +41,33 @@ public class BlockHeader {
|
||||||
/*todo: comment it when you know what the fuck it is*/
|
/*todo: comment it when you know what the fuck it is*/
|
||||||
private byte[] logsBloom;
|
private byte[] logsBloom;
|
||||||
/* A scalar value corresponding to the difficulty level of this block.
|
/* A scalar value corresponding to the difficulty level of this block.
|
||||||
* This can be calculated from the previous block’s difficulty level
|
* This can be calculated from the previous block’s difficulty level
|
||||||
* and the timestamp */
|
* and the timestamp */
|
||||||
private byte[] difficulty;
|
private byte[] difficulty;
|
||||||
/* A scalar value equal to the reasonable output of Unix's time()
|
/* A scalar value equal to the reasonable output of Unix's time()
|
||||||
* at this block's inception */
|
* at this block's inception */
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
/* A scalar value equal to the number of ancestor blocks.
|
/* A scalar value equal to the number of ancestor blocks.
|
||||||
* The genesis block has a number of zero */
|
* The genesis block has a number of zero */
|
||||||
private long number;
|
private long number;
|
||||||
/* A scalar value equal to the current limit of gas expenditure per block */
|
/* A scalar value equal to the current limit of gas expenditure per block */
|
||||||
private long gasLimit;
|
private long gasLimit;
|
||||||
/* A scalar value equal to the total gas used in transactions in this block */
|
/* A scalar value equal to the total gas used in transactions in this block */
|
||||||
private long gasUsed;
|
private long gasUsed;
|
||||||
/* An arbitrary byte array containing data relevant to this block.
|
/* An arbitrary byte array containing data relevant to this block.
|
||||||
* With the exception of the genesis block, this must be 32 bytes or fewer */
|
* With the exception of the genesis block, this must be 32 bytes or fewer */
|
||||||
private byte[] extraData;
|
private byte[] extraData;
|
||||||
/* A 256-bit hash which proves that a sufficient amount
|
/* A 256-bit hash which proves that a sufficient amount
|
||||||
* of computation has been carried out on this block */
|
* of computation has been carried out on this block */
|
||||||
private byte[] nonce;
|
private byte[] nonce;
|
||||||
|
|
||||||
public BlockHeader(RLPList rlpHeader) {
|
public BlockHeader(RLPList rlpHeader) {
|
||||||
|
|
||||||
this.parentHash = rlpHeader.get(0).getRLPData();
|
this.parentHash = rlpHeader.get(0).getRLPData();
|
||||||
this.unclesHash = rlpHeader.get(1).getRLPData();
|
this.unclesHash = rlpHeader.get(1).getRLPData();
|
||||||
this.coinbase = rlpHeader.get(2).getRLPData();
|
this.coinbase = rlpHeader.get(2).getRLPData();
|
||||||
this.stateRoot = rlpHeader.get(3).getRLPData();
|
this.stateRoot = rlpHeader.get(3).getRLPData();
|
||||||
|
|
||||||
this.txTrieRoot = rlpHeader.get(4).getRLPData();
|
this.txTrieRoot = rlpHeader.get(4).getRLPData();
|
||||||
if(this.txTrieRoot == null)
|
if(this.txTrieRoot == null)
|
||||||
this.txTrieRoot = EMPTY_TRIE_HASH;
|
this.txTrieRoot = EMPTY_TRIE_HASH;
|
||||||
|
@ -78,23 +78,23 @@ public class BlockHeader {
|
||||||
|
|
||||||
this.logsBloom = rlpHeader.get(6).getRLPData();
|
this.logsBloom = rlpHeader.get(6).getRLPData();
|
||||||
this.difficulty = rlpHeader.get(7).getRLPData();
|
this.difficulty = rlpHeader.get(7).getRLPData();
|
||||||
|
|
||||||
byte[] nrBytes = rlpHeader.get(8).getRLPData();
|
byte[] nrBytes = rlpHeader.get(8).getRLPData();
|
||||||
byte[] glBytes = rlpHeader.get(9).getRLPData();
|
byte[] glBytes = rlpHeader.get(9).getRLPData();
|
||||||
byte[] guBytes = rlpHeader.get(10).getRLPData();
|
byte[] guBytes = rlpHeader.get(10).getRLPData();
|
||||||
byte[] tsBytes = rlpHeader.get(11).getRLPData();
|
byte[] tsBytes = rlpHeader.get(11).getRLPData();
|
||||||
|
|
||||||
this.number = nrBytes == null ? 0 : (new BigInteger(1, nrBytes)).longValue();
|
this.number = nrBytes == null ? 0 : (new BigInteger(1, nrBytes)).longValue();
|
||||||
|
|
||||||
this.gasLimit = glBytes == null ? 0 : (new BigInteger(1, glBytes)).longValue();
|
this.gasLimit = glBytes == null ? 0 : (new BigInteger(1, glBytes)).longValue();
|
||||||
this.gasUsed = guBytes == null ? 0 : (new BigInteger(1, guBytes)).longValue();
|
this.gasUsed = guBytes == null ? 0 : (new BigInteger(1, guBytes)).longValue();
|
||||||
this.timestamp = tsBytes == null ? 0 : (new BigInteger(1, tsBytes)).longValue();
|
this.timestamp = tsBytes == null ? 0 : (new BigInteger(1, tsBytes)).longValue();
|
||||||
|
|
||||||
this.extraData = rlpHeader.get(12).getRLPData();
|
this.extraData = rlpHeader.get(12).getRLPData();
|
||||||
this.nonce = rlpHeader.get(13).getRLPData();
|
this.nonce = rlpHeader.get(13).getRLPData();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockHeader(byte[] parentHash, byte[] unclesHash, byte[] coinbase,
|
public BlockHeader(byte[] parentHash, byte[] unclesHash, byte[] coinbase,
|
||||||
byte[] logsBloom, byte[] difficulty, long number,
|
byte[] logsBloom, byte[] difficulty, long number,
|
||||||
long gasLimit, long gasUsed, long timestamp,
|
long gasLimit, long gasUsed, long timestamp,
|
||||||
|
@ -112,10 +112,10 @@ public class BlockHeader {
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
this.stateRoot = HashUtil.EMPTY_TRIE_HASH;
|
this.stateRoot = HashUtil.EMPTY_TRIE_HASH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate Difficulty
|
* Calculate Difficulty
|
||||||
* See Yellow Paper: http://www.gavwood.com/Paper.pdf - page 5, 4.3.4 (24)
|
* See Yellow Paper: http://www.gavwood.com/Paper.pdf - page 5, 4.3.4 (24)
|
||||||
* @return byte array value of the difficulty
|
* @return byte array value of the difficulty
|
||||||
*/
|
*/
|
||||||
|
@ -217,15 +217,15 @@ public class BlockHeader {
|
||||||
public void setNonce(byte[] nonce) {
|
public void setNonce(byte[] nonce) {
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
return this.getEncoded(true); // with nonce
|
return this.getEncoded(true); // with nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncodedWithoutNonce() {
|
public byte[] getEncodedWithoutNonce() {
|
||||||
return this.getEncoded(false);
|
return this.getEncoded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncoded(boolean withNonce) {
|
public byte[] getEncoded(boolean withNonce) {
|
||||||
byte[] parentHash = RLP.encodeElement(this.parentHash);
|
byte[] parentHash = RLP.encodeElement(this.parentHash);
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ public class BlockHeader {
|
||||||
|
|
||||||
|
|
||||||
private StringBuffer toStringBuff = new StringBuffer();
|
private StringBuffer toStringBuff = new StringBuffer();
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
||||||
toStringBuff.setLength(0);
|
toStringBuff.setLength(0);
|
||||||
|
@ -281,7 +281,7 @@ public class BlockHeader {
|
||||||
toStringBuff.append(" nonce=" + toHexString(nonce)).append("\n");
|
toStringBuff.append(" nonce=" + toHexString(nonce)).append("\n");
|
||||||
return toStringBuff.toString();
|
return toStringBuff.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toFlatString() {
|
public String toFlatString() {
|
||||||
toStringBuff.append(" parentHash=" + toHexString(parentHash)).append("");
|
toStringBuff.append(" parentHash=" + toHexString(parentHash)).append("");
|
||||||
toStringBuff.append(" unclesHash=" + toHexString(unclesHash)).append("");
|
toStringBuff.append(" unclesHash=" + toHexString(unclesHash)).append("");
|
||||||
|
|
|
@ -29,14 +29,14 @@ import static org.ethereum.config.SystemProperties.CONFIG;
|
||||||
import static org.ethereum.core.Denomination.SZABO;
|
import static org.ethereum.core.Denomination.SZABO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Ethereum blockchain is in many ways similar to the Bitcoin blockchain,
|
* The Ethereum blockchain is in many ways similar to the Bitcoin blockchain,
|
||||||
* although it does have some differences.
|
* although it does have some differences.
|
||||||
*
|
*
|
||||||
* The main difference between Ethereum and Bitcoin with regard to the blockchain architecture
|
* The main difference between Ethereum and Bitcoin with regard to the blockchain architecture
|
||||||
* is that, unlike Bitcoin, Ethereum blocks contain a copy of both the transaction list
|
* is that, unlike Bitcoin, Ethereum blocks contain a copy of both the transaction list
|
||||||
* and the most recent state. Aside from that, two other values, the block number and
|
* and the most recent state. Aside from that, two other values, the block number and
|
||||||
* the difficulty, are also stored in the block.
|
* the difficulty, are also stored in the block.
|
||||||
*
|
*
|
||||||
* The block validation algorithm in Ethereum is as follows:
|
* The block validation algorithm in Ethereum is as follows:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>Check if the previous block referenced exists and is valid.</li>
|
* <li>Check if the previous block referenced exists and is valid.</li>
|
||||||
|
@ -44,9 +44,9 @@ import static org.ethereum.core.Denomination.SZABO;
|
||||||
* <li>Check that the block number, difficulty, transaction root, uncle root and gas limit (various low-level Ethereum-specific concepts) are valid.</li>
|
* <li>Check that the block number, difficulty, transaction root, uncle root and gas limit (various low-level Ethereum-specific concepts) are valid.</li>
|
||||||
* <li>Check that the proof of work on the block is valid.</li>
|
* <li>Check that the proof of work on the block is valid.</li>
|
||||||
* <li>Let S[0] be the STATE_ROOT of the previous block.</li>
|
* <li>Let S[0] be the STATE_ROOT of the previous block.</li>
|
||||||
* <li>Let TX be the block's transaction list, with n transactions.
|
* <li>Let TX be the block's transaction list, with n transactions.
|
||||||
* For all in in 0...n-1, set S[i+1] = APPLY(S[i],TX[i]).
|
* For all in in 0...n-1, set S[i+1] = APPLY(S[i],TX[i]).
|
||||||
* If any applications returns an error, or if the total gas consumed in the block
|
* If any applications returns an error, or if the total gas consumed in the block
|
||||||
* up until this point exceeds the GASLIMIT, return an error.</li>
|
* up until this point exceeds the GASLIMIT, return an error.</li>
|
||||||
* <li>Let S_FINAL be S[n], but adding the block reward paid to the miner.</li>
|
* <li>Let S_FINAL be S[n], but adding the block reward paid to the miner.</li>
|
||||||
* <li>Check if S_FINAL is the same as the STATE_ROOT. If it is, the block is valid; otherwise, it is not valid.</li>
|
* <li>Check if S_FINAL is the same as the STATE_ROOT. If it is, the block is valid; otherwise, it is not valid.</li>
|
||||||
|
@ -66,7 +66,7 @@ public class BlockchainImpl implements Blockchain {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("blockchain");
|
private static final Logger logger = LoggerFactory.getLogger("blockchain");
|
||||||
private static final Logger stateLogger = LoggerFactory.getLogger("state");
|
private static final Logger stateLogger = LoggerFactory.getLogger("state");
|
||||||
|
|
||||||
// to avoid using minGasPrice=0 from Genesis for the wallet
|
// to avoid using minGasPrice=0 from Genesis for the wallet
|
||||||
private static final long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();
|
private static final long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ public class BlockchainImpl implements Blockchain {
|
||||||
public byte[] getBestBlockHash() {
|
public byte[] getBestBlockHash() {
|
||||||
return getBestBlock().getHash();
|
return getBestBlock().getHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize() {
|
public long getSize() {
|
||||||
return bestBlock.getNumber() + 1;
|
return bestBlock.getNumber() + 1;
|
||||||
|
@ -313,7 +313,7 @@ public class BlockchainImpl implements Blockchain {
|
||||||
logger.warn("Invalid block with nr: {}", block.getNumber());
|
logger.warn("Invalid block with nr: {}", block.getNumber());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TransactionReceipt> applyBlock(Block block) {
|
private List<TransactionReceipt> applyBlock(Block block) {
|
||||||
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
@ -363,15 +363,15 @@ public class BlockchainImpl implements Blockchain {
|
||||||
/**
|
/**
|
||||||
* Add reward to block- and every uncle coinbase
|
* Add reward to block- and every uncle coinbase
|
||||||
* assuming the entire block is valid.
|
* assuming the entire block is valid.
|
||||||
*
|
*
|
||||||
* @param block object containing the header and uncles
|
* @param block object containing the header and uncles
|
||||||
*/
|
*/
|
||||||
private void addReward(Block block) {
|
private void addReward(Block block) {
|
||||||
|
|
||||||
// Add standard block reward
|
// Add standard block reward
|
||||||
BigInteger totalBlockReward = Block.BLOCK_REWARD;
|
BigInteger totalBlockReward = Block.BLOCK_REWARD;
|
||||||
|
|
||||||
// Add extra rewards based on number of uncles
|
// Add extra rewards based on number of uncles
|
||||||
if(block.getUncleList().size() > 0) {
|
if(block.getUncleList().size() > 0) {
|
||||||
for (BlockHeader uncle : block.getUncleList()) {
|
for (BlockHeader uncle : block.getUncleList()) {
|
||||||
track.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
|
track.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
|
||||||
|
@ -381,7 +381,7 @@ public class BlockchainImpl implements Blockchain {
|
||||||
}
|
}
|
||||||
track.addBalance(block.getCoinbase(), totalBlockReward);
|
track.addBalance(block.getCoinbase(), totalBlockReward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeBlock(Block block, List<TransactionReceipt> receipts) {
|
public void storeBlock(Block block, List<TransactionReceipt> receipts) {
|
||||||
|
|
||||||
|
@ -408,8 +408,8 @@ public class BlockchainImpl implements Blockchain {
|
||||||
logger.debug("block added to the blockChain: index: [{}]", block.getNumber());
|
logger.debug("block added to the blockChain: index: [{}]", block.getNumber());
|
||||||
if (block.getNumber() % 100 == 0)
|
if (block.getNumber() % 100 == 0)
|
||||||
logger.info("*** Last block added [ #{} ]", block.getNumber());
|
logger.info("*** Last block added [ #{} ]", block.getNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean hasParentOnTheChain(Block block){
|
public boolean hasParentOnTheChain(Block block){
|
||||||
return getParent(block.getHeader()) != null;
|
return getParent(block.getHeader()) != null;
|
||||||
|
|
|
@ -12,10 +12,10 @@ public enum Denomination {
|
||||||
FINNY(newBigInt(15)),
|
FINNY(newBigInt(15)),
|
||||||
ETHER(newBigInt(18)),
|
ETHER(newBigInt(18)),
|
||||||
EINSTEIN(newBigInt(21)),
|
EINSTEIN(newBigInt(21)),
|
||||||
DOUGLAS(newBigInt(42));
|
DOUGLAS(newBigInt(42));
|
||||||
|
|
||||||
private BigInteger amount;
|
private BigInteger amount;
|
||||||
|
|
||||||
private Denomination(BigInteger value) {
|
private Denomination(BigInteger value) {
|
||||||
this.amount = value;
|
this.amount = value;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public enum Denomination {
|
||||||
public BigInteger value() {
|
public BigInteger value() {
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long longValue() {
|
public long longValue() {
|
||||||
return value().longValue();
|
return value().longValue();
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public enum Denomination {
|
||||||
private static BigInteger newBigInt(int value) {
|
private static BigInteger newBigInt(int value) {
|
||||||
return BigInteger.valueOf(10).pow(value);
|
return BigInteger.valueOf(10).pow(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toFriendlyString(BigInteger value) {
|
public static String toFriendlyString(BigInteger value) {
|
||||||
if(value.compareTo(DOUGLAS.value()) == 1 || value.compareTo(DOUGLAS.value()) == 0) {
|
if(value.compareTo(DOUGLAS.value()) == 1 || value.compareTo(DOUGLAS.value()) == 0) {
|
||||||
return Float.toString(value.divide(DOUGLAS.value()).floatValue()) + " DOUGLAS";
|
return Float.toString(value.divide(DOUGLAS.value()).floatValue()) + " DOUGLAS";
|
||||||
|
|
|
@ -10,26 +10,26 @@ import org.ethereum.trie.TrieImpl;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The genesis block is the first block in the chain and has fixed values according to
|
* The genesis block is the first block in the chain and has fixed values according to
|
||||||
* the protocol specification. The genesis block is 13 items, and is specified thus:
|
* the protocol specification. The genesis block is 13 items, and is specified thus:
|
||||||
*
|
*
|
||||||
* ( zerohash_256 , SHA3 RLP () , zerohash_160 , stateRoot, 0, 2^22 , 0, 0, 1000000, 0, 0, 0, SHA3 (42) , (), () )
|
* ( zerohash_256 , SHA3 RLP () , zerohash_160 , stateRoot, 0, 2^22 , 0, 0, 1000000, 0, 0, 0, SHA3 (42) , (), () )
|
||||||
*
|
*
|
||||||
* - Where zerohash_256 refers to the parent hash, a 256-bit hash which is all zeroes;
|
* - Where zerohash_256 refers to the parent hash, a 256-bit hash which is all zeroes;
|
||||||
* - zerohash_160 refers to the coinbase address, a 160-bit hash which is all zeroes;
|
* - zerohash_160 refers to the coinbase address, a 160-bit hash which is all zeroes;
|
||||||
* - 2^22 refers to the difficulty;
|
* - 2^22 refers to the difficulty;
|
||||||
* - 0 refers to the timestamp (the Unix epoch);
|
* - 0 refers to the timestamp (the Unix epoch);
|
||||||
* - the transaction trie root and extradata are both 0, being equivalent to the empty byte array.
|
* - the transaction trie root and extradata are both 0, being equivalent to the empty byte array.
|
||||||
* - The sequences of both uncles and transactions are empty and represented by ().
|
* - The sequences of both uncles and transactions are empty and represented by ().
|
||||||
* - SHA3 (42) refers to the SHA3 hash of a byte array of length one whose first and only byte is of value 42.
|
* - SHA3 (42) refers to the SHA3 hash of a byte array of length one whose first and only byte is of value 42.
|
||||||
* - SHA3 RLP () value refers to the hash of the uncle lists in RLP, both empty lists.
|
* - SHA3 RLP () value refers to the hash of the uncle lists in RLP, both empty lists.
|
||||||
*
|
*
|
||||||
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (Appendix I. Genesis Block)
|
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (Appendix I. Genesis Block)
|
||||||
*/
|
*/
|
||||||
public class Genesis extends Block {
|
public class Genesis extends Block {
|
||||||
|
|
||||||
public final static BigInteger PREMINE_AMOUNT = BigInteger.valueOf(2).pow(200);
|
public final static BigInteger PREMINE_AMOUNT = BigInteger.valueOf(2).pow(200);
|
||||||
|
|
||||||
private static String[] premine = new String[] {
|
private static String[] premine = new String[] {
|
||||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||||
"e6716f9544a56c530d868e4bfbacb172315bdead", // # (J)
|
"e6716f9544a56c530d868e4bfbacb172315bdead", // # (J)
|
||||||
|
@ -56,14 +56,14 @@ public class Genesis extends Block {
|
||||||
public static long TIMESTAMP = 0;
|
public static long TIMESTAMP = 0;
|
||||||
public static byte[] EXTRA_DATA = new byte[0];
|
public static byte[] EXTRA_DATA = new byte[0];
|
||||||
public static byte[] NONCE = sha3(new byte[]{42});
|
public static byte[] NONCE = sha3(new byte[]{42});
|
||||||
|
|
||||||
private static Block instance;
|
private static Block instance;
|
||||||
|
|
||||||
private Genesis() {
|
private Genesis() {
|
||||||
super(PARENT_HASH, UNCLES_HASH, COINBASE, LOG_BLOOM, DIFFICULTY,
|
super(PARENT_HASH, UNCLES_HASH, COINBASE, LOG_BLOOM, DIFFICULTY,
|
||||||
NUMBER, GAS_LIMIT, GAS_USED, TIMESTAMP,
|
NUMBER, GAS_LIMIT, GAS_USED, TIMESTAMP,
|
||||||
EXTRA_DATA, NONCE, null, null);
|
EXTRA_DATA, NONCE, null, null);
|
||||||
|
|
||||||
Trie state = new TrieImpl(null);
|
Trie state = new TrieImpl(null);
|
||||||
// The proof-of-concept series include a development pre-mine, making the state root hash
|
// The proof-of-concept series include a development pre-mine, making the state root hash
|
||||||
// some value stateRoot. The latest documentation should be consulted for the value of the state root.
|
// some value stateRoot. The latest documentation should be consulted for the value of the state root.
|
||||||
|
@ -74,14 +74,14 @@ public class Genesis extends Block {
|
||||||
|
|
||||||
setStateRoot(state.getRootHash());
|
setStateRoot(state.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Block getInstance() {
|
public static Block getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new Genesis();
|
instance = new Genesis();
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static String[] getPremine() {
|
public final static String[] getPremine() {
|
||||||
return premine;
|
return premine;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,17 @@ import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
||||||
import static org.ethereum.util.ByteUtil.ZERO_BYTE_ARRAY;
|
import static org.ethereum.util.ByteUtil.ZERO_BYTE_ARRAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction (formally, T) is a single cryptographically
|
* A transaction (formally, T) is a single cryptographically
|
||||||
* signed instruction sent by an actor external to Ethereum.
|
* signed instruction sent by an actor external to Ethereum.
|
||||||
* An external actor can be a person (via a mobile device or desktop computer)
|
* An external actor can be a person (via a mobile device or desktop computer)
|
||||||
* or could be from a piece of automated software running on a server.
|
* or could be from a piece of automated software running on a server.
|
||||||
* There are two types of transactions: those which result in message calls
|
* There are two types of transactions: those which result in message calls
|
||||||
* and those which result in the creation of new contracts.
|
* and those which result in the creation of new contracts.
|
||||||
*/
|
*/
|
||||||
public class Transaction {
|
public class Transaction {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Transaction.class);
|
private static final Logger logger = LoggerFactory.getLogger(Transaction.class);
|
||||||
|
|
||||||
/* SHA3 hash of the RLP encoded transaction */
|
/* SHA3 hash of the RLP encoded transaction */
|
||||||
private byte[] hash;
|
private byte[] hash;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class Transaction {
|
||||||
/* Tx in encoded form */
|
/* Tx in encoded form */
|
||||||
private byte[] rlpEncoded;
|
private byte[] rlpEncoded;
|
||||||
private byte[] rlpRaw;
|
private byte[] rlpRaw;
|
||||||
/* Indicates if this transaction has been parsed
|
/* Indicates if this transaction has been parsed
|
||||||
* from the RLP-encoded data */
|
* from the RLP-encoded data */
|
||||||
private boolean parsed = false;
|
private boolean parsed = false;
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ public class Transaction {
|
||||||
byte[] data = RLP.encodeElement(this.data);
|
byte[] data = RLP.encodeElement(this.data);
|
||||||
|
|
||||||
byte[] v, r, s;
|
byte[] v, r, s;
|
||||||
|
|
||||||
if(signature != null) {
|
if(signature != null) {
|
||||||
v = RLP.encodeByte( signature.v );
|
v = RLP.encodeByte( signature.v );
|
||||||
r = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.r));
|
r = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.r));
|
||||||
|
|
|
@ -13,9 +13,9 @@ import java.util.List;
|
||||||
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class Wallet {
|
||||||
// TODO: a) the values I need to keep for address state is balance & nonce & ECKey
|
// TODO: a) the values I need to keep for address state is balance & nonce & ECKey
|
||||||
// TODO: b) keep it to be easy accessed by the toAddress()
|
// TODO: b) keep it to be easy accessed by the toAddress()
|
||||||
// private HashMap<Address, BigInteger> rows = new HashMap<>();
|
// private HashMap<Address, BigInteger> rows = new HashMap<>();
|
||||||
|
|
||||||
// This map of transaction designed
|
// This map of transaction designed
|
||||||
// to approve the tx by external trusted peer
|
// to approve the tx by external trusted peer
|
||||||
private Map<String, WalletTransaction> walletTransactions = new ConcurrentHashMap<>();
|
private Map<String, WalletTransaction> walletTransactions = new ConcurrentHashMap<>();
|
||||||
|
@ -141,13 +141,13 @@ public class Wallet {
|
||||||
|
|
||||||
return walletTransaction;
|
return walletTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTransactions(List<Transaction> transactions) {
|
public void addTransactions(List<Transaction> transactions) {
|
||||||
for (Transaction transaction : transactions) {
|
for (Transaction transaction : transactions) {
|
||||||
this.addTransaction(transaction);
|
this.addTransaction(transaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeTransactions(List<Transaction> transactions) {
|
public void removeTransactions(List<Transaction> transactions) {
|
||||||
for (Transaction tx : transactions) {
|
for (Transaction tx : transactions) {
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
|
@ -182,7 +182,7 @@ public class Wallet {
|
||||||
Account receiver = rows.get(Hex.toHexString(receiveAddress));
|
Account receiver = rows.get(Hex.toHexString(receiveAddress));
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
receiver.addPendingTransaction(transaction);
|
receiver.addPendingTransaction(transaction);
|
||||||
|
|
||||||
logger.info("Pending transaction added to " +
|
logger.info("Pending transaction added to " +
|
||||||
"\n account: [{}], " +
|
"\n account: [{}], " +
|
||||||
"\n tx: [{}]",
|
"\n tx: [{}]",
|
||||||
|
@ -221,7 +221,7 @@ public class Wallet {
|
||||||
<value>900099909<value/>
|
<value>900099909<value/>
|
||||||
</row>
|
</row>
|
||||||
</wallet>
|
</wallet>
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
String dir = System.getProperty("user.dir");
|
String dir = System.getProperty("user.dir");
|
||||||
|
|
|
@ -50,7 +50,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
* Creating a new ECKey with the empty constructor will generate a new random keypair. Other static methods can be used
|
* Creating a new ECKey with the empty constructor will generate a new random keypair. Other static methods can be used
|
||||||
* when you already have the public or private parts. If you create a key with only the public part, you can check
|
* when you already have the public or private parts. If you create a key with only the public part, you can check
|
||||||
* signatures but not create them.</p>
|
* signatures but not create them.</p>
|
||||||
*
|
*
|
||||||
* <p>The ECDSA algorithm supports <i>key recovery</i> in which a signature plus a couple of discriminator bits can
|
* <p>The ECDSA algorithm supports <i>key recovery</i> in which a signature plus a couple of discriminator bits can
|
||||||
* be reversed to find the public key used to calculate it. This can be convenient when you have a message and a
|
* be reversed to find the public key used to calculate it. This can be convenient when you have a message and a
|
||||||
* signature and want to find out who signed it, rather than requiring the user to provide the expected identity.</p>
|
* signature and want to find out who signed it, rather than requiring the user to provide the expected identity.</p>
|
||||||
|
@ -70,7 +70,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
*/
|
*/
|
||||||
public class ECKey implements Serializable {
|
public class ECKey implements Serializable {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ECKey.class);
|
private static final Logger logger = LoggerFactory.getLogger(ECKey.class);
|
||||||
|
|
||||||
/** The parameters of the secp256k1 curve that Ethereum uses. */
|
/** The parameters of the secp256k1 curve that Ethereum uses. */
|
||||||
public static final ECDomainParameters CURVE;
|
public static final ECDomainParameters CURVE;
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ public class ECKey implements Serializable {
|
||||||
HALF_CURVE_ORDER = params.getN().shiftRight(1);
|
HALF_CURVE_ORDER = params.getN().shiftRight(1);
|
||||||
secureRandom = new SecureRandom();
|
secureRandom = new SecureRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The two parts of the key. If "priv" is set, "pub" can always be calculated. If "pub" is set but not "priv", we
|
// The two parts of the key. If "priv" is set, "pub" can always be calculated. If "pub" is set but not "priv", we
|
||||||
// can only verify signatures not make them.
|
// can only verify signatures not make them.
|
||||||
// TODO: Redesign this class to use consistent internals and more efficient serialization.
|
// TODO: Redesign this class to use consistent internals and more efficient serialization.
|
||||||
|
@ -129,7 +129,7 @@ public class ECKey implements Serializable {
|
||||||
throw new IllegalArgumentException("Public key may not be null");
|
throw new IllegalArgumentException("Public key may not be null");
|
||||||
this.pub = pub;
|
this.pub = pub;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility for compressing an elliptic curve point. Returns the same point if it's already compressed.
|
* Utility for compressing an elliptic curve point. Returns the same point if it's already compressed.
|
||||||
* See the ECKey class docs for a discussion of point compression.
|
* See the ECKey class docs for a discussion of point compression.
|
||||||
|
@ -145,7 +145,7 @@ public class ECKey implements Serializable {
|
||||||
public static ECPoint decompressPoint(ECPoint compressed) {
|
public static ECPoint decompressPoint(ECPoint compressed) {
|
||||||
return CURVE.getCurve().decodePoint(compressed.getEncoded(false));
|
return CURVE.getCurve().decodePoint(compressed.getEncoded(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow). Note that
|
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow). Note that
|
||||||
* the resulting public key is compressed.
|
* the resulting public key is compressed.
|
||||||
|
@ -197,7 +197,7 @@ public class ECKey implements Serializable {
|
||||||
public static ECKey fromPublicOnly(byte[] pub) {
|
public static ECKey fromPublicOnly(byte[] pub) {
|
||||||
return new ECKey(null, CURVE.getCurve().decodePoint(pub));
|
return new ECKey(null, CURVE.getCurve().decodePoint(pub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of this key, but with the public point represented in uncompressed form. Normally you would
|
* Returns a copy of this key, but with the public point represented in uncompressed form. Normally you would
|
||||||
* never need this: it's for specialised scenarios or when backwards compatibility in encoded form is necessary.
|
* never need this: it's for specialised scenarios or when backwards compatibility in encoded form is necessary.
|
||||||
|
@ -224,7 +224,7 @@ public class ECKey implements Serializable {
|
||||||
public boolean hasPrivKey() {
|
public boolean hasPrivKey() {
|
||||||
return priv != null;
|
return priv != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns public key bytes from the given private key. To convert a byte array into a BigInteger, use <tt>
|
* Returns public key bytes from the given private key. To convert a byte array into a BigInteger, use <tt>
|
||||||
* new BigInteger(1, bytes);</tt>
|
* new BigInteger(1, bytes);</tt>
|
||||||
|
@ -293,7 +293,7 @@ public class ECKey implements Serializable {
|
||||||
}
|
}
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Groups the two components that make up a signature, and provides a way to encode to Base64 form, which is
|
* Groups the two components that make up a signature, and provides a way to encode to Base64 form, which is
|
||||||
* how ECDSA signatures are represented when embedded in other data structures in the Ethereum protocol. The raw
|
* how ECDSA signatures are represented when embedded in other data structures in the Ethereum protocol. The raw
|
||||||
|
@ -311,11 +311,11 @@ public class ECKey implements Serializable {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.s = s;
|
this.s = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ECDSASignature fromComponents(byte[] r, byte[] s) {
|
private static ECDSASignature fromComponents(byte[] r, byte[] s) {
|
||||||
return new ECDSASignature(new BigInteger(1, r), new BigInteger(1, s));
|
return new ECDSASignature(new BigInteger(1, r), new BigInteger(1, s));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ECDSASignature fromComponents(byte[] r, byte[] s, byte v) {
|
public static ECDSASignature fromComponents(byte[] r, byte[] s, byte v) {
|
||||||
ECDSASignature signature = fromComponents(r, s);
|
ECDSASignature signature = fromComponents(r, s);
|
||||||
signature.v = v;
|
signature.v = v;
|
||||||
|
@ -341,7 +341,7 @@ public class ECKey implements Serializable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toBase64() {
|
public String toBase64() {
|
||||||
byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
|
byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
|
||||||
sigData[0] = v;
|
sigData[0] = v;
|
||||||
|
@ -349,7 +349,7 @@ public class ECKey implements Serializable {
|
||||||
System.arraycopy(bigIntegerToBytes(this.s, 32), 0, sigData, 33, 32);
|
System.arraycopy(bigIntegerToBytes(this.s, 32), 0, sigData, 33, 32);
|
||||||
return new String(Base64.encode(sigData), Charset.forName("UTF-8"));
|
return new String(Base64.encode(sigData), Charset.forName("UTF-8"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -370,11 +370,11 @@ public class ECKey implements Serializable {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the given hash and returns the R and S components as BigIntegers
|
* Signs the given hash and returns the R and S components as BigIntegers
|
||||||
* and put them in ECDSASignature
|
* and put them in ECDSASignature
|
||||||
*
|
*
|
||||||
* @param input to sign
|
* @param input to sign
|
||||||
* @return ECDSASignature signature that contains the R and S components
|
* @return ECDSASignature signature that contains the R and S components
|
||||||
*/
|
*/
|
||||||
|
@ -389,7 +389,7 @@ public class ECKey implements Serializable {
|
||||||
BigInteger[] components = signer.generateSignature(input);
|
BigInteger[] components = signer.generateSignature(input);
|
||||||
return new ECDSASignature(components[0], components[1]).toCanonicalised();
|
return new ECDSASignature(components[0], components[1]).toCanonicalised();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes the sha3 hash (32 bytes) of data and returns the ECDSA signature
|
* Takes the sha3 hash (32 bytes) of data and returns the ECDSA signature
|
||||||
*
|
*
|
||||||
|
@ -413,12 +413,12 @@ public class ECKey implements Serializable {
|
||||||
sig.v = (byte) (recId + 27 + (isCompressed() ? 4 : 0));
|
sig.v = (byte) (recId + 27 + (isCompressed() ? 4 : 0));
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a piece of text and a message signature encoded in base64, returns an ECKey
|
* Given a piece of text and a message signature encoded in base64, returns an ECKey
|
||||||
* containing the public key that was used to sign it. This can then be compared to the expected public key to
|
* containing the public key that was used to sign it. This can then be compared to the expected public key to
|
||||||
* determine if the signature was correct.
|
* determine if the signature was correct.
|
||||||
*
|
*
|
||||||
* @param messageHash a piece of human readable text that was signed
|
* @param messageHash a piece of human readable text that was signed
|
||||||
* @param signatureBase64 The Ethereum-format message signature in base64
|
* @param signatureBase64 The Ethereum-format message signature in base64
|
||||||
* @throws SignatureException If the public key could not be recovered or if there was a signature format error.
|
* @throws SignatureException If the public key could not be recovered or if there was a signature format error.
|
||||||
|
@ -453,10 +453,10 @@ public class ECKey implements Serializable {
|
||||||
throw new SignatureException("Could not recover public key from signature");
|
throw new SignatureException("Could not recover public key from signature");
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Verifies the given ECDSA signature against the message bytes using the public key bytes.</p>
|
* <p>Verifies the given ECDSA signature against the message bytes using the public key bytes.</p>
|
||||||
*
|
*
|
||||||
* <p>When using native ECDSA verification, data must be 32 bytes, and no element may be
|
* <p>When using native ECDSA verification, data must be 32 bytes, and no element may be
|
||||||
* larger than 520 bytes.</p>
|
* larger than 520 bytes.</p>
|
||||||
*
|
*
|
||||||
|
@ -471,7 +471,7 @@ public class ECKey implements Serializable {
|
||||||
try {
|
try {
|
||||||
return signer.verifySignature(data, signature.r, signature.s);
|
return signer.verifySignature(data, signature.r, signature.s);
|
||||||
} catch (NullPointerException npe) {
|
} catch (NullPointerException npe) {
|
||||||
// Bouncy Castle contains a bug that can cause NPEs given specially crafted signatures.
|
// Bouncy Castle contains a bug that can cause NPEs given specially crafted signatures.
|
||||||
// Those signatures are inherently invalid/attack sigs so we just fail them here rather than crash the thread.
|
// Those signatures are inherently invalid/attack sigs so we just fail them here rather than crash the thread.
|
||||||
logger.error("Caught NPE inside bouncy castle", npe);
|
logger.error("Caught NPE inside bouncy castle", npe);
|
||||||
return false;
|
return false;
|
||||||
|
@ -637,7 +637,7 @@ public class ECKey implements Serializable {
|
||||||
byte[] bits = getPubKey();
|
byte[] bits = getPubKey();
|
||||||
return (bits[0] & 0xFF) | ((bits[1] & 0xFF) << 8) | ((bits[2] & 0xFF) << 16) | ((bits[3] & 0xFF) << 24);
|
return (bits[0] & 0xFF) | ((bits[1] & 0xFF) << 8) | ((bits[2] & 0xFF) << 16) | ((bits[3] & 0xFF) << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public static class MissingPrivateKeyException extends RuntimeException {
|
public static class MissingPrivateKeyException extends RuntimeException {
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,14 @@ import org.ethereum.util.LRUMap;
|
||||||
|
|
||||||
public class HashUtil {
|
public class HashUtil {
|
||||||
|
|
||||||
private static final int MAX_ENTRIES = 100; // Should contain most commonly hashed values
|
private static final int MAX_ENTRIES = 100; // Should contain most commonly hashed values
|
||||||
private static LRUMap<ByteArrayWrapper, byte[]> sha3Cache = new LRUMap<>(0, MAX_ENTRIES);
|
private static LRUMap<ByteArrayWrapper, byte[]> sha3Cache = new LRUMap<>(0, MAX_ENTRIES);
|
||||||
public static final byte[] EMPTY_DATA_HASH = sha3(EMPTY_BYTE_ARRAY);
|
public static final byte[] EMPTY_DATA_HASH = sha3(EMPTY_BYTE_ARRAY);
|
||||||
public static final byte[] EMPTY_LIST_HASH = sha3(RLP.encodeList());
|
public static final byte[] EMPTY_LIST_HASH = sha3(RLP.encodeList());
|
||||||
public static final byte[] EMPTY_TRIE_HASH = sha3(RLP.encodeElement(EMPTY_BYTE_ARRAY));
|
public static final byte[] EMPTY_TRIE_HASH = sha3(RLP.encodeElement(EMPTY_BYTE_ARRAY));
|
||||||
|
|
||||||
private static final MessageDigest sha256digest;
|
private static final MessageDigest sha256digest;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
sha256digest = MessageDigest.getInstance("SHA-256");
|
sha256digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
@ -33,7 +33,7 @@ public class HashUtil {
|
||||||
throw new RuntimeException(e); // Can't happen.
|
throw new RuntimeException(e); // Can't happen.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] sha256(byte[] input) {
|
public static byte[] sha256(byte[] input) {
|
||||||
return sha256digest.digest(input);
|
return sha256digest.digest(input);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ public class HashUtil {
|
||||||
sha3Cache.put(inputByteArray, result);
|
sha3Cache.put(inputByteArray, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] ripemd160(byte[] message) {
|
public static byte[] ripemd160(byte[] message) {
|
||||||
Digest digest = new RIPEMD160Digest();
|
Digest digest = new RIPEMD160Digest();
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
|
@ -83,7 +83,7 @@ public class HashUtil {
|
||||||
|
|
||||||
return newAddress;
|
return newAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see #doubleDigest(byte[], int, int)
|
* @see #doubleDigest(byte[], int, int)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,9 +6,9 @@ import org.spongycastle.crypto.digests.SHA3Digest;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
public class SHA3Helper {
|
public class SHA3Helper {
|
||||||
|
|
||||||
private static int DEFAULT_SIZE = 256;
|
private static int DEFAULT_SIZE = 256;
|
||||||
|
|
||||||
public static String sha3String(String message) {
|
public static String sha3String(String message) {
|
||||||
return sha3String(message, new SHA3Digest(DEFAULT_SIZE), true);
|
return sha3String(message, new SHA3Digest(DEFAULT_SIZE), true);
|
||||||
}
|
}
|
||||||
|
@ -75,23 +75,23 @@ public class SHA3Helper {
|
||||||
digest.doFinal(hash, 0);
|
digest.doFinal(hash, 0);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Size {
|
public enum Size {
|
||||||
|
|
||||||
S224(224),
|
S224(224),
|
||||||
S256(256),
|
S256(256),
|
||||||
S384(384),
|
S384(384),
|
||||||
S512(512);
|
S512(512);
|
||||||
|
|
||||||
int bits = 0;
|
int bits = 0;
|
||||||
|
|
||||||
Size(int bits) {
|
Size(int bits) {
|
||||||
this.bits = bits;
|
this.bits = bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getValue() {
|
public int getValue() {
|
||||||
return this.bits;
|
return this.bits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class ByteArrayWrapper implements Comparable<ByteArrayWrapper> {
|
||||||
return false;
|
return false;
|
||||||
byte[] otherData = ((ByteArrayWrapper) other).getData();
|
byte[] otherData = ((ByteArrayWrapper) other).getData();
|
||||||
return FastByteComparisons.compareTo(
|
return FastByteComparisons.compareTo(
|
||||||
data, 0, data.length,
|
data, 0, data.length,
|
||||||
otherData, 0, otherData.length) == 0;
|
otherData, 0, otherData.length) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,10 +36,10 @@ public class ByteArrayWrapper implements Comparable<ByteArrayWrapper> {
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(ByteArrayWrapper o) {
|
public int compareTo(ByteArrayWrapper o) {
|
||||||
return FastByteComparisons.compareTo(
|
return FastByteComparisons.compareTo(
|
||||||
data, 0, data.length,
|
data, 0, data.length,
|
||||||
o.getData(), 0, o.getData().length);
|
o.getData(), 0, o.getData().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getData() {
|
public byte[] getData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class ContractDetails {
|
||||||
|
|
||||||
public ContractDetails() {
|
public ContractDetails() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContractDetails(byte[] rlpCode) {
|
public ContractDetails(byte[] rlpCode) {
|
||||||
decode(rlpCode);
|
decode(rlpCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,26 +4,26 @@ package org.ethereum.db;
|
||||||
* Ethereum generic database interface
|
* Ethereum generic database interface
|
||||||
*/
|
*/
|
||||||
public interface Database {
|
public interface Database {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get value from database
|
* Get value from database
|
||||||
*
|
*
|
||||||
* @param key for which to retrieve the value
|
* @param key for which to retrieve the value
|
||||||
* @return the value for the given key
|
* @return the value for the given key
|
||||||
*/
|
*/
|
||||||
public byte[] get(byte[] key);
|
public byte[] get(byte[] key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert value into database
|
* Insert value into database
|
||||||
*
|
*
|
||||||
* @param key for the given value
|
* @param key for the given value
|
||||||
* @param value to insert
|
* @param value to insert
|
||||||
*/
|
*/
|
||||||
public void put(byte[] key, byte[] value);
|
public void put(byte[] key, byte[] value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete key/value pair from database
|
* Delete key/value pair from database
|
||||||
*
|
*
|
||||||
* @param key for which to delete the value
|
* @param key for which to delete the value
|
||||||
*/
|
*/
|
||||||
public void delete(byte[] key);
|
public void delete(byte[] key);
|
||||||
|
|
|
@ -19,18 +19,18 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic interface for Ethereum database
|
* Generic interface for Ethereum database
|
||||||
*
|
*
|
||||||
* LevelDB key/value pair DB implementation will be used.
|
* LevelDB key/value pair DB implementation will be used.
|
||||||
* Choice must be made between:
|
* Choice must be made between:
|
||||||
* Pure Java: https://github.com/dain/leveldb
|
* Pure Java: https://github.com/dain/leveldb
|
||||||
* JNI binding: https://github.com/fusesource/leveldbjni
|
* JNI binding: https://github.com/fusesource/leveldbjni
|
||||||
*/
|
*/
|
||||||
public class DatabaseImpl implements Database {
|
public class DatabaseImpl implements Database {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("db");
|
private static final Logger logger = LoggerFactory.getLogger("db");
|
||||||
private DB db;
|
private DB db;
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public DatabaseImpl(String name) {
|
public DatabaseImpl(String name) {
|
||||||
// Initialize Database
|
// Initialize Database
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -69,9 +69,9 @@ public class DatabaseImpl implements Database {
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
logger.error(ioe.getMessage(), ioe);
|
logger.error(ioe.getMessage(), ioe);
|
||||||
throw new RuntimeException("Can't initialize database");
|
throw new RuntimeException("Can't initialize database");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroyDB(File fileLocation) {
|
public void destroyDB(File fileLocation) {
|
||||||
logger.debug("Destroying existing database");
|
logger.debug("Destroying existing database");
|
||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
|
@ -81,12 +81,12 @@ public class DatabaseImpl implements Database {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] get(byte[] key) {
|
public byte[] get(byte[] key) {
|
||||||
return db.get(key);
|
return db.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void put(byte[] key, byte[] value) {
|
public void put(byte[] key, byte[] value) {
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ public class DatabaseImpl implements Database {
|
||||||
Hex.toHexString(value));
|
Hex.toHexString(value));
|
||||||
db.put(key, value);
|
db.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(byte[] key) {
|
public void delete(byte[] key) {
|
||||||
if(logger.isDebugEnabled())
|
if(logger.isDebugEnabled())
|
||||||
|
@ -104,11 +104,11 @@ public class DatabaseImpl implements Database {
|
||||||
|
|
||||||
db.delete(key);
|
db.delete(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DBIterator iterator() {
|
public DBIterator iterator() {
|
||||||
return db.iterator();
|
return db.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DB getDb() {
|
public DB getDb() {
|
||||||
return this.db;
|
return this.db;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ public interface Repository {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new account in the database
|
* Create a new account in the database
|
||||||
*
|
*
|
||||||
* @param addr of the contract
|
* @param addr of the contract
|
||||||
* @return newly created account state
|
* @return newly created account state
|
||||||
*/
|
*/
|
||||||
|
@ -34,65 +34,65 @@ public interface Repository {
|
||||||
* false otherwise
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isExist(byte[] addr);
|
public boolean isExist(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an account
|
* Retrieve an account
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @return account state as stored in the database
|
* @return account state as stored in the database
|
||||||
*/
|
*/
|
||||||
public AccountState getAccountState(byte[] addr);
|
public AccountState getAccountState(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the account
|
* Deletes the account
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
*/
|
*/
|
||||||
public void delete(byte[] addr);
|
public void delete(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increase the account nonce of the given account by one
|
* Increase the account nonce of the given account by one
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @return new value of the nonce
|
* @return new value of the nonce
|
||||||
*/
|
*/
|
||||||
public BigInteger increaseNonce(byte[] addr);
|
public BigInteger increaseNonce(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current nonce of a given account
|
* Get current nonce of a given account
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @return value of the nonce
|
* @return value of the nonce
|
||||||
*/
|
*/
|
||||||
public BigInteger getNonce(byte[] addr);
|
public BigInteger getNonce(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve contract details for a given account from the database
|
* Retrieve contract details for a given account from the database
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @return new contract details
|
* @return new contract details
|
||||||
*/
|
*/
|
||||||
public ContractDetails getContractDetails(byte[] addr);
|
public ContractDetails getContractDetails(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store code associated with an account
|
* Store code associated with an account
|
||||||
*
|
*
|
||||||
* @param addr for the account
|
* @param addr for the account
|
||||||
* @param code that will be associated with this account
|
* @param code that will be associated with this account
|
||||||
*/
|
*/
|
||||||
public void saveCode(byte[] addr, byte[] code);
|
public void saveCode(byte[] addr, byte[] code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the code associated with an account
|
* Retrieve the code associated with an account
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @return code in byte-array format
|
* @return code in byte-array format
|
||||||
*/
|
*/
|
||||||
public byte[] getCode(byte[] addr);
|
public byte[] getCode(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put a value in storage of an account at a given key
|
* Put a value in storage of an account at a given key
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @param key of the data to store
|
* @param key of the data to store
|
||||||
* @param value is the data to store
|
* @param value is the data to store
|
||||||
|
@ -102,34 +102,34 @@ public interface Repository {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve storage value from an account for a given key
|
* Retrieve storage value from an account for a given key
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @param key associated with this value
|
* @param key associated with this value
|
||||||
* @return data in the form of a <code>DataWord</code>
|
* @return data in the form of a <code>DataWord</code>
|
||||||
*/
|
*/
|
||||||
public DataWord getStorageValue(byte[] addr, DataWord key);
|
public DataWord getStorageValue(byte[] addr, DataWord key);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve balance of an account
|
* Retrieve balance of an account
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @return balance of the account as a <code>BigInteger</code> value
|
* @return balance of the account as a <code>BigInteger</code> value
|
||||||
*/
|
*/
|
||||||
public BigInteger getBalance(byte[] addr);
|
public BigInteger getBalance(byte[] addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add value to the balance of an account
|
* Add value to the balance of an account
|
||||||
*
|
*
|
||||||
* @param addr of the account
|
* @param addr of the account
|
||||||
* @param value to be added
|
* @param value to be added
|
||||||
* @return new balance of the account
|
* @return new balance of the account
|
||||||
*/
|
*/
|
||||||
public BigInteger addBalance(byte[] addr, BigInteger value);
|
public BigInteger addBalance(byte[] addr, BigInteger value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an iterator over the accounts in this database in proper sequence
|
* Returns an iterator over the accounts in this database in proper sequence
|
||||||
*
|
*
|
||||||
* @return an iterator over the accounts in this database in proper sequence
|
* @return an iterator over the accounts in this database in proper sequence
|
||||||
*/
|
*/
|
||||||
public DBIterator getAccountsIterator();
|
public DBIterator getAccountsIterator();
|
||||||
|
@ -137,12 +137,12 @@ public interface Repository {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump the full state of the current repository into a file with JSON format
|
* Dump the full state of the current repository into a file with JSON format
|
||||||
* It contains all the contracts/account, their attributes and
|
* It contains all the contracts/account, their attributes and
|
||||||
*
|
*
|
||||||
* @param block of the current state
|
* @param block of the current state
|
||||||
* @param gasUsed the amount of gas used in the block until that point
|
* @param gasUsed the amount of gas used in the block until that point
|
||||||
* @param txNumber is the number of the transaction for which the dump has to be made
|
* @param txNumber is the number of the transaction for which the dump has to be made
|
||||||
* @param txHash is the hash of the given transaction.
|
* @param txHash is the hash of the given transaction.
|
||||||
* If null, the block state post coinbase reward is dumped.
|
* If null, the block state post coinbase reward is dumped.
|
||||||
*/
|
*/
|
||||||
public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash);
|
public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash);
|
||||||
|
@ -158,13 +158,13 @@ public interface Repository {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
public void commit();
|
public void commit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo all the changes made so far
|
* Undo all the changes made so far
|
||||||
* to a snapshot of the repository
|
* to a snapshot of the repository
|
||||||
*/
|
*/
|
||||||
public void rollback();
|
public void rollback();
|
||||||
|
@ -175,13 +175,13 @@ public interface Repository {
|
||||||
* @param root - new root
|
* @param root - new root
|
||||||
*/
|
*/
|
||||||
public void syncToRoot(byte[] root);
|
public void syncToRoot(byte[] root);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if the current repository has an open connection to the database
|
* Check to see if the current repository has an open connection to the database
|
||||||
* @return <tt>true</tt> if connection to database is open
|
* @return <tt>true</tt> if connection to database is open
|
||||||
*/
|
*/
|
||||||
public boolean isClosed();
|
public boolean isClosed();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the database
|
* Close the database
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,9 +11,9 @@ import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extended {@link com.fasterxml.jackson.databind.ObjectMapper ObjectMapper} class to
|
* An extended {@link com.fasterxml.jackson.databind.ObjectMapper ObjectMapper} class to
|
||||||
* customize ethereum state dumps.
|
* customize ethereum state dumps.
|
||||||
*
|
*
|
||||||
* @author Alon Muroch
|
* @author Alon Muroch
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -21,7 +21,7 @@ public class EtherObjectMapper extends ObjectMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String writeValueAsString(Object value)
|
public String writeValueAsString(Object value)
|
||||||
throws JsonProcessingException {
|
throws JsonProcessingException {
|
||||||
// alas, we have to pull the recycler directly here...
|
// alas, we have to pull the recycler directly here...
|
||||||
SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
|
SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
|
||||||
try {
|
try {
|
||||||
|
@ -29,7 +29,7 @@ public class EtherObjectMapper extends ObjectMapper {
|
||||||
// set ethereum custom pretty printer
|
// set ethereum custom pretty printer
|
||||||
EtherPrettyPrinter pp = new EtherPrettyPrinter();
|
EtherPrettyPrinter pp = new EtherPrettyPrinter();
|
||||||
ge.setPrettyPrinter(pp);
|
ge.setPrettyPrinter(pp);
|
||||||
|
|
||||||
_configAndWriteValue(ge, value);
|
_configAndWriteValue(ge, value);
|
||||||
} catch (JsonProcessingException e) { // to support [JACKSON-758]
|
} catch (JsonProcessingException e) { // to support [JACKSON-758]
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -38,11 +38,11 @@ public class EtherObjectMapper extends ObjectMapper {
|
||||||
}
|
}
|
||||||
return sw.getAndClear();
|
return sw.getAndClear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extended {@link com.fasterxml.jackson.core.util.DefaultPrettyPrinter} class to customize
|
* An extended {@link com.fasterxml.jackson.core.util.DefaultPrettyPrinter} class to customize
|
||||||
* an ethereum {@link com.fasterxml.jackson.core.PrettyPrinter Pretty Printer} Generator
|
* an ethereum {@link com.fasterxml.jackson.core.PrettyPrinter Pretty Printer} Generator
|
||||||
*
|
*
|
||||||
* @author Alon Muroch
|
* @author Alon Muroch
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -51,7 +51,7 @@ public class EtherObjectMapper extends ObjectMapper {
|
||||||
public EtherPrettyPrinter() {
|
public EtherPrettyPrinter() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeObjectFieldValueSeparator(JsonGenerator jg)
|
public void writeObjectFieldValueSeparator(JsonGenerator jg)
|
||||||
throws IOException, JsonGenerationException {
|
throws IOException, JsonGenerationException {
|
||||||
|
|
|
@ -16,26 +16,26 @@ import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON Helper class to format data into ObjectNodes
|
* JSON Helper class to format data into ObjectNodes
|
||||||
* to match PyEthereum blockstate output
|
* to match PyEthereum blockstate output
|
||||||
*
|
*
|
||||||
* Dump format:
|
* Dump format:
|
||||||
* {
|
* {
|
||||||
* "address":
|
* "address":
|
||||||
* {
|
* {
|
||||||
* "nonce": "n1",
|
* "nonce": "n1",
|
||||||
* "balance": "b1",
|
* "balance": "b1",
|
||||||
* "stateRoot": "s1",
|
* "stateRoot": "s1",
|
||||||
* "codeHash": "c1",
|
* "codeHash": "c1",
|
||||||
* "code": "c2",
|
* "code": "c2",
|
||||||
* "storage":
|
* "storage":
|
||||||
* {
|
* {
|
||||||
* "key1": "value1",
|
* "key1": "value1",
|
||||||
* "key2": "value2"
|
* "key2": "value2"
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* www.ethereumJ.com
|
* www.ethereumJ.com
|
||||||
*
|
*
|
||||||
* @author: Roman Mandeleil
|
* @author: Roman Mandeleil
|
||||||
|
@ -43,14 +43,14 @@ import java.util.*;
|
||||||
*/
|
*/
|
||||||
public class JSONHelper {
|
public class JSONHelper {
|
||||||
|
|
||||||
public static void dumpState(ObjectNode statesNode, String address, AccountState state, ContractDetails details) {
|
public static void dumpState(ObjectNode statesNode, String address, AccountState state, ContractDetails details) {
|
||||||
|
|
||||||
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
|
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
|
||||||
Collections.sort((List<DataWord>) storageKeys);
|
Collections.sort((List<DataWord>) storageKeys);
|
||||||
|
|
||||||
ObjectNode account = statesNode.objectNode();
|
ObjectNode account = statesNode.objectNode();
|
||||||
ObjectNode storage = statesNode.objectNode();
|
ObjectNode storage = statesNode.objectNode();
|
||||||
|
|
||||||
for (DataWord key : storageKeys) {
|
for (DataWord key : storageKeys) {
|
||||||
storage.put("0x" + Hex.toHexString(key.getData()),
|
storage.put("0x" + Hex.toHexString(key.getData()),
|
||||||
"0x" + Hex.toHexString(details.getStorage().get(key).getNoLeadZeroesData()));
|
"0x" + Hex.toHexString(details.getStorage().get(key).getNoLeadZeroesData()));
|
||||||
|
@ -61,14 +61,14 @@ public class JSONHelper {
|
||||||
account.put("nonce", state.getNonce() == null ? "0" : state.getNonce().toString());
|
account.put("nonce", state.getNonce() == null ? "0" : state.getNonce().toString());
|
||||||
account.put("storage", storage);
|
account.put("storage", storage);
|
||||||
account.put("storage_root", state.getStateRoot() == null ? "" : Hex.toHexString(state.getStateRoot()));
|
account.put("storage_root", state.getStateRoot() == null ? "" : Hex.toHexString(state.getStateRoot()));
|
||||||
|
|
||||||
statesNode.put(address, account);
|
statesNode.put(address, account);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void dumpBlock(ObjectNode blockNode, Block block,
|
public static void dumpBlock(ObjectNode blockNode, Block block,
|
||||||
long gasUsed, byte[] state, List<ByteArrayWrapper> keys,
|
long gasUsed, byte[] state, List<ByteArrayWrapper> keys,
|
||||||
Repository repository) {
|
Repository repository) {
|
||||||
|
|
||||||
blockNode.put("coinbase", Hex.toHexString(block.getCoinbase()));
|
blockNode.put("coinbase", Hex.toHexString(block.getCoinbase()));
|
||||||
blockNode.put("difficulty", new BigInteger(1, block.calcDifficulty()).toString());
|
blockNode.put("difficulty", new BigInteger(1, block.calcDifficulty()).toString());
|
||||||
blockNode.put("extra_data", "0x");
|
blockNode.put("extra_data", "0x");
|
||||||
|
@ -76,25 +76,25 @@ public class JSONHelper {
|
||||||
blockNode.put("nonce", "0x" + Hex.toHexString(block.getNonce()));
|
blockNode.put("nonce", "0x" + Hex.toHexString(block.getNonce()));
|
||||||
blockNode.put("number", String.valueOf(block.getNumber()));
|
blockNode.put("number", String.valueOf(block.getNumber()));
|
||||||
blockNode.put("prevhash", "0x" + Hex.toHexString(block.getParentHash()));
|
blockNode.put("prevhash", "0x" + Hex.toHexString(block.getParentHash()));
|
||||||
|
|
||||||
ObjectNode statesNode = blockNode.objectNode();
|
ObjectNode statesNode = blockNode.objectNode();
|
||||||
for (ByteArrayWrapper key : keys) {
|
for (ByteArrayWrapper key : keys) {
|
||||||
byte[] keyBytes = key.getData();
|
byte[] keyBytes = key.getData();
|
||||||
AccountState accountState = repository.getAccountState(keyBytes);
|
AccountState accountState = repository.getAccountState(keyBytes);
|
||||||
ContractDetails details = repository.getContractDetails(keyBytes);
|
ContractDetails details = repository.getContractDetails(keyBytes);
|
||||||
JSONHelper.dumpState(statesNode, Hex.toHexString(keyBytes), accountState, details);
|
JSONHelper.dumpState(statesNode, Hex.toHexString(keyBytes), accountState, details);
|
||||||
}
|
}
|
||||||
blockNode.put("state", statesNode);
|
blockNode.put("state", statesNode);
|
||||||
|
|
||||||
blockNode.put("state_root", Hex.toHexString(state));
|
blockNode.put("state_root", Hex.toHexString(state));
|
||||||
blockNode.put("timestamp", String.valueOf(block.getTimestamp()));
|
blockNode.put("timestamp", String.valueOf(block.getTimestamp()));
|
||||||
|
|
||||||
ArrayNode transactionsNode = blockNode.arrayNode();
|
ArrayNode transactionsNode = blockNode.arrayNode();
|
||||||
blockNode.put("transactions", transactionsNode);
|
blockNode.put("transactions", transactionsNode);
|
||||||
|
|
||||||
blockNode.put("tx_list_root", ByteUtil.toHexString(block.getTxTrieRoot()));
|
blockNode.put("tx_list_root", ByteUtil.toHexString(block.getTxTrieRoot()));
|
||||||
blockNode.put("uncles_hash", "0x" + Hex.toHexString(block.getUnclesHash()));
|
blockNode.put("uncles_hash", "0x" + Hex.toHexString(block.getUnclesHash()));
|
||||||
|
|
||||||
// JSONHelper.dumpTransactions(blockNode,
|
// JSONHelper.dumpTransactions(blockNode,
|
||||||
// stateRoot, codeHash, code, storage);
|
// stateRoot, codeHash, code, storage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class JSONReader {
|
||||||
json = getFromUrl("https://raw.githubusercontent.com/ethereum/tests/develop/" + filename);
|
json = getFromUrl("https://raw.githubusercontent.com/ethereum/tests/develop/" + filename);
|
||||||
return json == "" ? json = getFromLocal(filename) : json;
|
return json == "" ? json = getFromLocal(filename) : json;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getFromLocal(String filename) {
|
public static String getFromLocal(String filename) {
|
||||||
System.out.println("Loading local file: " + filename);
|
System.out.println("Loading local file: " + filename);
|
||||||
try {
|
try {
|
||||||
|
@ -36,7 +36,7 @@ public class JSONReader {
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getFromUrl(String urlToRead) {
|
public static String getFromUrl(String urlToRead) {
|
||||||
URL url;
|
URL url;
|
||||||
HttpURLConnection conn;
|
HttpURLConnection conn;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
public class Logs {
|
public class Logs {
|
||||||
List<LogInfo> logs = new ArrayList<>();
|
List<LogInfo> logs = new ArrayList<>();
|
||||||
|
|
||||||
public Logs(JSONArray jLogs) {
|
public Logs(JSONArray jLogs) {
|
||||||
|
|
||||||
for (int i = 0; i < jLogs.size(); ++i){
|
for (int i = 0; i < jLogs.size(); ++i){
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class TestCase {
|
||||||
|
|
||||||
// "env": { ... },
|
// "env": { ... },
|
||||||
private Env env;
|
private Env env;
|
||||||
|
|
||||||
//
|
//
|
||||||
private Logs logs;
|
private Logs logs;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class TestCase {
|
||||||
JSONArray callCreates = new JSONArray();
|
JSONArray callCreates = new JSONArray();
|
||||||
if(testCaseJSONObj.containsKey("callcreates"))
|
if(testCaseJSONObj.containsKey("callcreates"))
|
||||||
callCreates = (JSONArray)testCaseJSONObj.get("callcreates");
|
callCreates = (JSONArray)testCaseJSONObj.get("callcreates");
|
||||||
|
|
||||||
JSONArray logsJSON = new JSONArray();
|
JSONArray logsJSON = new JSONArray();
|
||||||
if(testCaseJSONObj.containsKey("logs"))
|
if(testCaseJSONObj.containsKey("logs"))
|
||||||
logsJSON = (JSONArray)testCaseJSONObj.get("logs");
|
logsJSON = (JSONArray)testCaseJSONObj.get("logs");
|
||||||
|
@ -74,7 +74,7 @@ public class TestCase {
|
||||||
if(testCaseJSONObj.containsKey("gas"))
|
if(testCaseJSONObj.containsKey("gas"))
|
||||||
gasString = testCaseJSONObj.get("gas").toString();
|
gasString = testCaseJSONObj.get("gas").toString();
|
||||||
this.gas = ByteUtil.bigIntegerToBytes(new BigInteger(gasString));
|
this.gas = ByteUtil.bigIntegerToBytes(new BigInteger(gasString));
|
||||||
|
|
||||||
String outString = null;
|
String outString = null;
|
||||||
if(testCaseJSONObj.containsKey("out"))
|
if(testCaseJSONObj.containsKey("out"))
|
||||||
outString = testCaseJSONObj.get("out").toString();
|
outString = testCaseJSONObj.get("out").toString();
|
||||||
|
@ -122,7 +122,7 @@ public class TestCase {
|
||||||
public Exec getExec() {
|
public Exec getExec() {
|
||||||
return exec;
|
return exec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Logs getLogs() {
|
public Logs getLogs() {
|
||||||
return logs;
|
return logs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class TestRunner {
|
||||||
private ProgramTrace trace = null;
|
private ProgramTrace trace = null;
|
||||||
|
|
||||||
public List<String> runTestSuite(TestSuite testSuite) {
|
public List<String> runTestSuite(TestSuite testSuite) {
|
||||||
|
|
||||||
Iterator<TestCase> testIterator = testSuite.iterator();
|
Iterator<TestCase> testIterator = testSuite.iterator();
|
||||||
List<String> resultCollector = new ArrayList<>();
|
List<String> resultCollector = new ArrayList<>();
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ import static org.ethereum.config.SystemProperties.CONFIG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WorldManager is a singleton containing references to different parts of the system.
|
* WorldManager is a singleton containing references to different parts of the system.
|
||||||
*
|
*
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 01/06/2014 10:44
|
* Created on: 01/06/2014 10:44
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@ -57,12 +57,12 @@ public class WorldManager {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AdminInfo adminInfo;
|
private AdminInfo adminInfo;
|
||||||
|
|
||||||
private final Set<Transaction> pendingTransactions = Collections.synchronizedSet(new HashSet<Transaction>());
|
private final Set<Transaction> pendingTransactions = Collections.synchronizedSet(new HashSet<Transaction>());
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EthereumListener listener;
|
private EthereumListener listener;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
byte[] cowAddr = HashUtil.sha3("cow".getBytes());
|
byte[] cowAddr = HashUtil.sha3("cow".getBytes());
|
||||||
|
@ -72,7 +72,7 @@ public class WorldManager {
|
||||||
byte[] cbAddr = HashUtil.sha3(secret.getBytes());
|
byte[] cbAddr = HashUtil.sha3(secret.getBytes());
|
||||||
wallet.importKey(cbAddr);
|
wallet.importKey(cbAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(EthereumListener listener) {
|
public void addListener(EthereumListener listener) {
|
||||||
logger.info("Ethereum listener added");
|
logger.info("Ethereum listener added");
|
||||||
((EthereumListenerWrapper)this.listener).addListener(listener);
|
((EthereumListenerWrapper)this.listener).addListener(listener);
|
||||||
|
@ -115,7 +115,7 @@ public class WorldManager {
|
||||||
public EthereumListener getListener() {
|
public EthereumListener getListener() {
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWallet(Wallet wallet) {
|
public void setWallet(Wallet wallet) {
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
}
|
}
|
||||||
|
@ -123,11 +123,11 @@ public class WorldManager {
|
||||||
public Repository getRepository() {
|
public Repository getRepository() {
|
||||||
return repository;
|
return repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Blockchain getBlockchain() {
|
public Blockchain getBlockchain() {
|
||||||
return blockchain;
|
return blockchain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Wallet getWallet() {
|
public Wallet getWallet() {
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,18 +14,18 @@ import java.math.BigInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Miner performs the proof-of-work needed for a valid block
|
* The Miner performs the proof-of-work needed for a valid block
|
||||||
*
|
*
|
||||||
* The mining proof-of-work (PoW) exists as a cryptographically secure nonce
|
* The mining proof-of-work (PoW) exists as a cryptographically secure nonce
|
||||||
* that proves beyond reasonable doubt that a particular amount of computation
|
* that proves beyond reasonable doubt that a particular amount of computation
|
||||||
* has been expended in the determination of some token value n.
|
* has been expended in the determination of some token value n.
|
||||||
* It is utilised to enforce the blockchain security by giving meaning
|
* It is utilised to enforce the blockchain security by giving meaning
|
||||||
* and credence to the notion of difficulty (and, by extension, total difficulty).
|
* and credence to the notion of difficulty (and, by extension, total difficulty).
|
||||||
*
|
*
|
||||||
* However, since mining new blocks comes with an attached reward,
|
* However, since mining new blocks comes with an attached reward,
|
||||||
* the proof-of-work not only functions as a method of securing confidence
|
* the proof-of-work not only functions as a method of securing confidence
|
||||||
* that the blockchain will remain canonical into the future, but also as
|
* that the blockchain will remain canonical into the future, but also as
|
||||||
* a wealth distribution mechanism.
|
* a wealth distribution mechanism.
|
||||||
*
|
*
|
||||||
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (chapter 11.5 Mining Proof-of-Work)
|
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (chapter 11.5 Mining Proof-of-Work)
|
||||||
*/
|
*/
|
||||||
public class Miner {
|
public class Miner {
|
||||||
|
@ -36,13 +36,13 @@ public class Miner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a nonce to given block which complies with the given difficulty
|
* Adds a nonce to given block which complies with the given difficulty
|
||||||
*
|
*
|
||||||
* For the PoC series, we use a simplified proof-of-work.
|
* For the PoC series, we use a simplified proof-of-work.
|
||||||
* This is not ASIC resistant and is meant merely as a placeholder.
|
* This is not ASIC resistant and is meant merely as a placeholder.
|
||||||
* It utilizes the bare SHA3 hash function to secure the block chain by requiring
|
* It utilizes the bare SHA3 hash function to secure the block chain by requiring
|
||||||
* the SHA3 hash of the concatenation of the nonce and the header’s SHA3 hash to be
|
* the SHA3 hash of the concatenation of the nonce and the header’s SHA3 hash to be
|
||||||
* sufficiently low. It is formally defined as PoW:
|
* sufficiently low. It is formally defined as PoW:
|
||||||
*
|
*
|
||||||
* PoW(H, n) ≡ BE(SHA3(SHA3(RLP(H!n)) ◦ n))
|
* PoW(H, n) ≡ BE(SHA3(SHA3(RLP(H!n)) ◦ n))
|
||||||
*
|
*
|
||||||
* where:
|
* where:
|
||||||
|
@ -54,7 +54,7 @@ public class Miner {
|
||||||
* o is the series concatenation operator;
|
* o is the series concatenation operator;
|
||||||
* BE(X) evaluates to the value equal to X when interpreted as a
|
* BE(X) evaluates to the value equal to X when interpreted as a
|
||||||
* big-endian-encoded integer.
|
* big-endian-encoded integer.
|
||||||
*
|
*
|
||||||
* @param newBlock without a valid nonce
|
* @param newBlock without a valid nonce
|
||||||
* @param difficulty - the mining difficulty
|
* @param difficulty - the mining difficulty
|
||||||
* @return true if valid nonce has been added to the block
|
* @return true if valid nonce has been added to the block
|
||||||
|
@ -76,7 +76,7 @@ public class Miner {
|
||||||
|
|
||||||
byte[] testNonce = new byte[32];
|
byte[] testNonce = new byte[32];
|
||||||
byte[] concat;
|
byte[] concat;
|
||||||
|
|
||||||
while(ByteUtil.increment(testNonce) && !stop) {
|
while(ByteUtil.increment(testNonce) && !stop) {
|
||||||
|
|
||||||
if (testNonce[31] == 0 && testNonce[30] == 0){
|
if (testNonce[31] == 0 && testNonce[30] == 0){
|
||||||
|
|
|
@ -18,8 +18,8 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
* The processing queue for blocks to be validated and added to the blockchain.
|
* The processing queue for blocks to be validated and added to the blockchain.
|
||||||
* This class also maintains the list of hashes from the peer with the heaviest sub-tree.
|
* This class also maintains the list of hashes from the peer with the heaviest sub-tree.
|
||||||
* Based on these hashes, blocks are added to the queue.
|
* Based on these hashes, blocks are added to the queue.
|
||||||
*
|
*
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 27/07/2014 11:28
|
* Created on: 27/07/2014 11:28
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@ -27,13 +27,13 @@ public class BlockQueue {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("blockqueue");
|
private static final Logger logger = LoggerFactory.getLogger("blockqueue");
|
||||||
|
|
||||||
/** The list of hashes of the heaviest chain on the network,
|
/** The list of hashes of the heaviest chain on the network,
|
||||||
* for which this client doesn't have the blocks yet */
|
* for which this client doesn't have the blocks yet */
|
||||||
private Deque<byte[]> blockHashQueue = new ArrayDeque<>();
|
private Deque<byte[]> blockHashQueue = new ArrayDeque<>();
|
||||||
|
|
||||||
/** Queue with blocks to be validated and added to the blockchain */
|
/** Queue with blocks to be validated and added to the blockchain */
|
||||||
private Queue<Block> blockReceivedQueue = new ConcurrentLinkedQueue<>();
|
private Queue<Block> blockReceivedQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
/** Highest known total difficulty, representing the heaviest chain on the network */
|
/** Highest known total difficulty, representing the heaviest chain on the network */
|
||||||
private BigInteger highestTotalDifficulty;
|
private BigInteger highestTotalDifficulty;
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ public class BlockQueue {
|
||||||
private void nudgeQueue() {
|
private void nudgeQueue() {
|
||||||
if (blockReceivedQueue.isEmpty())
|
if (blockReceivedQueue.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
logger.info("BlockQueue size: {}", blockReceivedQueue.size());
|
logger.info("BlockQueue size: {}", blockReceivedQueue.size());
|
||||||
while(!blockReceivedQueue.isEmpty()){
|
while(!blockReceivedQueue.isEmpty()){
|
||||||
Block block = blockReceivedQueue.poll();
|
Block block = blockReceivedQueue.poll();
|
||||||
|
@ -74,10 +74,10 @@ public class BlockQueue {
|
||||||
* Add a list of blocks to the processing queue.
|
* Add a list of blocks to the processing queue.
|
||||||
* The list is validated by making sure the first block in the received list of blocks
|
* The list is validated by making sure the first block in the received list of blocks
|
||||||
* is the next expected block number of the queue.
|
* is the next expected block number of the queue.
|
||||||
*
|
*
|
||||||
* The queue is configured to contain a maximum number of blocks to avoid memory issues
|
* The queue is configured to contain a maximum number of blocks to avoid memory issues
|
||||||
* If the list exceeds that, the rest of the received blocks in the list are discarded.
|
* If the list exceeds that, the rest of the received blocks in the list are discarded.
|
||||||
*
|
*
|
||||||
* @param blockList - the blocks received from a peer to be added to the queue
|
* @param blockList - the blocks received from a peer to be added to the queue
|
||||||
*/
|
*/
|
||||||
public void addBlocks(List<Block> blockList) {
|
public void addBlocks(List<Block> blockList) {
|
||||||
|
@ -104,13 +104,13 @@ public class BlockQueue {
|
||||||
blockReceivedQueue.size(),
|
blockReceivedQueue.size(),
|
||||||
lastBlock.getNumber());
|
lastBlock.getNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the last block in the queue. If the queue is empty,
|
* Returns the last block in the queue. If the queue is empty,
|
||||||
* this will return the last block added to the blockchain.
|
* this will return the last block added to the blockchain.
|
||||||
*
|
*
|
||||||
* @return The last known block this client on the network
|
* @return The last known block this client on the network
|
||||||
* and will never return <code>null</code> as there is
|
* and will never return <code>null</code> as there is
|
||||||
* always the Genesis block at the start of the chain.
|
* always the Genesis block at the start of the chain.
|
||||||
*/
|
*/
|
||||||
public Block getLastBlock() {
|
public Block getLastBlock() {
|
||||||
|
@ -122,7 +122,7 @@ public class BlockQueue {
|
||||||
/**
|
/**
|
||||||
* Reset the queue of hashes of blocks to be retrieved
|
* Reset the queue of hashes of blocks to be retrieved
|
||||||
* and add the best hash to the top of the queue
|
* and add the best hash to the top of the queue
|
||||||
*
|
*
|
||||||
* @param hash - the best hash
|
* @param hash - the best hash
|
||||||
*/
|
*/
|
||||||
public void setBestHash(byte[] hash) {
|
public void setBestHash(byte[] hash) {
|
||||||
|
@ -131,9 +131,9 @@ public class BlockQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the last added hash to the queue representing
|
* Returns the last added hash to the queue representing
|
||||||
* the latest known block on the network
|
* the latest known block on the network
|
||||||
*
|
*
|
||||||
* @return The best hash on the network known to the client
|
* @return The best hash on the network known to the client
|
||||||
*/
|
*/
|
||||||
public byte[] getBestHash() {
|
public byte[] getBestHash() {
|
||||||
|
@ -158,10 +158,10 @@ public class BlockQueue {
|
||||||
public void addNewBlockHash(byte[] hash){
|
public void addNewBlockHash(byte[] hash){
|
||||||
blockHashQueue.addFirst(hash);
|
blockHashQueue.addFirst(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of hashes from blocks that still need to be downloaded.
|
* Return a list of hashes from blocks that still need to be downloaded.
|
||||||
*
|
*
|
||||||
* @return A list of hashes for which blocks need to be retrieved.
|
* @return A list of hashes for which blocks need to be retrieved.
|
||||||
*/
|
*/
|
||||||
public List<byte[]> getHashes() {
|
public List<byte[]> getHashes() {
|
||||||
|
@ -195,7 +195,7 @@ public class BlockQueue {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getHighestTotalDifficulty() {
|
public BigInteger getHighestTotalDifficulty() {
|
||||||
return highestTotalDifficulty;
|
return highestTotalDifficulty;
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ public class BlockQueue {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current number of blocks in the queue
|
* Returns the current number of blocks in the queue
|
||||||
*
|
*
|
||||||
* @return the current number of blocks in the queue
|
* @return the current number of blocks in the queue
|
||||||
*/
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
|
@ -223,7 +223,7 @@ public class BlockQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel and purge the timer-thread that
|
* Cancel and purge the timer-thread that
|
||||||
* processes the blocks in the queue
|
* processes the blocks in the queue
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import org.ethereum.net.message.Message;
|
||||||
* been offered This class also contains the last time a message was offered and
|
* been offered This class also contains the last time a message was offered and
|
||||||
* is updated when an answer has been received to it can be removed from the
|
* is updated when an answer has been received to it can be removed from the
|
||||||
* queue.
|
* queue.
|
||||||
*
|
*
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
*/
|
*/
|
||||||
public class MessageRoundtrip {
|
public class MessageRoundtrip {
|
||||||
|
|
|
@ -8,7 +8,7 @@ public class Capability implements Comparable<Capability> {
|
||||||
public final static String P2P = "p2p";
|
public final static String P2P = "p2p";
|
||||||
public final static String ETH = "eth";
|
public final static String ETH = "eth";
|
||||||
public final static String SHH = "shh";
|
public final static String SHH = "shh";
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private byte version;
|
private byte version;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ public class Capability implements Comparable<Capability> {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public class Capability implements Comparable<Capability> {
|
||||||
public byte getVersion() {
|
public byte getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj) return true;
|
if (this == obj) return true;
|
||||||
|
@ -41,7 +41,7 @@ public class Capability implements Comparable<Capability> {
|
||||||
public int compareTo(Capability o) {
|
public int compareTo(Capability o) {
|
||||||
return this.name.compareTo(o.name);
|
return this.name.compareTo(o.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name + ":" + version;
|
return name + ":" + version;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,12 @@ public class PeerClient {
|
||||||
Bootstrap b = new Bootstrap();
|
Bootstrap b = new Bootstrap();
|
||||||
b.group(workerGroup);
|
b.group(workerGroup);
|
||||||
b.channel(NioSocketChannel.class);
|
b.channel(NioSocketChannel.class);
|
||||||
|
|
||||||
b.option(ChannelOption.SO_KEEPALIVE, true);
|
b.option(ChannelOption.SO_KEEPALIVE, true);
|
||||||
b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT);
|
b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT);
|
||||||
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONFIG.peerConnectionTimeout());
|
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONFIG.peerConnectionTimeout());
|
||||||
b.remoteAddress(host, port);
|
b.remoteAddress(host, port);
|
||||||
|
|
||||||
b.handler(ethereumChannelInitializer);
|
b.handler(ethereumChannelInitializer);
|
||||||
|
|
||||||
// Start the client.
|
// Start the client.
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class BlockHashesMessage extends EthMessage {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (!parsed) parse();
|
if (!parsed) parse();
|
||||||
|
|
||||||
StringBuffer sb = Utils.getHashlistShort(this.blockHashes);
|
StringBuffer sb = Utils.getHashlistShort(this.blockHashes);
|
||||||
return "[" + this.getCommand().name() + sb.toString() + "] (" + this.blockHashes.size() + ")";
|
return "[" + this.getCommand().name() + sb.toString() + "] (" + this.blockHashes.size() + ")";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.ethereum.net.eth.EthMessageCodes;
|
||||||
import org.ethereum.net.message.Message;
|
import org.ethereum.net.message.Message;
|
||||||
|
|
||||||
public abstract class EthMessage extends Message {
|
public abstract class EthMessage extends Message {
|
||||||
|
|
||||||
public EthMessage() {}
|
public EthMessage() {}
|
||||||
|
|
||||||
public EthMessage(byte[] encoded) {
|
public EthMessage(byte[] encoded) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.util.Map;
|
||||||
* A list of commands for the Ethereum network protocol.
|
* A list of commands for the Ethereum network protocol.
|
||||||
* <br>
|
* <br>
|
||||||
* The codes for these commands are the first byte in every packet.
|
* The codes for these commands are the first byte in every packet.
|
||||||
*
|
*
|
||||||
* @see <a href="https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol">
|
* @see <a href="https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol">
|
||||||
* https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol</a>
|
* https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol</a>
|
||||||
*/
|
*/
|
||||||
|
@ -56,8 +56,8 @@ public enum EthMessageCodes {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [+0x07 [blockHeader, transactionList, uncleList], totalDifficulty] <br>
|
* [+0x07 [blockHeader, transactionList, uncleList], totalDifficulty] <br>
|
||||||
* Specify a single block that the peer should know about. The composite item
|
* Specify a single block that the peer should know about. The composite item
|
||||||
* in the list (following the message ID) is a block in the format described
|
* in the list (following the message ID) is a block in the format described
|
||||||
* in the main Ethereum specification. */
|
* in the main Ethereum specification. */
|
||||||
NEW_BLOCK(0x07),
|
NEW_BLOCK(0x07),
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,15 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum GetBlockHashes message on the network
|
* Wrapper around an Ethereum GetBlockHashes message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.eth.EthMessageCodes#GET_BLOCK_HASHES
|
* @see org.ethereum.net.eth.EthMessageCodes#GET_BLOCK_HASHES
|
||||||
*/
|
*/
|
||||||
public class GetBlockHashesMessage extends EthMessage {
|
public class GetBlockHashesMessage extends EthMessage {
|
||||||
|
|
||||||
/** The newest block hash from which to start sending older hashes */
|
/** The newest block hash from which to start sending older hashes */
|
||||||
private byte[] bestHash;
|
private byte[] bestHash;
|
||||||
|
|
||||||
/** The maximum number of blocks to return.
|
/** The maximum number of blocks to return.
|
||||||
* <b>Note:</b> the peer could return fewer. */
|
* <b>Note:</b> the peer could return fewer. */
|
||||||
private int maxBlocks;
|
private int maxBlocks;
|
||||||
|
|
||||||
|
@ -80,8 +80,8 @@ public class GetBlockHashesMessage extends EthMessage {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (!parsed) parse();
|
if (!parsed) parse();
|
||||||
return "[" + this.getCommand().name() +
|
return "[" + this.getCommand().name() +
|
||||||
" bestHash=" + Hex.toHexString(bestHash) +
|
" bestHash=" + Hex.toHexString(bestHash) +
|
||||||
" maxBlocks=" + maxBlocks + "]";
|
" maxBlocks=" + maxBlocks + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ import static org.ethereum.net.eth.EthMessageCodes.GET_BLOCKS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum GetBlocks message on the network
|
* Wrapper around an Ethereum GetBlocks message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.eth.EthMessageCodes#GET_BLOCKS
|
* @see org.ethereum.net.eth.EthMessageCodes#GET_BLOCKS
|
||||||
*/
|
*/
|
||||||
public class GetBlocksMessage extends EthMessage {
|
public class GetBlocksMessage extends EthMessage {
|
||||||
|
|
|
@ -6,12 +6,12 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum GetTransactions message on the network
|
* Wrapper around an Ethereum GetTransactions message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.eth.EthMessageCodes#GET_TRANSACTIONS
|
* @see org.ethereum.net.eth.EthMessageCodes#GET_TRANSACTIONS
|
||||||
*/
|
*/
|
||||||
public class GetTransactionsMessage extends EthMessage {
|
public class GetTransactionsMessage extends EthMessage {
|
||||||
|
|
||||||
/** GetTransactions message is always a the same single command payload */
|
/** GetTransactions message is always a the same single command payload */
|
||||||
private final static byte[] FIXED_PAYLOAD = Hex.decode("C116");
|
private final static byte[] FIXED_PAYLOAD = Hex.decode("C116");
|
||||||
|
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum Blocks message on the network
|
* Wrapper around an Ethereum Blocks message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.eth.EthMessageCodes#NEW_BLOCK
|
* @see org.ethereum.net.eth.EthMessageCodes#NEW_BLOCK
|
||||||
*/
|
*/
|
||||||
public class NewBlockMessage extends EthMessage {
|
public class NewBlockMessage extends EthMessage {
|
||||||
|
@ -48,7 +48,7 @@ public class NewBlockMessage extends EthMessage {
|
||||||
if (!parsed) parse();
|
if (!parsed) parse();
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getDifficulty(){
|
public byte[] getDifficulty(){
|
||||||
if (!parsed) parse();
|
if (!parsed) parse();
|
||||||
return difficulty;
|
return difficulty;
|
||||||
|
|
|
@ -9,8 +9,8 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
import static org.ethereum.net.eth.EthMessageCodes.STATUS;
|
import static org.ethereum.net.eth.EthMessageCodes.STATUS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum Status message on the network
|
* Wrapper around an Ethereum Status message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.eth.EthMessageCodes#STATUS
|
* @see org.ethereum.net.eth.EthMessageCodes#STATUS
|
||||||
*/
|
*/
|
||||||
public class StatusMessage extends EthMessage {
|
public class StatusMessage extends EthMessage {
|
||||||
|
|
|
@ -13,12 +13,12 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum Transactions message on the network
|
* Wrapper around an Ethereum Transactions message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.eth.EthMessageCodes#TRANSACTIONS
|
* @see org.ethereum.net.eth.EthMessageCodes#TRANSACTIONS
|
||||||
*/
|
*/
|
||||||
public class TransactionsMessage extends EthMessage {
|
public class TransactionsMessage extends EthMessage {
|
||||||
|
|
||||||
private Set<Transaction> transactions;
|
private Set<Transaction> transactions;
|
||||||
|
|
||||||
public TransactionsMessage(byte[] encoded) {
|
public TransactionsMessage(byte[] encoded) {
|
||||||
|
@ -36,7 +36,7 @@ public class TransactionsMessage extends EthMessage {
|
||||||
this.transactions = transactionList;
|
this.transactions = transactionList;
|
||||||
parsed = true;
|
parsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parse() {
|
private void parse() {
|
||||||
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
|
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public class TransactionsMessage extends EthMessage {
|
||||||
}
|
}
|
||||||
parsed = true;
|
parsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encode() {
|
private void encode() {
|
||||||
List<byte[]> encodedElements = new ArrayList<>();
|
List<byte[]> encodedElements = new ArrayList<>();
|
||||||
encodedElements.add(RLP.encodeByte(TRANSACTIONS.asByte()));
|
encodedElements.add(RLP.encodeByte(TRANSACTIONS.asByte()));
|
||||||
|
@ -58,7 +58,7 @@ public class TransactionsMessage extends EthMessage {
|
||||||
.toArray(new byte[encodedElements.size()][]);
|
.toArray(new byte[encodedElements.size()][]);
|
||||||
this.encoded = RLP.encodeList(encodedElementArray);
|
this.encoded = RLP.encodeList(encodedElementArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
if (encoded == null) encode();
|
if (encoded == null) encode();
|
||||||
|
@ -80,7 +80,7 @@ public class TransactionsMessage extends EthMessage {
|
||||||
public Class<?> getAnswerMessage() {
|
public Class<?> getAnswerMessage() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if(!parsed) parse();
|
if(!parsed) parse();
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
|
|
|
@ -2,7 +2,7 @@ package org.ethereum.net.message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract message class for all messages on the Ethereum network
|
* Abstract message class for all messages on the Ethereum network
|
||||||
*
|
*
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 06/04/14 14:58
|
* Created on: 06/04/14 14:58
|
||||||
*/
|
*/
|
||||||
|
@ -18,19 +18,19 @@ public abstract class Message {
|
||||||
this.encoded = encoded;
|
this.encoded = encoded;
|
||||||
parsed = false;
|
parsed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the RLP encoded byte array of this message
|
* Gets the RLP encoded byte array of this message
|
||||||
*
|
*
|
||||||
* @return RLP encoded byte array representation of this message
|
* @return RLP encoded byte array representation of this message
|
||||||
*/
|
*/
|
||||||
public abstract byte[] getEncoded();
|
public abstract byte[] getEncoded();
|
||||||
|
|
||||||
public abstract Class<?> getAnswerMessage();
|
public abstract Class<?> getAnswerMessage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the message in String format
|
* Returns the message in String format
|
||||||
*
|
*
|
||||||
* @return A string with all attributes of the message
|
* @return A string with all attributes of the message
|
||||||
*/
|
*/
|
||||||
public abstract String toString();
|
public abstract String toString();
|
||||||
|
|
|
@ -4,61 +4,61 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reason is an optional integer specifying one
|
* Reason is an optional integer specifying one
|
||||||
* of a number of reasons for disconnect
|
* of a number of reasons for disconnect
|
||||||
*/
|
*/
|
||||||
public enum ReasonCode {
|
public enum ReasonCode {
|
||||||
|
|
||||||
/** [0x00] Disconnect request by other peer */
|
/** [0x00] Disconnect request by other peer */
|
||||||
REQUESTED(0x00),
|
REQUESTED(0x00),
|
||||||
|
|
||||||
/** [0x01] */
|
/** [0x01] */
|
||||||
TCP_ERROR(0x01),
|
TCP_ERROR(0x01),
|
||||||
|
|
||||||
/** [0x02] Packets can not be parsed */
|
/** [0x02] Packets can not be parsed */
|
||||||
BAD_PROTOCOL(0x02),
|
BAD_PROTOCOL(0x02),
|
||||||
|
|
||||||
/** [0x03] This peer is too slow or delivers unreliable data */
|
/** [0x03] This peer is too slow or delivers unreliable data */
|
||||||
USELESS_PEER(0x03),
|
USELESS_PEER(0x03),
|
||||||
|
|
||||||
/** [0x04] Already too many connections with other peers */
|
/** [0x04] Already too many connections with other peers */
|
||||||
TOO_MANY_PEERS(0x04),
|
TOO_MANY_PEERS(0x04),
|
||||||
|
|
||||||
/** [0x05] Already have a running connection with this peer */
|
/** [0x05] Already have a running connection with this peer */
|
||||||
ALREADY_CONNECTED(0x05),
|
ALREADY_CONNECTED(0x05),
|
||||||
|
|
||||||
/** [0x06] Version of the p2p protocol is not the same as ours */
|
/** [0x06] Version of the p2p protocol is not the same as ours */
|
||||||
INCOMPATIBLE_PROTOCOL(0x06),
|
INCOMPATIBLE_PROTOCOL(0x06),
|
||||||
|
|
||||||
/** [0x07] Peer identifies itself with the wrong networkId */
|
/** [0x07] Peer identifies itself with the wrong networkId */
|
||||||
INCOMPATIBLE_NETWORK(0x07),
|
INCOMPATIBLE_NETWORK(0x07),
|
||||||
|
|
||||||
/** [0x08] Peer quit voluntarily */
|
/** [0x08] Peer quit voluntarily */
|
||||||
PEER_QUITING(0x08),
|
PEER_QUITING(0x08),
|
||||||
|
|
||||||
/** [0xFF] Reason not specified */
|
/** [0xFF] Reason not specified */
|
||||||
UNKNOWN(0xFF);
|
UNKNOWN(0xFF);
|
||||||
|
|
||||||
private int reason;
|
private int reason;
|
||||||
|
|
||||||
private static final Map<Integer, ReasonCode> intToTypeMap = new HashMap<>();
|
private static final Map<Integer, ReasonCode> intToTypeMap = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
for (ReasonCode type : ReasonCode.values()) {
|
for (ReasonCode type : ReasonCode.values()) {
|
||||||
intToTypeMap.put(type.reason, type);
|
intToTypeMap.put(type.reason, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReasonCode(int reason) {
|
private ReasonCode(int reason) {
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReasonCode fromInt(int i) {
|
public static ReasonCode fromInt(int i) {
|
||||||
ReasonCode type = intToTypeMap.get(Integer.valueOf(i));
|
ReasonCode type = intToTypeMap.get(Integer.valueOf(i));
|
||||||
if (type == null)
|
if (type == null)
|
||||||
return ReasonCode.UNKNOWN;
|
return ReasonCode.UNKNOWN;
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte asByte() {
|
public byte asByte() {
|
||||||
return (byte) reason;
|
return (byte) reason;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,14 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
/**
|
/**
|
||||||
* This class contains static values of messages on the network. These message
|
* This class contains static values of messages on the network. These message
|
||||||
* will always be the same and therefore don't need to be created each time.
|
* will always be the same and therefore don't need to be created each time.
|
||||||
*
|
*
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 13/04/14 20:19
|
* Created on: 13/04/14 20:19
|
||||||
*/
|
*/
|
||||||
public class StaticMessages {
|
public class StaticMessages {
|
||||||
|
|
||||||
public static final String PEER_ID = Hex.toHexString(HashUtil.randomPeerId());
|
public static final String PEER_ID = Hex.toHexString(HashUtil.randomPeerId());
|
||||||
|
|
||||||
public final static PingMessage PING_MESSAGE = new PingMessage();
|
public final static PingMessage PING_MESSAGE = new PingMessage();
|
||||||
public final static PongMessage PONG_MESSAGE = new PongMessage();
|
public final static PongMessage PONG_MESSAGE = new PongMessage();
|
||||||
public final static HelloMessage HELLO_MESSAGE = generateHelloMessage();
|
public final static HelloMessage HELLO_MESSAGE = generateHelloMessage();
|
||||||
|
@ -43,7 +43,7 @@ public class StaticMessages {
|
||||||
new Capability(Capability.SHH, ShhHandler.VERSION));
|
new Capability(Capability.SHH, ShhHandler.VERSION));
|
||||||
int listenPort = SystemProperties.CONFIG.listenPort();
|
int listenPort = SystemProperties.CONFIG.listenPort();
|
||||||
|
|
||||||
return new HelloMessage(p2pVersion, helloAnnouncement,
|
return new HelloMessage(p2pVersion, helloAnnouncement,
|
||||||
capabilities, listenPort, PEER_ID);
|
capabilities, listenPort, PEER_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum GetPeers message on the network
|
* Wrapper around an Ethereum GetPeers message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.p2p.P2pMessageCodes#GET_PEERS
|
* @see org.ethereum.net.p2p.P2pMessageCodes#GET_PEERS
|
||||||
*/
|
*/
|
||||||
public class GetPeersMessage extends P2pMessage {
|
public class GetPeersMessage extends P2pMessage {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum HelloMessage on the network
|
* Wrapper around an Ethereum HelloMessage on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.p2p.P2pMessageCodes#HELLO
|
* @see org.ethereum.net.p2p.P2pMessageCodes#HELLO
|
||||||
*/
|
*/
|
||||||
public class HelloMessage extends P2pMessage {
|
public class HelloMessage extends P2pMessage {
|
||||||
|
@ -24,7 +24,7 @@ public class HelloMessage extends P2pMessage {
|
||||||
private byte p2pVersion;
|
private byte p2pVersion;
|
||||||
/** The underlying client. A user-readable string. */
|
/** The underlying client. A user-readable string. */
|
||||||
private String clientId;
|
private String clientId;
|
||||||
/** A peer-network capability code, readable ASCII and 3 letters.
|
/** A peer-network capability code, readable ASCII and 3 letters.
|
||||||
* Currently only "eth", "shh" and "bzz" are known. */
|
* Currently only "eth", "shh" and "bzz" are known. */
|
||||||
private List<Capability> capabilities;
|
private List<Capability> capabilities;
|
||||||
/** The port on which the peer is listening for an incoming connection */
|
/** The port on which the peer is listening for an incoming connection */
|
||||||
|
@ -64,10 +64,10 @@ public class HelloMessage extends P2pMessage {
|
||||||
|
|
||||||
RLPElement capId = ((RLPList)capabilityList.get(i)).get(0);
|
RLPElement capId = ((RLPList)capabilityList.get(i)).get(0);
|
||||||
RLPElement capVersion = ((RLPList)capabilityList.get(i)).get(1);
|
RLPElement capVersion = ((RLPList)capabilityList.get(i)).get(1);
|
||||||
|
|
||||||
String name = new String(capId.getRLPData());
|
String name = new String(capId.getRLPData());
|
||||||
byte version = capVersion.getRLPData() == null ? 0 : capVersion.getRLPData()[0];
|
byte version = capVersion.getRLPData() == null ? 0 : capVersion.getRLPData()[0];
|
||||||
|
|
||||||
Capability cap = new Capability(name, version);
|
Capability cap = new Capability(name, version);
|
||||||
this.capabilities.add(cap);
|
this.capabilities.add(cap);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the basic protocol messages between every peer on the network.
|
* Process the basic protocol messages between every peer on the network.
|
||||||
*
|
*
|
||||||
* Peers can send/receive
|
* Peers can send/receive
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>HELLO : Announce themselves to the network</li>
|
* <li>HELLO : Announce themselves to the network</li>
|
||||||
|
@ -48,7 +48,7 @@ import org.springframework.stereotype.Component;
|
||||||
public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
|
public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
|
||||||
|
|
||||||
public final static byte VERSION = 2;
|
public final static byte VERSION = 2;
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger("net");
|
private final static Logger logger = LoggerFactory.getLogger("net");
|
||||||
|
|
||||||
private final Timer timer = new Timer("MessageTimer");
|
private final Timer timer = new Timer("MessageTimer");
|
||||||
|
@ -69,7 +69,7 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
|
||||||
|
|
||||||
this.peerDiscoveryMode = false;
|
this.peerDiscoveryMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public P2pHandler(MessageQueue msgQueue, boolean peerDiscoveryMode) {
|
public P2pHandler(MessageQueue msgQueue, boolean peerDiscoveryMode) {
|
||||||
this.msgQueue = msgQueue;
|
this.msgQueue = msgQueue;
|
||||||
this.peerDiscoveryMode = peerDiscoveryMode;
|
this.peerDiscoveryMode = peerDiscoveryMode;
|
||||||
|
@ -167,7 +167,7 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
|
||||||
ctx.close();
|
ctx.close();
|
||||||
killTimers();
|
killTimers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processPeers(ChannelHandlerContext ctx, PeersMessage peersMessage) {
|
private void processPeers(ChannelHandlerContext ctx, PeersMessage peersMessage) {
|
||||||
worldManager.getPeerDiscovery().addPeers(peersMessage.getPeers());
|
worldManager.getPeerDiscovery().addPeers(peersMessage.getPeers());
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
|
||||||
if (capability.getName().equals(Capability.ETH)) {
|
if (capability.getName().equals(Capability.ETH)) {
|
||||||
EthMessageCodes.setOffset(offset);
|
EthMessageCodes.setOffset(offset);
|
||||||
offset += EthMessageCodes.values().length;
|
offset += EthMessageCodes.values().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capability.getName().equals(Capability.SHH)) {
|
if (capability.getName().equals(Capability.SHH)) {
|
||||||
ShhMessageCodes.setOffset(offset);
|
ShhMessageCodes.setOffset(offset);
|
||||||
|
@ -295,7 +295,7 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
|
||||||
}, 500, 25000);
|
}, 500, 25000);
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public void killTimers(){
|
public void killTimers(){
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
timer.purge();
|
timer.purge();
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class Peer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[ip=" + getAddress().getHostAddress() +
|
return "[ip=" + getAddress().getHostAddress() +
|
||||||
" port=" + getPort()
|
" port=" + getPort()
|
||||||
+ " peerId=" + getPeerId() + "]";
|
+ " peerId=" + getPeerId() + "]";
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum Peers message on the network
|
* Wrapper around an Ethereum Peers message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.p2p.P2pMessageCodes#PEERS
|
* @see org.ethereum.net.p2p.P2pMessageCodes#PEERS
|
||||||
*/
|
*/
|
||||||
public class PeersMessage extends P2pMessage {
|
public class PeersMessage extends P2pMessage {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum Ping message on the network
|
* Wrapper around an Ethereum Ping message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.p2p.P2pMessageCodes#PING
|
* @see org.ethereum.net.p2p.P2pMessageCodes#PING
|
||||||
*/
|
*/
|
||||||
public class PingMessage extends P2pMessage {
|
public class PingMessage extends P2pMessage {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around an Ethereum Pong message on the network
|
* Wrapper around an Ethereum Pong message on the network
|
||||||
*
|
*
|
||||||
* @see org.ethereum.net.p2p.P2pMessageCodes#PONG
|
* @see org.ethereum.net.p2p.P2pMessageCodes#PONG
|
||||||
*/
|
*/
|
||||||
public class PongMessage extends P2pMessage {
|
public class PongMessage extends P2pMessage {
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class DiscoveryChannel {
|
||||||
Bootstrap b = new Bootstrap();
|
Bootstrap b = new Bootstrap();
|
||||||
b.group(workerGroup);
|
b.group(workerGroup);
|
||||||
b.channel(NioSocketChannel.class);
|
b.channel(NioSocketChannel.class);
|
||||||
|
|
||||||
b.option(ChannelOption.SO_KEEPALIVE, true);
|
b.option(ChannelOption.SO_KEEPALIVE, true);
|
||||||
b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT);
|
b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT);
|
||||||
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONFIG.peerConnectionTimeout());
|
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONFIG.peerConnectionTimeout());
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class PeerDiscovery {
|
||||||
private static final Logger logger = LoggerFactory.getLogger("peerdiscovery");
|
private static final Logger logger = LoggerFactory.getLogger("peerdiscovery");
|
||||||
|
|
||||||
private final Set<PeerInfo> peers = Collections.synchronizedSet(new HashSet<PeerInfo>());
|
private final Set<PeerInfo> peers = Collections.synchronizedSet(new HashSet<PeerInfo>());
|
||||||
|
|
||||||
private PeerMonitorThread monitor;
|
private PeerMonitorThread monitor;
|
||||||
private ThreadFactory threadFactory;
|
private ThreadFactory threadFactory;
|
||||||
private ThreadPoolExecutor executorPool;
|
private ThreadPoolExecutor executorPool;
|
||||||
|
@ -77,11 +77,11 @@ public class PeerDiscovery {
|
||||||
public boolean isStarted() {
|
public boolean isStarted() {
|
||||||
return started.get();
|
return started.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<PeerInfo> getPeers() {
|
public Set<PeerInfo> getPeers() {
|
||||||
return peers;
|
return peers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update list of known peers with new peers
|
* Update list of known peers with new peers
|
||||||
* This method checks for duplicate peer id's and addresses
|
* This method checks for duplicate peer id's and addresses
|
||||||
|
|
|
@ -9,8 +9,8 @@ import java.util.concurrent.ThreadPoolExecutor;
|
||||||
/**
|
/**
|
||||||
* A handler to log rejected threads when execution is blocked because the
|
* A handler to log rejected threads when execution is blocked because the
|
||||||
* thread bounds and queue capacities are reached
|
* thread bounds and queue capacities are reached
|
||||||
*
|
*
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 22/05/2014 10:31
|
* Created on: 22/05/2014 10:31
|
||||||
*/
|
*/
|
||||||
public class RejectionLogger implements RejectedExecutionHandler {
|
public class RejectionLogger implements RejectedExecutionHandler {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.springframework.stereotype.Component;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 22/05/2014 09:26
|
* Created on: 22/05/2014 09:26
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class PeerServer {
|
||||||
|
|
||||||
b.group(bossGroup, workerGroup);
|
b.group(bossGroup, workerGroup);
|
||||||
b.channel(NioServerSocketChannel.class);
|
b.channel(NioServerSocketChannel.class);
|
||||||
|
|
||||||
b.option(ChannelOption.SO_KEEPALIVE, true);
|
b.option(ChannelOption.SO_KEEPALIVE, true);
|
||||||
b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT);
|
b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT);
|
||||||
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONFIG.peerConnectionTimeout());
|
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONFIG.peerConnectionTimeout());
|
||||||
|
|
|
@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the messages between peers with 'shh' capability on the network.
|
* Process the messages between peers with 'shh' capability on the network.
|
||||||
*
|
*
|
||||||
* Peers with 'shh' capability can send/receive:
|
* Peers with 'shh' capability can send/receive:
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -63,7 +63,7 @@ public class ShhHandler extends SimpleChannelInboundHandler<ShhMessage> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
logger.error(cause.getCause().toString());
|
logger.error(cause.getCause().toString());
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.util.Map;
|
||||||
* A list of commands for the Whisper network protocol.
|
* A list of commands for the Whisper network protocol.
|
||||||
* <br>
|
* <br>
|
||||||
* The codes for these commands are the first byte in every packet.
|
* The codes for these commands are the first byte in every packet.
|
||||||
*
|
*
|
||||||
* @see <a href="https://github.com/ethereum/wiki/wiki/Wire-Protocol">
|
* @see <a href="https://github.com/ethereum/wiki/wiki/Wire-Protocol">
|
||||||
* https://github.com/ethereum/wiki/wiki/Wire-Protocol</a>
|
* https://github.com/ethereum/wiki/wiki/Wire-Protocol</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class MessageDecoder extends ByteToMessageDecoder {
|
||||||
|
|
||||||
byte[] encoded = new byte[in.readInt()];
|
byte[] encoded = new byte[in.readInt()];
|
||||||
in.readBytes(encoded);
|
in.readBytes(encoded);
|
||||||
|
|
||||||
if (loggerWire.isDebugEnabled())
|
if (loggerWire.isDebugEnabled())
|
||||||
loggerWire.debug("Encoded: [{}]", Hex.toHexString(encoded));
|
loggerWire.debug("Encoded: [{}]", Hex.toHexString(encoded));
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ public class MessageDecoder extends ByteToMessageDecoder {
|
||||||
out.add(msg);
|
out.add(msg);
|
||||||
in.markReaderIndex();
|
in.markReaderIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isValidEthereumPacket(ByteBuf in) {
|
private boolean isValidEthereumPacket(ByteBuf in) {
|
||||||
// Ethereum message is at least 8 bytes
|
// Ethereum message is at least 8 bytes
|
||||||
if (in.readableBytes() < 8)
|
if (in.readableBytes() < 8)
|
||||||
|
|
|
@ -39,10 +39,10 @@ public class MessageEncoder extends MessageToByteEncoder<Message> {
|
||||||
loggerNet.info("To: \t{} \tSend: \t{}", ctx.channel().remoteAddress(), msg);
|
loggerNet.info("To: \t{} \tSend: \t{}", ctx.channel().remoteAddress(), msg);
|
||||||
|
|
||||||
byte[] encoded = msg.getEncoded();
|
byte[] encoded = msg.getEncoded();
|
||||||
|
|
||||||
if (loggerWire.isDebugEnabled())
|
if (loggerWire.isDebugEnabled())
|
||||||
loggerWire.debug("Encoded: [{}]", Hex.toHexString(encoded));
|
loggerWire.debug("Encoded: [{}]", Hex.toHexString(encoded));
|
||||||
|
|
||||||
out.capacity(encoded.length + 8);
|
out.capacity(encoded.length + 8);
|
||||||
out.writeBytes(StaticMessages.SYNC_TOKEN);
|
out.writeBytes(StaticMessages.SYNC_TOKEN);
|
||||||
out.writeBytes(ByteUtil.calcPacketLength(encoded));
|
out.writeBytes(ByteUtil.calcPacketLength(encoded));
|
||||||
|
|
|
@ -124,7 +124,7 @@ public class SerpentCompiler {
|
||||||
|
|
||||||
// encode ref for 5 bytes
|
// encode ref for 5 bytes
|
||||||
for (int i = 0; i < lexaList.size(); ++i) {
|
for (int i = 0; i < lexaList.size(); ++i) {
|
||||||
|
|
||||||
String lexa = lexaList.get(i);
|
String lexa = lexaList.get(i);
|
||||||
if (!lexa.contains("REF_")) continue;
|
if (!lexa.contains("REF_")) continue;
|
||||||
lexaList.add(i + 1, lexa);
|
lexaList.add(i + 1, lexa);
|
||||||
|
|
|
@ -15,7 +15,7 @@ import org.iq80.leveldb.WriteBatch;
|
||||||
* Created on: 20/05/2014 10:44
|
* Created on: 20/05/2014 10:44
|
||||||
*/
|
*/
|
||||||
public class Cache {
|
public class Cache {
|
||||||
|
|
||||||
private Map<ByteArrayWrapper, Node> nodes = new ConcurrentHashMap<>();
|
private Map<ByteArrayWrapper, Node> nodes = new ConcurrentHashMap<>();
|
||||||
private DB db;
|
private DB db;
|
||||||
private boolean isDirty;
|
private boolean isDirty;
|
||||||
|
@ -26,8 +26,8 @@ public class Cache {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put the node in the cache if RLP encoded value is longer than 32 bytes
|
* Put the node in the cache if RLP encoded value is longer than 32 bytes
|
||||||
*
|
*
|
||||||
* @param o the Node which could be a pair-, multi-item Node or single Value
|
* @param o the Node which could be a pair-, multi-item Node or single Value
|
||||||
* @return sha3 hash of RLP encoded node if length > 32 otherwise return node itself
|
* @return sha3 hash of RLP encoded node if length > 32 otherwise return node itself
|
||||||
*/
|
*/
|
||||||
public Object put(Object o) {
|
public Object put(Object o) {
|
||||||
|
|
|
@ -3,40 +3,40 @@ package org.ethereum.trie;
|
||||||
import org.ethereum.util.Value;
|
import org.ethereum.util.Value;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Node in a Merkle Patricia Tree is one of the following:
|
* A Node in a Merkle Patricia Tree is one of the following:
|
||||||
*
|
*
|
||||||
* - NULL (represented as the empty string)
|
* - NULL (represented as the empty string)
|
||||||
* - A two-item array [ key, value ] (1 key for 2-item array)
|
* - A two-item array [ key, value ] (1 key for 2-item array)
|
||||||
* - A 17-item array [ v0 ... v15, vt ] (16 keys for 17-item array)
|
* - A 17-item array [ v0 ... v15, vt ] (16 keys for 17-item array)
|
||||||
*
|
*
|
||||||
* The idea is that in the event that there is a long path of nodes
|
* The idea is that in the event that there is a long path of nodes
|
||||||
* each with only one element, we shortcut the descent by setting up
|
* each with only one element, we shortcut the descent by setting up
|
||||||
* a [ key, value ] node, where the key gives the hexadecimal path
|
* a [ key, value ] node, where the key gives the hexadecimal path
|
||||||
* to descend, in the compact encoding described above, and the value
|
* to descend, in the compact encoding described above, and the value
|
||||||
* is just the hash of the node like in the standard radix tree.
|
* is just the hash of the node like in the standard radix tree.
|
||||||
*
|
*
|
||||||
* R
|
* R
|
||||||
* / \
|
* / \
|
||||||
* / \
|
* / \
|
||||||
* N N
|
* N N
|
||||||
* / \ / \
|
* / \ / \
|
||||||
* L L L L
|
* L L L L
|
||||||
* *
|
* *
|
||||||
* Also, we add another conceptual change: internal nodes can no longer
|
* Also, we add another conceptual change: internal nodes can no longer
|
||||||
* have values, only leaves with no children of their own can; however,
|
* have values, only leaves with no children of their own can; however,
|
||||||
* since to be fully generic we want the key/value store to be able to
|
* since to be fully generic we want the key/value store to be able to
|
||||||
* store keys like 'dog' and 'doge' at the same time, we simply add
|
* store keys like 'dog' and 'doge' at the same time, we simply add
|
||||||
* a terminator symbol (16) to the alphabet so there is never a value
|
* a terminator symbol (16) to the alphabet so there is never a value
|
||||||
* "en-route" to another value.
|
* "en-route" to another value.
|
||||||
*
|
*
|
||||||
* Where a node is referenced inside a node, what is included is:
|
* Where a node is referenced inside a node, what is included is:
|
||||||
*
|
*
|
||||||
* H(rlp.encode(x)) where H(x) = sha3(x) if len(x) >= 32 else x
|
* H(rlp.encode(x)) where H(x) = sha3(x) if len(x) >= 32 else x
|
||||||
*
|
*
|
||||||
* Note that when updating a trie, you will need to store the key/value pair (sha3(x), x)
|
* Note that when updating a trie, you will need to store the key/value pair (sha3(x), x)
|
||||||
* in a persistent lookup table when you create a node with length >= 32,
|
* in a persistent lookup table when you create a node with length >= 32,
|
||||||
* but if the node is shorter than that then you do not need to store anything
|
* but if the node is shorter than that then you do not need to store anything
|
||||||
* when length < 32 for the obvious reason that the function f(x) = x is reversible.
|
* when length < 32 for the obvious reason that the function f(x) = x is reversible.
|
||||||
*
|
*
|
||||||
* www.ethereumJ.com
|
* www.ethereumJ.com
|
||||||
|
@ -52,7 +52,7 @@ public class Node {
|
||||||
public Node(Value val) {
|
public Node(Value val) {
|
||||||
this(val, false);
|
this(val, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Node(Value val, boolean dirty) {
|
public Node(Value val, boolean dirty) {
|
||||||
this.value = val;
|
this.value = val;
|
||||||
this.dirty = dirty;
|
this.dirty = dirty;
|
||||||
|
@ -65,11 +65,11 @@ public class Node {
|
||||||
public boolean isDirty() {
|
public boolean isDirty() {
|
||||||
return dirty;
|
return dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDirty(boolean ditry) {
|
public void setDirty(boolean ditry) {
|
||||||
this.dirty = ditry;
|
this.dirty = ditry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Value getValue() {
|
public Value getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package org.ethereum.trie;
|
package org.ethereum.trie;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trie interface for the main data structure in Ethereum
|
* Trie interface for the main data structure in Ethereum
|
||||||
* which is used to store both the account state and storage of each account.
|
* which is used to store both the account state and storage of each account.
|
||||||
*/
|
*/
|
||||||
public interface Trie {
|
public interface Trie {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a value from the trie for a given key
|
* Gets a value from the trie for a given key
|
||||||
*
|
*
|
||||||
* @param key - any length byte array
|
* @param key - any length byte array
|
||||||
* @return an rlp encoded byte array of the stored object
|
* @return an rlp encoded byte array of the stored object
|
||||||
*/
|
*/
|
||||||
|
@ -16,43 +16,43 @@ public interface Trie {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert or update a value in the trie for a specified key
|
* Insert or update a value in the trie for a specified key
|
||||||
*
|
*
|
||||||
* @param key - any length byte array
|
* @param key - any length byte array
|
||||||
* @param value rlp encoded byte array of the object to store
|
* @param value rlp encoded byte array of the object to store
|
||||||
*/
|
*/
|
||||||
public void update(byte[] key, byte[] value);
|
public void update(byte[] key, byte[] value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a key/value from the trie for a given key
|
* Deletes a key/value from the trie for a given key
|
||||||
*
|
*
|
||||||
* @param key - any length byte array
|
* @param key - any length byte array
|
||||||
*/
|
*/
|
||||||
public void delete(byte[] key);
|
public void delete(byte[] key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a SHA-3 hash from the top node of the trie
|
* Returns a SHA-3 hash from the top node of the trie
|
||||||
*
|
*
|
||||||
* @return 32-byte SHA-3 hash representing the entire contents of the trie.
|
* @return 32-byte SHA-3 hash representing the entire contents of the trie.
|
||||||
*/
|
*/
|
||||||
public byte[] getRootHash();
|
public byte[] getRootHash();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the top node of the trie
|
* Set the top node of the trie
|
||||||
*
|
*
|
||||||
* @param root - 32-byte SHA-3 hash of the root node
|
* @param root - 32-byte SHA-3 hash of the root node
|
||||||
*/
|
*/
|
||||||
public void setRoot(byte[] root);
|
public void setRoot(byte[] root);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commit all the changes until now
|
* Commit all the changes until now
|
||||||
*/
|
*/
|
||||||
public void sync();
|
public void sync();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discard all the changes until now
|
* Discard all the changes until now
|
||||||
*/
|
*/
|
||||||
public void undo();
|
public void undo();
|
||||||
|
|
||||||
public String getTrieDump();
|
public String getTrieDump();
|
||||||
|
|
||||||
public boolean validate();
|
public boolean validate();
|
||||||
|
|
|
@ -19,19 +19,19 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The modified Merkle Patricia tree (trie) provides a persistent data structure
|
* The modified Merkle Patricia tree (trie) provides a persistent data structure
|
||||||
* to map between arbitrary-length binary data (byte arrays). It is defined in terms of
|
* to map between arbitrary-length binary data (byte arrays). It is defined in terms of
|
||||||
* a mutable data structure to map between 256-bit binary fragments and arbitrary-length
|
* a mutable data structure to map between 256-bit binary fragments and arbitrary-length
|
||||||
* binary data, typically implemented as a database. The core of the trie, and its sole
|
* binary data, typically implemented as a database. The core of the trie, and its sole
|
||||||
* requirement in terms of the protocol specification is to provide a single value that
|
* requirement in terms of the protocol specification is to provide a single value that
|
||||||
* identifies a given set of key-value pairs, which may either a 32 byte sequence or
|
* identifies a given set of key-value pairs, which may either a 32 byte sequence or
|
||||||
* the empty byte sequence. It is left as an implementation consideration to store and
|
* the empty byte sequence. It is left as an implementation consideration to store and
|
||||||
* maintain the structure of the trie in a manner the allows effective and efficient
|
* maintain the structure of the trie in a manner the allows effective and efficient
|
||||||
* realisation of the protocol.
|
* realisation of the protocol.
|
||||||
*
|
*
|
||||||
* The trie implements a caching mechanism and will use cached values if they are present.
|
* The trie implements a caching mechanism and will use cached values if they are present.
|
||||||
* If a node is not present in the cache it will try to fetch it from the database and
|
* If a node is not present in the cache it will try to fetch it from the database and
|
||||||
* store the cached value.
|
* store the cached value.
|
||||||
*
|
*
|
||||||
* <b>Note:</b> the data isn't persisted unless `sync` is explicitly called.
|
* <b>Note:</b> the data isn't persisted unless `sync` is explicitly called.
|
||||||
*
|
*
|
||||||
|
@ -63,7 +63,7 @@ public class TrieImpl implements Trie {
|
||||||
public TrieIterator getIterator() {
|
public TrieIterator getIterator() {
|
||||||
return new TrieIterator(this);
|
return new TrieIterator(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCache(Cache cache) {
|
public void setCache(Cache cache) {
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
}
|
}
|
||||||
|
@ -101,14 +101,14 @@ public class TrieImpl implements Trie {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] get(byte[] key) {
|
public byte[] get(byte[] key) {
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
logger.debug("Retrieving key {}", Hex.toHexString(key));
|
logger.debug("Retrieving key {}", Hex.toHexString(key));
|
||||||
byte[] k = binToNibbles(key);
|
byte[] k = binToNibbles(key);
|
||||||
Value c = new Value(this.get(this.root, k));
|
Value c = new Value(this.get(this.root, k));
|
||||||
|
|
||||||
return (c == null)? null : c.asBytes();
|
return (c == null)? null : c.asBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert key/value pair into trie
|
* Insert key/value pair into trie
|
||||||
*
|
*
|
||||||
|
@ -140,7 +140,7 @@ public class TrieImpl implements Trie {
|
||||||
public void delete(String key) {
|
public void delete(String key) {
|
||||||
this.update(key.getBytes(), "".getBytes());
|
this.update(key.getBytes(), "".getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(byte[] key) {
|
public void delete(byte[] key) {
|
||||||
delete(new String(key));
|
delete(new String(key));
|
||||||
|
@ -149,7 +149,7 @@ public class TrieImpl implements Trie {
|
||||||
logger.debug("New root-hash: {}", Hex.toHexString(this.getRootHash()));
|
logger.debug("New root-hash: {}", Hex.toHexString(this.getRootHash()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getRootHash() {
|
public byte[] getRootHash() {
|
||||||
if (root == null
|
if (root == null
|
||||||
|
|
|
@ -11,7 +11,7 @@ import static org.ethereum.util.CompactEncoder.unpackToNibbles;
|
||||||
* Created on: 20/05/2014 10:44
|
* Created on: 20/05/2014 10:44
|
||||||
*/
|
*/
|
||||||
public class TrieIterator {
|
public class TrieIterator {
|
||||||
|
|
||||||
private TrieImpl trie;
|
private TrieImpl trie;
|
||||||
private String key;
|
private String key;
|
||||||
private String value;
|
private String value;
|
||||||
|
@ -73,9 +73,9 @@ public class TrieIterator {
|
||||||
|
|
||||||
public int purge() {
|
public int purge() {
|
||||||
List<byte[]> shas = this.collect();
|
List<byte[]> shas = this.collect();
|
||||||
|
|
||||||
for (byte[] sha : shas) {
|
for (byte[] sha : shas) {
|
||||||
this.trie.getCache().delete(sha);
|
this.trie.getCache().delete(sha);
|
||||||
}
|
}
|
||||||
return this.values.size();
|
return this.values.size();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class ByteUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The regular {@link java.math.BigInteger#toByteArray()} method isn't quite what we often need:
|
* The regular {@link java.math.BigInteger#toByteArray()} method isn't quite what we often need:
|
||||||
* it appends a leading zero to indicate that the number is positive and may need padding.
|
* it appends a leading zero to indicate that the number is positive and may need padding.
|
||||||
*
|
*
|
||||||
* @param b the integer to format into a byte array
|
* @param b the integer to format into a byte array
|
||||||
|
@ -39,23 +39,23 @@ public class ByteUtil {
|
||||||
int start = (biBytes.length == numBytes + 1) ? 1 : 0;
|
int start = (biBytes.length == numBytes + 1) ? 1 : 0;
|
||||||
int length = Math.min(biBytes.length, numBytes);
|
int length = Math.min(biBytes.length, numBytes);
|
||||||
System.arraycopy(biBytes, start, bytes, numBytes - length, length);
|
System.arraycopy(biBytes, start, bytes, numBytes - length, length);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Omitting sign indication byte.
|
* Omitting sign indication byte.
|
||||||
* <br><br>
|
* <br><br>
|
||||||
* Instead of {@link org.spongycastle.util.BigIntegers#asUnsignedByteArray(BigInteger)}
|
* Instead of {@link org.spongycastle.util.BigIntegers#asUnsignedByteArray(BigInteger)}
|
||||||
* <br>we use this custom method to avoid an empty array in case of BigInteger.ZERO
|
* <br>we use this custom method to avoid an empty array in case of BigInteger.ZERO
|
||||||
*
|
*
|
||||||
* @param value - any big integer number. A <code>null</code>-value will return <code>null</code>
|
* @param value - any big integer number. A <code>null</code>-value will return <code>null</code>
|
||||||
* @return A byte array without a leading zero byte if present in the signed encoding.
|
* @return A byte array without a leading zero byte if present in the signed encoding.
|
||||||
* BigInteger.ZERO will return an array with length 1 and byte-value 0.
|
* BigInteger.ZERO will return an array with length 1 and byte-value 0.
|
||||||
*/
|
*/
|
||||||
public static byte[] bigIntegerToBytes(BigInteger value) {
|
public static byte[] bigIntegerToBytes(BigInteger value) {
|
||||||
if (value == null)
|
if (value == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] data = value.toByteArray();
|
byte[] data = value.toByteArray();
|
||||||
|
|
||||||
if (data.length != 1 && data[0] == 0) {
|
if (data.length != 1 && data[0] == 0) {
|
||||||
|
@ -66,10 +66,10 @@ public class ByteUtil {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the amount of nibbles that match each other from 0 ...
|
* Returns the amount of nibbles that match each other from 0 ...
|
||||||
* amount will never be larger than smallest input
|
* amount will never be larger than smallest input
|
||||||
*
|
*
|
||||||
* @param a - first input
|
* @param a - first input
|
||||||
* @param b - second input
|
* @param b - second input
|
||||||
* @return Number of bytes that match
|
* @return Number of bytes that match
|
||||||
|
@ -84,22 +84,22 @@ public class ByteUtil {
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a long value into a byte array.
|
* Converts a long value into a byte array.
|
||||||
*
|
*
|
||||||
* @param val - long value to convert
|
* @param val - long value to convert
|
||||||
* @return <code>byte[]</code> of length 8, representing the long value
|
* @return <code>byte[]</code> of length 8, representing the long value
|
||||||
*/
|
*/
|
||||||
public static byte[] longToBytes(long val) {
|
public static byte[] longToBytes(long val) {
|
||||||
return ByteBuffer.allocate(8).putLong(val).array();
|
return ByteBuffer.allocate(8).putLong(val).array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a byte-array into a hex String.<br>
|
* Convert a byte-array into a hex String.<br>
|
||||||
* Works similar to {@link Hex#toHexString}
|
* Works similar to {@link Hex#toHexString}
|
||||||
* but allows for <code>null</code>
|
* but allows for <code>null</code>
|
||||||
*
|
*
|
||||||
* @param data - byte-array to convert to a hex-string
|
* @param data - byte-array to convert to a hex-string
|
||||||
* @return hex representation of the data.<br>
|
* @return hex representation of the data.<br>
|
||||||
* Returns an empty String if the input is <code>null</code>
|
* Returns an empty String if the input is <code>null</code>
|
||||||
|
@ -109,7 +109,7 @@ public class ByteUtil {
|
||||||
public static String toHexString(byte[] data) {
|
public static String toHexString(byte[] data) {
|
||||||
return data == null ? "" : Hex.toHexString(data);
|
return data == null ? "" : Hex.toHexString(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate packet length
|
* Calculate packet length
|
||||||
* @param msg byte[]
|
* @param msg byte[]
|
||||||
|
@ -124,14 +124,14 @@ public class ByteUtil {
|
||||||
(byte)((msgLen ) & 0xFF)};
|
(byte)((msgLen ) & 0xFF)};
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cast hex encoded value from byte[] to int
|
* Cast hex encoded value from byte[] to int
|
||||||
*
|
*
|
||||||
* Limited to Integer.MAX_VALUE: 2^32-1 (4 bytes)
|
* Limited to Integer.MAX_VALUE: 2^32-1 (4 bytes)
|
||||||
*
|
*
|
||||||
* @param b array contains the values
|
* @param b array contains the values
|
||||||
* @return unsigned positive int value.
|
* @return unsigned positive int value.
|
||||||
*/
|
*/
|
||||||
public static int byteArrayToInt(byte[] b) {
|
public static int byteArrayToInt(byte[] b) {
|
||||||
if (b == null || b.length == 0)
|
if (b == null || b.length == 0)
|
||||||
|
@ -153,10 +153,10 @@ public class ByteUtil {
|
||||||
return new BigInteger(1, b).longValue();
|
return new BigInteger(1, b).longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn nibbles to a pretty looking output string
|
* Turn nibbles to a pretty looking output string
|
||||||
*
|
*
|
||||||
* Example. [ 1, 2, 3, 4, 5 ] becomes '\x11\x23\x45'
|
* Example. [ 1, 2, 3, 4, 5 ] becomes '\x11\x23\x45'
|
||||||
*
|
*
|
||||||
* @param nibbles - getting byte of data [ 04 ] and turning
|
* @param nibbles - getting byte of data [ 04 ] and turning
|
||||||
|
@ -171,7 +171,7 @@ public class ByteUtil {
|
||||||
}
|
}
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String oneByteToHexString(byte value) {
|
public static String oneByteToHexString(byte value) {
|
||||||
String retVal = Integer.toString(value & 0xFF, 16);
|
String retVal = Integer.toString(value & 0xFF, 16);
|
||||||
if (retVal.length() == 1) retVal = "0" + retVal;
|
if (retVal.length() == 1) retVal = "0" + retVal;
|
||||||
|
@ -197,7 +197,7 @@ public class ByteUtil {
|
||||||
if (bytes == 0) ++bytes;
|
if (bytes == 0) ++bytes;
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param arg - not more that 32 bits
|
* @param arg - not more that 32 bits
|
||||||
* @return - bytes of the value pad with complete to 32 zeroes
|
* @return - bytes of the value pad with complete to 32 zeroes
|
||||||
|
@ -215,7 +215,7 @@ public class ByteUtil {
|
||||||
else
|
else
|
||||||
data = arg.toString().trim().getBytes();
|
data = arg.toString().trim().getBytes();
|
||||||
|
|
||||||
|
|
||||||
if (data.length > 32)
|
if (data.length > 32)
|
||||||
throw new RuntimeException("values can't be more than 32 byte");
|
throw new RuntimeException("values can't be more than 32 byte");
|
||||||
|
|
||||||
|
@ -304,9 +304,9 @@ public class ByteUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function to copy a byte array into a new byte array with given size.
|
* Utility function to copy a byte array into a new byte array with given size.
|
||||||
* If the src length is smaller than the given size, the result will be left-padded
|
* If the src length is smaller than the given size, the result will be left-padded
|
||||||
* with zeros.
|
* with zeros.
|
||||||
*
|
*
|
||||||
* @param value - a BigInteger with a maximum value of 2^256-1
|
* @param value - a BigInteger with a maximum value of 2^256-1
|
||||||
* @return Byte array of given size with a copy of the <code>src</code>
|
* @return Byte array of given size with a copy of the <code>src</code>
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,29 +12,29 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compact encoding of hex sequence with optional terminator
|
* Compact encoding of hex sequence with optional terminator
|
||||||
*
|
*
|
||||||
* The traditional compact way of encoding a hex string is to convert it into binary
|
* The traditional compact way of encoding a hex string is to convert it into binary
|
||||||
* - that is, a string like 0f1248 would become three bytes 15, 18, 72. However,
|
* - that is, a string like 0f1248 would become three bytes 15, 18, 72. However,
|
||||||
* this approach has one slight problem: what if the length of the hex string is odd?
|
* this approach has one slight problem: what if the length of the hex string is odd?
|
||||||
* In that case, there is no way to distinguish between, say, 0f1248 and f1248.
|
* In that case, there is no way to distinguish between, say, 0f1248 and f1248.
|
||||||
*
|
*
|
||||||
* Additionally, our application in the Merkle Patricia tree requires the additional feature
|
* Additionally, our application in the Merkle Patricia tree requires the additional feature
|
||||||
* that a hex string can also have a special "terminator symbol" at the end (denoted by the 'T').
|
* that a hex string can also have a special "terminator symbol" at the end (denoted by the 'T').
|
||||||
* A terminator symbol can occur only once, and only at the end.
|
* A terminator symbol can occur only once, and only at the end.
|
||||||
*
|
*
|
||||||
* An alternative way of thinking about this to not think of there being a terminator symbol,
|
* An alternative way of thinking about this to not think of there being a terminator symbol,
|
||||||
* but instead treat bit specifying the existence of the terminator symbol as a bit specifying
|
* but instead treat bit specifying the existence of the terminator symbol as a bit specifying
|
||||||
* that the given node encodes a final node, where the value is an actual value, rather than
|
* that the given node encodes a final node, where the value is an actual value, rather than
|
||||||
* the hash of yet another node.
|
* the hash of yet another node.
|
||||||
*
|
*
|
||||||
* To solve both of these issues, we force the first nibble of the final byte-stream to encode
|
* To solve both of these issues, we force the first nibble of the final byte-stream to encode
|
||||||
* two flags, specifying oddness of length (ignoring the 'T' symbol) and terminator status;
|
* two flags, specifying oddness of length (ignoring the 'T' symbol) and terminator status;
|
||||||
* these are placed, respectively, into the two lowest significant bits of the first nibble.
|
* these are placed, respectively, into the two lowest significant bits of the first nibble.
|
||||||
* In the case of an even-length hex string, we must introduce a second nibble (of value zero)
|
* In the case of an even-length hex string, we must introduce a second nibble (of value zero)
|
||||||
* to ensure the hex-string is even in length and thus is representable by a whole number of bytes.
|
* to ensure the hex-string is even in length and thus is representable by a whole number of bytes.
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
* > [ 1, 2, 3, 4, 5 ]
|
* > [ 1, 2, 3, 4, 5 ]
|
||||||
* '\x11\x23\x45'
|
* '\x11\x23\x45'
|
||||||
|
@ -77,7 +77,7 @@ public class CompactEncoder {
|
||||||
*/
|
*/
|
||||||
public static byte[] packNibbles(byte[] nibbles) {
|
public static byte[] packNibbles(byte[] nibbles) {
|
||||||
int terminator = 0;
|
int terminator = 0;
|
||||||
|
|
||||||
if (nibbles[nibbles.length-1] == TERMINATOR) {
|
if (nibbles[nibbles.length-1] == TERMINATOR) {
|
||||||
terminator = 1;
|
terminator = 1;
|
||||||
nibbles = copyOf(nibbles, nibbles.length-1);
|
nibbles = copyOf(nibbles, nibbles.length-1);
|
||||||
|
@ -100,9 +100,9 @@ public class CompactEncoder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpack a binary string to its nibbles equivalent
|
* Unpack a binary string to its nibbles equivalent
|
||||||
*
|
*
|
||||||
* @param str of binary data
|
* @param str of binary data
|
||||||
* @return array of nibbles in byte-format
|
* @return array of nibbles in byte-format
|
||||||
*/
|
*/
|
||||||
public static byte[] unpackToNibbles(byte[] str) {
|
public static byte[] unpackToNibbles(byte[] str) {
|
||||||
byte[] base = binToNibbles(str);
|
byte[] base = binToNibbles(str);
|
||||||
|
@ -122,7 +122,7 @@ public class CompactEncoder {
|
||||||
* Transforms a binary array to hexadecimal format + terminator
|
* Transforms a binary array to hexadecimal format + terminator
|
||||||
*
|
*
|
||||||
* @param str byte[]
|
* @param str byte[]
|
||||||
* @return array with each individual nibble adding a terminator at the end
|
* @return array with each individual nibble adding a terminator at the end
|
||||||
*/
|
*/
|
||||||
public static byte[] binToNibbles(byte[] str) {
|
public static byte[] binToNibbles(byte[] str) {
|
||||||
byte[] hexEncoded = encode(str);
|
byte[] hexEncoded = encode(str);
|
||||||
|
|
|
@ -9,23 +9,23 @@ public class DecodeResult implements Serializable {
|
||||||
|
|
||||||
private int pos;
|
private int pos;
|
||||||
private Object decoded;
|
private Object decoded;
|
||||||
|
|
||||||
public DecodeResult(int pos, Object decoded) {
|
public DecodeResult(int pos, Object decoded) {
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
this.decoded = decoded;
|
this.decoded = decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPos() {
|
public int getPos() {
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
public Object getDecoded() {
|
public Object getDecoded() {
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return asString(this.decoded);
|
return asString(this.decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String asString(Object decoded) {
|
private String asString(Object decoded) {
|
||||||
if(decoded instanceof String) {
|
if(decoded instanceof String) {
|
||||||
return (String) decoded;
|
return (String) decoded;
|
||||||
|
@ -37,7 +37,7 @@ public class DecodeResult implements Serializable {
|
||||||
result += asString(item);
|
result += asString(item);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
throw new RuntimeException("Not a valid type. Should not occur");
|
throw new RuntimeException("Not a valid type. Should not occur");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ public abstract class FastByteComparisons {
|
||||||
private static class LexicographicalComparerHolder {
|
private static class LexicographicalComparerHolder {
|
||||||
static final String UNSAFE_COMPARER_NAME =
|
static final String UNSAFE_COMPARER_NAME =
|
||||||
LexicographicalComparerHolder.class.getName() + "$UnsafeComparer";
|
LexicographicalComparerHolder.class.getName() + "$UnsafeComparer";
|
||||||
|
|
||||||
static final Comparer<byte[]> BEST_COMPARER = getBestComparer();
|
static final Comparer<byte[]> BEST_COMPARER = getBestComparer();
|
||||||
/**
|
/**
|
||||||
* Returns the Unsafe-using Comparer, or falls back to the pure-Java
|
* Returns the Unsafe-using Comparer, or falls back to the pure-Java
|
||||||
|
@ -91,7 +91,7 @@ public abstract class FastByteComparisons {
|
||||||
return lexicographicalComparerJavaImpl();
|
return lexicographicalComparerJavaImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum PureJavaComparer implements Comparer<byte[]> {
|
private enum PureJavaComparer implements Comparer<byte[]> {
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ public abstract class FastByteComparisons {
|
||||||
return length1 - length2;
|
return length1 - length2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused") // used via reflection
|
@SuppressWarnings("unused") // used via reflection
|
||||||
private enum UnsafeComparer implements Comparer<byte[]> {
|
private enum UnsafeComparer implements Comparer<byte[]> {
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class LRUMap<K,V> extends ConcurrentHashMap<K,V> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
protected final int maxEntries;
|
protected final int maxEntries;
|
||||||
|
|
||||||
public LRUMap(int initialEntries, int maxEntries) {
|
public LRUMap(int initialEntries, int maxEntries) {
|
||||||
super(initialEntries, 0.8f, 3);
|
super(initialEntries, 0.8f, 3);
|
||||||
this.maxEntries = maxEntries;
|
this.maxEntries = maxEntries;
|
||||||
|
|
|
@ -19,8 +19,8 @@ import org.ethereum.util.RLPList;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursive Length Prefix (RLP) encoding.
|
* Recursive Length Prefix (RLP) encoding.
|
||||||
*
|
*
|
||||||
* The purpose of RLP is to encode arbitrarily nested arrays of binary data, and
|
* The purpose of RLP is to encode arbitrarily nested arrays of binary data, and
|
||||||
* RLP is the main encoding method used to serialize objects in Ethereum. The
|
* RLP is the main encoding method used to serialize objects in Ethereum. The
|
||||||
* only purpose of RLP is to encode structure; encoding specific atomic data
|
* only purpose of RLP is to encode structure; encoding specific atomic data
|
||||||
|
@ -30,18 +30,18 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
* canonical forms are to either use [[k1,v1],[k2,v2]...] with keys in
|
* canonical forms are to either use [[k1,v1],[k2,v2]...] with keys in
|
||||||
* lexicographic order or to use the higher-level Patricia Tree encoding as
|
* lexicographic order or to use the higher-level Patricia Tree encoding as
|
||||||
* Ethereum does.
|
* Ethereum does.
|
||||||
*
|
*
|
||||||
* The RLP encoding function takes in an item. An item is defined as follows:
|
* The RLP encoding function takes in an item. An item is defined as follows:
|
||||||
*
|
*
|
||||||
* - A string (ie. byte array) is an item - A list of items is an item
|
* - A string (ie. byte array) is an item - A list of items is an item
|
||||||
*
|
*
|
||||||
* For example, an empty string is an item, as is the string containing the word
|
* For example, an empty string is an item, as is the string containing the word
|
||||||
* "cat", a list containing any number of strings, as well as more complex data
|
* "cat", a list containing any number of strings, as well as more complex data
|
||||||
* structures like ["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]. Note
|
* structures like ["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]. Note
|
||||||
* that in the context of the rest of this article, "string" will be used as a
|
* that in the context of the rest of this article, "string" will be used as a
|
||||||
* synonym for "a certain number of bytes of binary data"; no special encodings
|
* synonym for "a certain number of bytes of binary data"; no special encodings
|
||||||
* are used and no knowledge about the content of the strings is implied.
|
* are used and no knowledge about the content of the strings is implied.
|
||||||
*
|
*
|
||||||
* See: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
|
* See: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
|
||||||
*
|
*
|
||||||
* www.ethereumJ.com
|
* www.ethereumJ.com
|
||||||
|
@ -57,16 +57,16 @@ public class RLP {
|
||||||
/**
|
/**
|
||||||
* Reason for threshold according to Vitalik Buterin:
|
* Reason for threshold according to Vitalik Buterin:
|
||||||
* - 56 bytes maximizes the benefit of both options
|
* - 56 bytes maximizes the benefit of both options
|
||||||
* - if we went with 60 then we would have only had 4 slots for long strings
|
* - if we went with 60 then we would have only had 4 slots for long strings
|
||||||
* so RLP would not have been able to store objects above 4gb
|
* so RLP would not have been able to store objects above 4gb
|
||||||
* - if we went with 48 then RLP would be fine for 2^128 space, but that's way too much
|
* - if we went with 48 then RLP would be fine for 2^128 space, but that's way too much
|
||||||
* - so 56 and 2^64 space seems like the right place to put the cutoff
|
* - so 56 and 2^64 space seems like the right place to put the cutoff
|
||||||
* - also, that's where Bitcoin's varint does the cutof
|
* - also, that's where Bitcoin's varint does the cutof
|
||||||
**/
|
**/
|
||||||
private static int SIZE_THRESHOLD = 56;
|
private static int SIZE_THRESHOLD = 56;
|
||||||
|
|
||||||
/** RLP encoding rules are defined as follows: */
|
/** RLP encoding rules are defined as follows: */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For a single byte whose value is in the [0x00, 0x7f] range, that byte is
|
* For a single byte whose value is in the [0x00, 0x7f] range, that byte is
|
||||||
* its own RLP encoding.
|
* its own RLP encoding.
|
||||||
|
@ -110,12 +110,12 @@ public class RLP {
|
||||||
* range of the first byte is thus [0xf8, 0xff].
|
* range of the first byte is thus [0xf8, 0xff].
|
||||||
*/
|
*/
|
||||||
private static int OFFSET_LONG_LIST = 0xf7;
|
private static int OFFSET_LONG_LIST = 0xf7;
|
||||||
|
|
||||||
|
|
||||||
/* ******************************************************
|
/* ******************************************************
|
||||||
* DECODING *
|
* DECODING *
|
||||||
* ******************************************************/
|
* ******************************************************/
|
||||||
|
|
||||||
private static byte decodeOneByteItem(byte[] data, int index) {
|
private static byte decodeOneByteItem(byte[] data, int index) {
|
||||||
// null item
|
// null item
|
||||||
if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM) {
|
if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM) {
|
||||||
|
@ -183,7 +183,7 @@ public class RLP {
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String decodeStringItem(byte[] data, int index) {
|
private static String decodeStringItem(byte[] data, int index) {
|
||||||
|
|
||||||
String value = null;
|
String value = null;
|
||||||
|
@ -281,7 +281,7 @@ public class RLP {
|
||||||
value = valueBytes;
|
value = valueBytes;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int nextItemLength(byte[] data, int index) {
|
private static int nextItemLength(byte[] data, int index) {
|
||||||
|
|
||||||
if (index >= data.length)
|
if (index >= data.length)
|
||||||
|
@ -517,7 +517,7 @@ public class RLP {
|
||||||
}
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int calcLengthRaw(int lengthOfLength, byte[] msgData, int index) {
|
private static int calcLengthRaw(int lengthOfLength, byte[] msgData, int index) {
|
||||||
byte pow = (byte) (lengthOfLength - 1);
|
byte pow = (byte) (lengthOfLength - 1);
|
||||||
int length = 0;
|
int length = 0;
|
||||||
|
@ -527,7 +527,7 @@ public class RLP {
|
||||||
}
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte getCommandCode(byte[] data) {
|
public static byte getCommandCode(byte[] data) {
|
||||||
byte command = 0;
|
byte command = 0;
|
||||||
int index = getFirstListElement(data, 0);
|
int index = getFirstListElement(data, 0);
|
||||||
|
@ -535,7 +535,7 @@ public class RLP {
|
||||||
command = ((int) (command & 0xFF) == OFFSET_SHORT_ITEM) ? 0 : command;
|
command = ((int) (command & 0xFF) == OFFSET_SHORT_ITEM) ? 0 : command;
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse wire byte[] message into RLP elements
|
* Parse wire byte[] message into RLP elements
|
||||||
*
|
*
|
||||||
|
@ -671,11 +671,11 @@ public class RLP {
|
||||||
throw new RuntimeException("RLP wrong encoding", e);
|
throw new RuntimeException("RLP wrong encoding", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads any RLP encoded byte-array and returns all objects as byte-array or list of byte-arrays
|
* Reads any RLP encoded byte-array and returns all objects as byte-array or list of byte-arrays
|
||||||
*
|
*
|
||||||
* @param data RLP encoded byte-array
|
* @param data RLP encoded byte-array
|
||||||
* @param pos position in the array to start reading
|
* @param pos position in the array to start reading
|
||||||
* @return DecodeResult encapsulates the decoded items as a single Object and the final read position
|
* @return DecodeResult encapsulates the decoded items as a single Object and the final read position
|
||||||
*/
|
*/
|
||||||
|
@ -709,7 +709,7 @@ public class RLP {
|
||||||
throw new RuntimeException("Only byte values between 0x00 and 0xFF are supported, but got: " + prefix);
|
throw new RuntimeException("Only byte values between 0x00 and 0xFF are supported, but got: " + prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DecodeResult decodeList(byte[] data, int pos, int prevPos, int len) {
|
private static DecodeResult decodeList(byte[] data, int pos, int prevPos, int len) {
|
||||||
List<Object> slice = new ArrayList<>();
|
List<Object> slice = new ArrayList<>();
|
||||||
for (int i = 0; i < len;) {
|
for (int i = 0; i < len;) {
|
||||||
|
@ -723,17 +723,17 @@ public class RLP {
|
||||||
}
|
}
|
||||||
return new DecodeResult(pos, slice.toArray());
|
return new DecodeResult(pos, slice.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ******************************************************
|
/* ******************************************************
|
||||||
* ENCODING *
|
* ENCODING *
|
||||||
* ******************************************************/
|
* ******************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn Object into its RLP encoded equivalent of a byte-array
|
* Turn Object into its RLP encoded equivalent of a byte-array
|
||||||
* Support for String, Integer, BigInteger and Lists of any of these types.
|
* Support for String, Integer, BigInteger and Lists of any of these types.
|
||||||
*
|
*
|
||||||
* @param input as object or List of objects
|
* @param input as object or List of objects
|
||||||
* @return byte[] RLP encoded
|
* @return byte[] RLP encoded
|
||||||
*/
|
*/
|
||||||
public static byte[] encode(Object input) {
|
public static byte[] encode(Object input) {
|
||||||
Value val = new Value(input);
|
Value val = new Value(input);
|
||||||
|
@ -749,16 +749,16 @@ public class RLP {
|
||||||
byte[] prefix = encodeLength(output.length, OFFSET_SHORT_LIST);
|
byte[] prefix = encodeLength(output.length, OFFSET_SHORT_LIST);
|
||||||
return concatenate(prefix, output);
|
return concatenate(prefix, output);
|
||||||
} else {
|
} else {
|
||||||
byte[] inputAsBytes = toBytes(input);
|
byte[] inputAsBytes = toBytes(input);
|
||||||
if (inputAsBytes.length == 1) {
|
if (inputAsBytes.length == 1) {
|
||||||
return inputAsBytes;
|
return inputAsBytes;
|
||||||
} else {
|
} else {
|
||||||
byte[] firstByte = encodeLength(inputAsBytes.length, OFFSET_SHORT_ITEM);
|
byte[] firstByte = encodeLength(inputAsBytes.length, OFFSET_SHORT_ITEM);
|
||||||
return concatenate(firstByte, inputAsBytes);
|
return concatenate(firstByte, inputAsBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Integer limitation goes up to 2^31-1 so length can never be bigger than MAX_ITEM_LENGTH */
|
/** Integer limitation goes up to 2^31-1 so length can never be bigger than MAX_ITEM_LENGTH */
|
||||||
public static byte[] encodeLength(int length, int offset) {
|
public static byte[] encodeLength(int length, int offset) {
|
||||||
if (length < SIZE_THRESHOLD) {
|
if (length < SIZE_THRESHOLD) {
|
||||||
|
@ -796,7 +796,7 @@ public class RLP {
|
||||||
(byte) (singleShort >> 0 & 0xFF) };
|
(byte) (singleShort >> 0 & 0xFF) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] encodeInt(int singleInt) {
|
public static byte[] encodeInt(int singleInt) {
|
||||||
if (singleInt <= 0xFF)
|
if (singleInt <= 0xFF)
|
||||||
return encodeByte((byte) singleInt);
|
return encodeByte((byte) singleInt);
|
||||||
|
@ -821,9 +821,9 @@ public class RLP {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] encodeBigInteger(BigInteger srcBigInteger) {
|
public static byte[] encodeBigInteger(BigInteger srcBigInteger) {
|
||||||
if(srcBigInteger == BigInteger.ZERO)
|
if(srcBigInteger == BigInteger.ZERO)
|
||||||
return encodeByte((byte)0);
|
return encodeByte((byte)0);
|
||||||
else
|
else
|
||||||
return encodeElement(asUnsignedByteArray(srcBigInteger));
|
return encodeElement(asUnsignedByteArray(srcBigInteger));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,9 +910,9 @@ public class RLP {
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Utility function to convert Objects into byte arrays
|
* Utility function to convert Objects into byte arrays
|
||||||
*/
|
*/
|
||||||
private static byte[] toBytes(Object input) {
|
private static byte[] toBytes(Object input) {
|
||||||
if (input instanceof byte[]) {
|
if (input instanceof byte[]) {
|
||||||
|
@ -921,10 +921,10 @@ public class RLP {
|
||||||
String inputString = (String) input;
|
String inputString = (String) input;
|
||||||
return inputString.getBytes();
|
return inputString.getBytes();
|
||||||
} else if(input instanceof Long) {
|
} else if(input instanceof Long) {
|
||||||
Long inputLong = (Long) input;
|
Long inputLong = (Long) input;
|
||||||
return (inputLong == 0) ? ByteUtil.EMPTY_BYTE_ARRAY : asUnsignedByteArray(BigInteger.valueOf(inputLong));
|
return (inputLong == 0) ? ByteUtil.EMPTY_BYTE_ARRAY : asUnsignedByteArray(BigInteger.valueOf(inputLong));
|
||||||
} else if(input instanceof Integer) {
|
} else if(input instanceof Integer) {
|
||||||
Integer inputInt = (Integer) input;
|
Integer inputInt = (Integer) input;
|
||||||
return (inputInt == 0) ? ByteUtil.EMPTY_BYTE_ARRAY : asUnsignedByteArray(BigInteger.valueOf(inputInt.intValue()));
|
return (inputInt == 0) ? ByteUtil.EMPTY_BYTE_ARRAY : asUnsignedByteArray(BigInteger.valueOf(inputInt.intValue()));
|
||||||
} else if(input instanceof BigInteger) {
|
} else if(input instanceof BigInteger) {
|
||||||
BigInteger inputBigInt = (BigInteger) input;
|
BigInteger inputBigInt = (BigInteger) input;
|
||||||
|
|
|
@ -2,18 +2,18 @@ package org.ethereum.util;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* www.ethereumJ.com
|
* www.ethereumJ.com
|
||||||
* @author: Roman Mandeleil
|
* @author: Roman Mandeleil
|
||||||
* Created on: 21/04/14 16:26
|
* Created on: 21/04/14 16:26
|
||||||
*/
|
*/
|
||||||
public class RLPItem implements RLPElement {
|
public class RLPItem implements RLPElement {
|
||||||
|
|
||||||
byte[] rlpData;
|
byte[] rlpData;
|
||||||
|
|
||||||
public RLPItem(byte[] rlpData) {
|
public RLPItem(byte[] rlpData) {
|
||||||
this.rlpData = rlpData;
|
this.rlpData = rlpData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRLPData() {
|
public byte[] getRLPData() {
|
||||||
if (rlpData.length == 0)
|
if (rlpData.length == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class RLPList extends ArrayList<RLPElement> implements RLPElement {
|
||||||
if (element instanceof RLPList) {
|
if (element instanceof RLPList) {
|
||||||
|
|
||||||
RLPList rlpList = (RLPList) element;
|
RLPList rlpList = (RLPList) element;
|
||||||
System.out.print("[");
|
System.out.print("[");
|
||||||
for (RLPElement singleElement : rlpList) {
|
for (RLPElement singleElement : rlpList) {
|
||||||
recursivePrint(singleElement);
|
recursivePrint(singleElement);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,11 @@ public class Utils {
|
||||||
byte[] numberBytes = Hex.decode(hexNum.substring(2));
|
byte[] numberBytes = Hex.decode(hexNum.substring(2));
|
||||||
return (new BigInteger(1, numberBytes)).toString();
|
return (new BigInteger(1, numberBytes)).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return formatted Date String: yyyy.MM.dd HH:mm:ss
|
* Return formatted Date String: yyyy.MM.dd HH:mm:ss
|
||||||
* Based on Unix's time() input in seconds
|
* Based on Unix's time() input in seconds
|
||||||
*
|
*
|
||||||
* @param timestamp seconds since start of Unix-time
|
* @param timestamp seconds since start of Unix-time
|
||||||
* @return String formatted as - yyyy.MM.dd HH:mm:ss
|
* @return String formatted as - yyyy.MM.dd HH:mm:ss
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +42,7 @@ public class Utils {
|
||||||
DateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
|
DateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
|
||||||
return formatter.format(date);
|
return formatter.format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageIcon getImageIcon(String resource) {
|
public static ImageIcon getImageIcon(String resource) {
|
||||||
URL imageURL = ClassLoader.getSystemResource(resource);
|
URL imageURL = ClassLoader.getSystemResource(resource);
|
||||||
ImageIcon image = new ImageIcon(imageURL);
|
ImageIcon image = new ImageIcon(imageURL);
|
||||||
|
@ -59,10 +59,10 @@ public class Utils {
|
||||||
}
|
}
|
||||||
return result.toString() + "·(" + "10^" + pow + ")";
|
return result.toString() + "·(" + "10^" + pow + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a hex string to address bytes and checks validity
|
* Decodes a hex string to address bytes and checks validity
|
||||||
*
|
*
|
||||||
* @param hex - a hex string of the address, e.g., 6c386a4b26f73c802f34673f7248bb118f97424a
|
* @param hex - a hex string of the address, e.g., 6c386a4b26f73c802f34673f7248bb118f97424a
|
||||||
* @return - decode and validated address byte[]
|
* @return - decode and validated address byte[]
|
||||||
*/
|
*/
|
||||||
|
@ -70,12 +70,12 @@ public class Utils {
|
||||||
byte[] addr = null;
|
byte[] addr = null;
|
||||||
try { addr = Hex.decode(hex); }
|
try { addr = Hex.decode(hex); }
|
||||||
catch(DecoderException addressIsNotValid) { return null; }
|
catch(DecoderException addressIsNotValid) { return null; }
|
||||||
|
|
||||||
if(isValidAddress(addr))
|
if(isValidAddress(addr))
|
||||||
return addr;
|
return addr;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidAddress(byte[] addr) {
|
public static boolean isValidAddress(byte[] addr) {
|
||||||
return addr != null && addr.length == 20;
|
return addr != null && addr.length == 20;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class Value {
|
||||||
this.value = obj;
|
this.value = obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *****************
|
/* *****************
|
||||||
* Convert
|
* Convert
|
||||||
* *****************/
|
* *****************/
|
||||||
|
@ -102,7 +102,7 @@ public class Value {
|
||||||
// If this wasn't a slice you probably shouldn't be using this function
|
// If this wasn't a slice you probably shouldn't be using this function
|
||||||
return new Value(null);
|
return new Value(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *****************
|
/* *****************
|
||||||
* Utility
|
* Utility
|
||||||
* *****************/
|
* *****************/
|
||||||
|
@ -114,7 +114,7 @@ public class Value {
|
||||||
public boolean cmp(Value o) {
|
public boolean cmp(Value o) {
|
||||||
return DeepEquals.deepEquals(this, o);
|
return DeepEquals.deepEquals(this, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *****************
|
/* *****************
|
||||||
* Checks
|
* Checks
|
||||||
* *****************/
|
* *****************/
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
else if (data.length <= 32)
|
else if (data.length <= 32)
|
||||||
System.arraycopy(data, 0, this.data, 32 - data.length, data.length);
|
System.arraycopy(data, 0, this.data, 32 - data.length, data.length);
|
||||||
else
|
else
|
||||||
throw new RuntimeException("Data word can't exit 32 bytes: " + data);
|
throw new RuntimeException("Data word can't exit 32 bytes: " + data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getData() {
|
public byte[] getData() {
|
||||||
|
@ -67,12 +67,12 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
public BigInteger value() {
|
public BigInteger value() {
|
||||||
return new BigInteger(1, data);
|
return new BigInteger(1, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this DataWord to an int, checking for lost information.
|
* Converts this DataWord to an int, checking for lost information.
|
||||||
* If this DataWord is out of the possible range for an int result
|
* If this DataWord is out of the possible range for an int result
|
||||||
* then an ArithmeticException is thrown.
|
* then an ArithmeticException is thrown.
|
||||||
*
|
*
|
||||||
* @return this DataWord converted to an int.
|
* @return this DataWord converted to an int.
|
||||||
* @throws ArithmeticException - if this will not fit in an int.
|
* @throws ArithmeticException - if this will not fit in an int.
|
||||||
*/
|
*/
|
||||||
|
@ -82,12 +82,12 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
return Integer.MAX_VALUE;
|
return Integer.MAX_VALUE;
|
||||||
return tmpValue.intValueExact();
|
return tmpValue.intValueExact();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this DataWord to a long, checking for lost information.
|
* Converts this DataWord to a long, checking for lost information.
|
||||||
* If this DataWord is out of the possible range for a long result
|
* If this DataWord is out of the possible range for a long result
|
||||||
* then an ArithmeticException is thrown.
|
* then an ArithmeticException is thrown.
|
||||||
*
|
*
|
||||||
* @return this DataWord converted to a long.
|
* @return this DataWord converted to a long.
|
||||||
* @throws ArithmeticException - if this will not fit in a long.
|
* @throws ArithmeticException - if this will not fit in a long.
|
||||||
*/
|
*/
|
||||||
|
@ -153,7 +153,7 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
if (this.data[i] != 0) break;
|
if (this.data[i] != 0) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bnot() {
|
public void bnot() {
|
||||||
if (this.isZero()) return;
|
if (this.isZero()) return;
|
||||||
this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value()));
|
this.data = ByteUtil.copyToArray(MAX_VALUE.subtract(this.value()));
|
||||||
|
@ -170,13 +170,13 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
}
|
}
|
||||||
this.data = result;
|
this.data = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// old add-method with BigInteger quick hack
|
// old add-method with BigInteger quick hack
|
||||||
public void add2(DataWord word) {
|
public void add2(DataWord word) {
|
||||||
BigInteger result = value().add(word.value());
|
BigInteger result = value().add(word.value());
|
||||||
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: mul can be done in more efficient way
|
// TODO: mul can be done in more efficient way
|
||||||
// TODO: with shift left shift right trick
|
// TODO: with shift left shift right trick
|
||||||
// TODO without BigInteger quick hack
|
// TODO without BigInteger quick hack
|
||||||
|
@ -212,7 +212,7 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
|
|
||||||
// TODO: improve with no BigInteger
|
// TODO: improve with no BigInteger
|
||||||
public void sub(DataWord word) {
|
public void sub(DataWord word) {
|
||||||
BigInteger result = value().subtract(word.value());
|
BigInteger result = value().subtract(word.value());
|
||||||
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,22 +244,22 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
BigInteger result = sValue().remainder(word.sValue());
|
BigInteger result = sValue().remainder(word.sValue());
|
||||||
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addmod(DataWord word1, DataWord word2) {
|
public void addmod(DataWord word1, DataWord word2) {
|
||||||
this.add(word1);
|
this.add(word1);
|
||||||
BigInteger result = this.value().mod(word2.value());
|
BigInteger result = this.value().mod(word2.value());
|
||||||
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mulmod(DataWord word1, DataWord word2) {
|
public void mulmod(DataWord word1, DataWord word2) {
|
||||||
BigInteger result = value().multiply(word1.value()).mod(word2.value());
|
BigInteger result = value().multiply(word1.value()).mod(word2.value());
|
||||||
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
this.data = ByteUtil.copyToArray(result.and(MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Hex.toHexString(data);
|
return Hex.toHexString(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String shortHex() {
|
public String shortHex() {
|
||||||
String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase();
|
String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase();
|
||||||
return "0x" + hexValue.replaceFirst("^0+(?!$)", "");
|
return "0x" + hexValue.replaceFirst("^0+(?!$)", "");
|
||||||
|
@ -280,7 +280,7 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return java.util.Arrays.hashCode(data);
|
return java.util.Arrays.hashCode(data);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package org.ethereum.vm;
|
package org.ethereum.vm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The fundamental network cost unit. Paid for exclusively by Ether, which is converted
|
* The fundamental network cost unit. Paid for exclusively by Ether, which is converted
|
||||||
* freely to and from Gas as required. Gas does not exist outside of the internal Ethereum
|
* freely to and from Gas as required. Gas does not exist outside of the internal Ethereum
|
||||||
* computation engine; its price is set by the Transaction and miners are free to
|
* computation engine; its price is set by the Transaction and miners are free to
|
||||||
* ignore Transactions whose Gas price is too low.
|
* ignore Transactions whose Gas price is too low.
|
||||||
*/
|
*/
|
||||||
public class GasCost {
|
public class GasCost {
|
||||||
|
|
|
@ -5,13 +5,13 @@ package org.ethereum.vm;
|
||||||
* This can either be a normal CALL, STATELESS call or POST call.
|
* This can either be a normal CALL, STATELESS call or POST call.
|
||||||
*/
|
*/
|
||||||
public class MessageCall {
|
public class MessageCall {
|
||||||
|
|
||||||
public enum MsgType {
|
public enum MsgType {
|
||||||
CALL,
|
CALL,
|
||||||
STATELESS,
|
STATELESS,
|
||||||
POST;
|
POST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Type of internal call. Either CALL, STATELESS or POST */
|
/** Type of internal call. Either CALL, STATELESS or POST */
|
||||||
private MsgType type;
|
private MsgType type;
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ public class MessageCall {
|
||||||
this.inDataOffs = inDataOffs;
|
this.inDataOffs = inDataOffs;
|
||||||
this.inDataSize = inDataSize;
|
this.inDataSize = inDataSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageCall(MsgType type, DataWord gas, DataWord codeAddress,
|
public MessageCall(MsgType type, DataWord gas, DataWord codeAddress,
|
||||||
DataWord endowment, DataWord inDataOffs, DataWord inDataSize,
|
DataWord endowment, DataWord inDataOffs, DataWord inDataSize,
|
||||||
DataWord outDataOffs, DataWord outDataSize) {
|
DataWord outDataOffs, DataWord outDataSize) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instruction set for the Ethereum Virtual Machine
|
* Instruction set for the Ethereum Virtual Machine
|
||||||
* See Yellow Paper: http://www.gavwood.com/Paper.pdf
|
* See Yellow Paper: http://www.gavwood.com/Paper.pdf
|
||||||
* - Appendix G. Virtual Machine Specification
|
* - Appendix G. Virtual Machine Specification
|
||||||
*/
|
*/
|
||||||
public enum OpCode {
|
public enum OpCode {
|
||||||
|
@ -29,10 +29,10 @@ public enum OpCode {
|
||||||
MOD(0x06, 2),
|
MOD(0x06, 2),
|
||||||
/** (0x07) Signed modulo remainder operation*/
|
/** (0x07) Signed modulo remainder operation*/
|
||||||
SMOD(0x07, 2),
|
SMOD(0x07, 2),
|
||||||
/** (0x08) Addition combined with modulo
|
/** (0x08) Addition combined with modulo
|
||||||
* remainder operation */
|
* remainder operation */
|
||||||
ADDMOD(0x08, 3),
|
ADDMOD(0x08, 3),
|
||||||
/** (0x09) Multiplication combined with modulo
|
/** (0x09) Multiplication combined with modulo
|
||||||
* remainder operation */
|
* remainder operation */
|
||||||
MULMOD(0x09, 3),
|
MULMOD(0x09, 3),
|
||||||
/** (0x0a) Exponential operation */
|
/** (0x0a) Exponential operation */
|
||||||
|
@ -72,7 +72,7 @@ public enum OpCode {
|
||||||
|
|
||||||
/* Environmental Information */
|
/* Environmental Information */
|
||||||
|
|
||||||
/** (0x30) Get address of currently
|
/** (0x30) Get address of currently
|
||||||
* executing account */
|
* executing account */
|
||||||
ADDRESS(0x30, 0),
|
ADDRESS(0x30, 0),
|
||||||
/** (0x31) Get balance of the given account */
|
/** (0x31) Get balance of the given account */
|
||||||
|
@ -81,38 +81,38 @@ public enum OpCode {
|
||||||
ORIGIN(0x32, 0),
|
ORIGIN(0x32, 0),
|
||||||
/** (0x33) Get caller address */
|
/** (0x33) Get caller address */
|
||||||
CALLER(0x33, 0),
|
CALLER(0x33, 0),
|
||||||
/** (0x34) Get deposited value by the
|
/** (0x34) Get deposited value by the
|
||||||
* instruction/transaction responsible
|
* instruction/transaction responsible
|
||||||
* for this execution */
|
* for this execution */
|
||||||
CALLVALUE(0x34, 0),
|
CALLVALUE(0x34, 0),
|
||||||
/** (0x35) Get input data of current
|
/** (0x35) Get input data of current
|
||||||
* environment */
|
* environment */
|
||||||
CALLDATALOAD(0x35, 1),
|
CALLDATALOAD(0x35, 1),
|
||||||
/** (0x36) Get size of input data in current
|
/** (0x36) Get size of input data in current
|
||||||
* environment */
|
* environment */
|
||||||
CALLDATASIZE(0x36, 0),
|
CALLDATASIZE(0x36, 0),
|
||||||
/** (0x37) Copy input data in current
|
/** (0x37) Copy input data in current
|
||||||
* environment to memory */
|
* environment to memory */
|
||||||
CALLDATACOPY(0x37, 3),
|
CALLDATACOPY(0x37, 3),
|
||||||
/** (0x38) Get size of code running in
|
/** (0x38) Get size of code running in
|
||||||
* current environment */
|
* current environment */
|
||||||
CODESIZE(0x38, 0),
|
CODESIZE(0x38, 0),
|
||||||
/** (0x39) Copy code running in current
|
/** (0x39) Copy code running in current
|
||||||
* environment to memory */
|
* environment to memory */
|
||||||
CODECOPY(0x39, 3), // [len code_start mem_start CODECOPY]
|
CODECOPY(0x39, 3), // [len code_start mem_start CODECOPY]
|
||||||
/** (0x3a) Get price of gas in current
|
/** (0x3a) Get price of gas in current
|
||||||
* environment */
|
* environment */
|
||||||
GASPRICE(0x3a, 0),
|
GASPRICE(0x3a, 0),
|
||||||
/** (0x3b) Get size of code running in
|
/** (0x3b) Get size of code running in
|
||||||
* current environment with given offset */
|
* current environment with given offset */
|
||||||
EXTCODESIZE(0x3b, 1),
|
EXTCODESIZE(0x3b, 1),
|
||||||
/** (0x3c) Copy code running in current
|
/** (0x3c) Copy code running in current
|
||||||
* environment to memory with given offset */
|
* environment to memory with given offset */
|
||||||
EXTCODECOPY(0x3c, 4),
|
EXTCODECOPY(0x3c, 4),
|
||||||
|
|
||||||
/* Block Information */
|
/* Block Information */
|
||||||
|
|
||||||
/** (0x40) Get hash of most recent
|
/** (0x40) Get hash of most recent
|
||||||
* complete block */
|
* complete block */
|
||||||
PREVHASH(0x40, 0),
|
PREVHASH(0x40, 0),
|
||||||
/** (0x41) Get the block’s coinbase address */
|
/** (0x41) Get the block’s coinbase address */
|
||||||
|
@ -142,7 +142,7 @@ public enum OpCode {
|
||||||
SSTORE(0x55, 2),
|
SSTORE(0x55, 2),
|
||||||
/** (0x56) Alter the program counter */
|
/** (0x56) Alter the program counter */
|
||||||
JUMP(0x56, 1),
|
JUMP(0x56, 1),
|
||||||
/** (0x57) Conditionally alter the program
|
/** (0x57) Conditionally alter the program
|
||||||
* counter */
|
* counter */
|
||||||
JUMPI(0x57, 2),
|
JUMPI(0x57, 2),
|
||||||
/** (0x58) Get the program counter */
|
/** (0x58) Get the program counter */
|
||||||
|
@ -218,7 +218,7 @@ public enum OpCode {
|
||||||
PUSH30(0x7d, 0),
|
PUSH30(0x7d, 0),
|
||||||
/** (0x7e) Place 31-byte item on stack */
|
/** (0x7e) Place 31-byte item on stack */
|
||||||
PUSH31(0x7e, 0),
|
PUSH31(0x7e, 0),
|
||||||
/** (0x7f) Place 32-byte (full word)
|
/** (0x7f) Place 32-byte (full word)
|
||||||
* item on stack */
|
* item on stack */
|
||||||
PUSH32(0x7f, 0),
|
PUSH32(0x7f, 0),
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ public enum OpCode {
|
||||||
|
|
||||||
private byte opcode;
|
private byte opcode;
|
||||||
private int require;
|
private int require;
|
||||||
|
|
||||||
private static final Map<Byte, OpCode> intToTypeMap = new HashMap<>();
|
private static final Map<Byte, OpCode> intToTypeMap = new HashMap<>();
|
||||||
private static final Map<String, Byte> stringToByteMap = new HashMap<>();
|
private static final Map<String, Byte> stringToByteMap = new HashMap<>();
|
||||||
|
|
||||||
|
@ -326,19 +326,19 @@ public enum OpCode {
|
||||||
stringToByteMap.put(type.name(), type.opcode);
|
stringToByteMap.put(type.name(), type.opcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private OpCode(int op, int require) {
|
private OpCode(int op, int require) {
|
||||||
this.opcode = (byte) op;
|
this.opcode = (byte) op;
|
||||||
this.require = require;
|
this.require = require;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte val() {
|
public byte val() {
|
||||||
return opcode;
|
return opcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mininum amount of items required on the stack for this operation
|
* Returns the mininum amount of items required on the stack for this operation
|
||||||
*
|
*
|
||||||
* @return minimum amount of expected items on the stack
|
* @return minimum amount of expected items on the stack
|
||||||
*/
|
*/
|
||||||
public int require() {
|
public int require() {
|
||||||
|
|
|
@ -32,7 +32,7 @@ import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
||||||
* Created on: 01/06/2014 10:45
|
* Created on: 01/06/2014 10:45
|
||||||
*/
|
*/
|
||||||
public class Program {
|
public class Program {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("VM");
|
private static final Logger logger = LoggerFactory.getLogger("VM");
|
||||||
private static final Logger gasLogger = LoggerFactory.getLogger("gas");
|
private static final Logger gasLogger = LoggerFactory.getLogger("gas");
|
||||||
|
|
||||||
|
@ -57,10 +57,10 @@ public class Program {
|
||||||
ProgramInvoke invokeData;
|
ProgramInvoke invokeData;
|
||||||
|
|
||||||
public Program(byte[] ops, ProgramInvoke invokeData) {
|
public Program(byte[] ops, ProgramInvoke invokeData) {
|
||||||
|
|
||||||
if (ops == null) ops = EMPTY_BYTE_ARRAY;
|
if (ops == null) ops = EMPTY_BYTE_ARRAY;
|
||||||
this.ops = ops;
|
this.ops = ops;
|
||||||
|
|
||||||
if (invokeData != null) {
|
if (invokeData != null) {
|
||||||
this.invokeData = invokeData;
|
this.invokeData = invokeData;
|
||||||
this.programAddress = invokeData.getOwnerAddress();
|
this.programAddress = invokeData.getOwnerAddress();
|
||||||
|
@ -88,7 +88,7 @@ public class Program {
|
||||||
public void setLastOp(byte op) {
|
public void setLastOp(byte op) {
|
||||||
this.lastOp = op;
|
this.lastOp = op;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be set only after the OP is fully executed
|
* Should be set only after the OP is fully executed
|
||||||
* @param op
|
* @param op
|
||||||
|
@ -96,7 +96,7 @@ public class Program {
|
||||||
public void setPreviouslyExecutedOp(byte op) {
|
public void setPreviouslyExecutedOp(byte op) {
|
||||||
this.previouslyExecutedOp = op;
|
this.previouslyExecutedOp = op;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the last fully executed OP
|
* returns the last fully executed OP
|
||||||
* @return
|
* @return
|
||||||
|
@ -104,7 +104,7 @@ public class Program {
|
||||||
public byte getPreviouslyExecutedOp() {
|
public byte getPreviouslyExecutedOp() {
|
||||||
return this.previouslyExecutedOp;
|
return this.previouslyExecutedOp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stackPush(byte[] data) {
|
public void stackPush(byte[] data) {
|
||||||
DataWord stackWord = new DataWord(data);
|
DataWord stackWord = new DataWord(data);
|
||||||
stack.push(stackWord);
|
stack.push(stackWord);
|
||||||
|
@ -123,7 +123,7 @@ public class Program {
|
||||||
public void stackPush(DataWord stackWord) {
|
public void stackPush(DataWord stackWord) {
|
||||||
stack.push(stackWord);
|
stack.push(stackWord);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stack<DataWord> getStack() {
|
public Stack<DataWord> getStack() {
|
||||||
return this.stack;
|
return this.stack;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ public class Program {
|
||||||
|
|
||||||
public void setPC(int pc) {
|
public void setPC(int pc) {
|
||||||
this.pc = pc;
|
this.pc = pc;
|
||||||
|
|
||||||
if (this.pc >= ops.length)
|
if (this.pc >= ops.length)
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
@ -175,11 +175,11 @@ public class Program {
|
||||||
public DataWord stackPop() {
|
public DataWord stackPop() {
|
||||||
return stack.pop();
|
return stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that the stack is at least <code>stackSize</code>
|
* Verifies that the stack is at least <code>stackSize</code>
|
||||||
* @param stackSize int
|
* @param stackSize int
|
||||||
* @throws StackTooSmallException If the stack is
|
* @throws StackTooSmallException If the stack is
|
||||||
* smaller than <code>stackSize</code>
|
* smaller than <code>stackSize</code>
|
||||||
*/
|
*/
|
||||||
public void stackRequire(int stackSize) {
|
public void stackRequire(int stackSize) {
|
||||||
|
@ -203,7 +203,7 @@ public class Program {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates a piece of memory and stores value at given offset address
|
* Allocates a piece of memory and stores value at given offset address
|
||||||
*
|
*
|
||||||
* @param addr is the offset address
|
* @param addr is the offset address
|
||||||
* @param allocSize size of memory needed to write
|
* @param allocSize size of memory needed to write
|
||||||
* @param value the data to write to memory
|
* @param value the data to write to memory
|
||||||
|
@ -213,11 +213,11 @@ public class Program {
|
||||||
allocateMemory(addr, allocSize);
|
allocateMemory(addr, allocSize);
|
||||||
System.arraycopy(value, 0, memory.array(), addr, value.length);
|
System.arraycopy(value, 0, memory.array(), addr, value.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataWord memoryLoad(DataWord addr) {
|
public DataWord memoryLoad(DataWord addr) {
|
||||||
return memoryLoad(addr.intValue());
|
return memoryLoad(addr.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataWord memoryLoad(int address) {
|
public DataWord memoryLoad(int address) {
|
||||||
|
|
||||||
allocateMemory(address, DataWord.ZERO.getData().length);
|
allocateMemory(address, DataWord.ZERO.getData().length);
|
||||||
|
@ -255,14 +255,14 @@ public class Program {
|
||||||
/**
|
/**
|
||||||
* Allocates extra memory in the program for
|
* Allocates extra memory in the program for
|
||||||
* a specified size, calculated from a given offset
|
* a specified size, calculated from a given offset
|
||||||
*
|
*
|
||||||
* @param offset the memory address offset
|
* @param offset the memory address offset
|
||||||
* @param size the number of bytes to allocate
|
* @param size the number of bytes to allocate
|
||||||
*/
|
*/
|
||||||
public void allocateMemory(int offset, int size) {
|
public void allocateMemory(int offset, int size) {
|
||||||
|
|
||||||
int memSize = memory != null ? memory.limit() : 0;
|
int memSize = memory != null ? memory.limit() : 0;
|
||||||
double newMemSize = Math.max(memSize, size != 0 ?
|
double newMemSize = Math.max(memSize, size != 0 ?
|
||||||
Math.ceil((double) (offset + size) / 32) * 32 : 0);
|
Math.ceil((double) (offset + size) / 32) * 32 : 0);
|
||||||
ByteBuffer tmpMem = ByteBuffer.allocate((int)newMemSize);
|
ByteBuffer tmpMem = ByteBuffer.allocate((int)newMemSize);
|
||||||
if (memory != null)
|
if (memory != null)
|
||||||
|
@ -295,7 +295,7 @@ public class Program {
|
||||||
byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();
|
byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();
|
||||||
if (logger.isInfoEnabled())
|
if (logger.isInfoEnabled())
|
||||||
logger.info("creating a new contract inside contract run: [{}]", Hex.toHexString(senderAddress));
|
logger.info("creating a new contract inside contract run: [{}]", Hex.toHexString(senderAddress));
|
||||||
|
|
||||||
// actual gas subtract
|
// actual gas subtract
|
||||||
DataWord gasLimit = this.getGas();
|
DataWord gasLimit = this.getGas();
|
||||||
this.spendGas(gasLimit.longValue(), "internal call");
|
this.spendGas(gasLimit.longValue(), "internal call");
|
||||||
|
@ -332,9 +332,9 @@ public class Program {
|
||||||
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);
|
||||||
|
|
||||||
ProgramResult result = null;
|
ProgramResult result = null;
|
||||||
|
|
||||||
if (programCode != null && programCode.length != 0) {
|
if (programCode != null && programCode.length != 0) {
|
||||||
VM vm = new VM();
|
VM vm = new VM();
|
||||||
Program program = new Program(programCode, programInvoke);
|
Program program = new Program(programCode, programInvoke);
|
||||||
|
@ -343,8 +343,8 @@ public class Program {
|
||||||
this.result.addDeleteAccounts(result.getDeleteAccounts());
|
this.result.addDeleteAccounts(result.getDeleteAccounts());
|
||||||
this.result.addLogInfos(result.getLogInfoList());
|
this.result.addLogInfos(result.getLogInfoList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != null &&
|
if (result != null &&
|
||||||
result.getException() != null &&
|
result.getException() != null &&
|
||||||
result.getException() instanceof Program.OutOfGasException) {
|
result.getException() instanceof Program.OutOfGasException) {
|
||||||
logger.debug("contract run halted by Exception: contract: [{}], exception: [{}]",
|
logger.debug("contract run halted by Exception: contract: [{}], exception: [{}]",
|
||||||
|
@ -374,7 +374,7 @@ public class Program {
|
||||||
|
|
||||||
// IN SUCCESS PUSH THE ADDRESS INTO THE STACK
|
// IN SUCCESS PUSH THE ADDRESS INTO THE STACK
|
||||||
stackPush(new DataWord(newAddress));
|
stackPush(new DataWord(newAddress));
|
||||||
|
|
||||||
// 5. REFUND THE REMAIN GAS
|
// 5. REFUND THE REMAIN GAS
|
||||||
|
|
||||||
long refundGas = gasLimit.longValue() - result.getGasUsed();
|
long refundGas = gasLimit.longValue() - result.getGasUsed();
|
||||||
|
@ -390,14 +390,14 @@ public class Program {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* That method is for internal code invocations
|
* That method is for internal code invocations
|
||||||
*
|
*
|
||||||
* - Normal calls invoke a specified contract which updates itself
|
* - Normal calls invoke a specified contract which updates itself
|
||||||
* - Stateless calls invoke code from another contract, within the context of the caller
|
* - Stateless calls invoke code from another contract, within the context of the caller
|
||||||
*
|
*
|
||||||
* @param msg is the message call object
|
* @param msg is the message call object
|
||||||
*/
|
*/
|
||||||
public void callToAddress(MessageCall msg) {
|
public void callToAddress(MessageCall msg) {
|
||||||
|
|
||||||
byte[] data = memoryChunk(msg.getInDataOffs(), msg.getInDataSize()).array();
|
byte[] data = memoryChunk(msg.getInDataOffs(), msg.getInDataSize()).array();
|
||||||
|
|
||||||
// FETCH THE SAVED STORAGE
|
// FETCH THE SAVED STORAGE
|
||||||
|
@ -420,7 +420,7 @@ public class Program {
|
||||||
Hex.toHexString(senderAddress), Hex.toHexString(codeAddress));
|
Hex.toHexString(senderAddress), Hex.toHexString(codeAddress));
|
||||||
throw new OutOfGasException();
|
throw new OutOfGasException();
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInteger endowment = msg.getEndowment().value();
|
BigInteger endowment = msg.getEndowment().value();
|
||||||
BigInteger senderBalance = result.getRepository().getBalance(senderAddress);
|
BigInteger senderBalance = result.getRepository().getBalance(senderAddress);
|
||||||
if (senderBalance.compareTo(endowment) < 0) {
|
if (senderBalance.compareTo(endowment) < 0) {
|
||||||
|
@ -429,22 +429,22 @@ public class Program {
|
||||||
}
|
}
|
||||||
result.getRepository().addBalance(senderAddress, endowment.negate());
|
result.getRepository().addBalance(senderAddress, endowment.negate());
|
||||||
BigInteger contextBalance = result.getRepository().addBalance(contextAddress, endowment);
|
BigInteger contextBalance = result.getRepository().addBalance(contextAddress, endowment);
|
||||||
|
|
||||||
if (invokeData.byTestingSuite()) {
|
if (invokeData.byTestingSuite()) {
|
||||||
// This keeps track of the calls created for a test
|
// This keeps track of the calls created for a test
|
||||||
this.getResult().addCallCreate(data, contextAddress,
|
this.getResult().addCallCreate(data, contextAddress,
|
||||||
msg.getGas().getNoLeadZeroesData(),
|
msg.getGas().getNoLeadZeroesData(),
|
||||||
msg.getEndowment().getNoLeadZeroesData());
|
msg.getEndowment().getNoLeadZeroesData());
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual gas subtract
|
// actual gas subtract
|
||||||
this.spendGas(msg.getGas().longValue(), "internal call");
|
this.spendGas(msg.getGas().longValue(), "internal call");
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
ProgramResult result = null;
|
ProgramResult result = null;
|
||||||
|
|
||||||
if (programCode != null && programCode.length != 0) {
|
if (programCode != null && programCode.length != 0) {
|
||||||
|
@ -456,7 +456,7 @@ public class Program {
|
||||||
this.result.addDeleteAccounts(result.getDeleteAccounts());
|
this.result.addDeleteAccounts(result.getDeleteAccounts());
|
||||||
this.result.addLogInfos(result.getLogInfoList());
|
this.result.addLogInfos(result.getLogInfoList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != null &&
|
if (result != null &&
|
||||||
result.getException() != null &&
|
result.getException() != null &&
|
||||||
result.getException() instanceof Program.OutOfGasException) {
|
result.getException() instanceof Program.OutOfGasException) {
|
||||||
|
@ -483,11 +483,11 @@ public class Program {
|
||||||
this.memorySave(offset, allocSize, buffer.array());
|
this.memorySave(offset, allocSize, buffer.array());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
|
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
|
||||||
trackRepository.commit();
|
trackRepository.commit();
|
||||||
stackPushOne();
|
stackPushOne();
|
||||||
|
|
||||||
// 5. REFUND THE REMAIN GAS
|
// 5. REFUND THE REMAIN GAS
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
BigInteger refundGas = msg.getGas().value().subtract(BigInteger.valueOf(result.getGasUsed()));
|
BigInteger refundGas = msg.getGas().value().subtract(BigInteger.valueOf(result.getGasUsed()));
|
||||||
|
@ -511,7 +511,7 @@ public class Program {
|
||||||
throw new OutOfGasException();
|
throw new OutOfGasException();
|
||||||
result.spendGas(gasValue);
|
result.spendGas(gasValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spendAllGas() {
|
public void spendAllGas() {
|
||||||
spendGas(invokeData.getGas().longValue() - result.getGasUsed(), "Spending all remaining");
|
spendGas(invokeData.getGas().longValue() - result.getGasUsed(), "Spending all remaining");
|
||||||
}
|
}
|
||||||
|
@ -534,11 +534,11 @@ public class Program {
|
||||||
DataWord valWord = new DataWord(val);
|
DataWord valWord = new DataWord(val);
|
||||||
result.getRepository().addStorageRow(this.programAddress.getLast20Bytes(), keyWord, valWord);
|
result.getRepository().addStorageRow(this.programAddress.getLast20Bytes(), keyWord, valWord);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getCode() {
|
public byte[] getCode() {
|
||||||
return ops;
|
return ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getCodeAt(DataWord address) {
|
public byte[] getCodeAt(DataWord address) {
|
||||||
|
|
||||||
byte[] code = invokeData.getRepository().getCode(address.getLast20Bytes());
|
byte[] code = invokeData.getRepository().getCode(address.getLast20Bytes());
|
||||||
|
@ -637,7 +637,7 @@ public class Program {
|
||||||
public void setRuntimeFailure(RuntimeException e) {
|
public void setRuntimeFailure(RuntimeException e) {
|
||||||
result.setException(e);
|
result.setException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String memoryToString() {
|
public String memoryToString() {
|
||||||
StringBuilder memoryData = new StringBuilder();
|
StringBuilder memoryData = new StringBuilder();
|
||||||
StringBuilder firstLine = new StringBuilder();
|
StringBuilder firstLine = new StringBuilder();
|
||||||
|
@ -645,7 +645,7 @@ public class Program {
|
||||||
for (int i = 0; memory != null && i < memory.limit(); ++i) {
|
for (int i = 0; memory != null && i < memory.limit(); ++i) {
|
||||||
|
|
||||||
byte value = memory.get(i);
|
byte value = memory.get(i);
|
||||||
// Check if value is ASCII
|
// Check if value is ASCII
|
||||||
String character = ((byte) 0x20 <= value && value <= (byte) 0x7e) ? new String(new byte[] { value }) : "?";
|
String character = ((byte) 0x20 <= value && value <= (byte) 0x7e) ? new String(new byte[] { value }) : "?";
|
||||||
firstLine.append(character).append("");
|
firstLine.append(character).append("");
|
||||||
secondLine.append(ByteUtil.oneByteToHexString(value)).append(" ");
|
secondLine.append(ByteUtil.oneByteToHexString(value)).append(" ");
|
||||||
|
@ -820,29 +820,29 @@ public class Program {
|
||||||
public static String stringify(byte[] code, int index, String result) {
|
public static String stringify(byte[] code, int index, String result) {
|
||||||
if(code == null || code.length == 0)
|
if(code == null || code.length == 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
OpCode op = OpCode.code(code[index]);
|
OpCode op = OpCode.code(code[index]);
|
||||||
byte[] continuedCode = null;
|
byte[] continuedCode = null;
|
||||||
|
|
||||||
switch(op) {
|
switch(op) {
|
||||||
case PUSH1: case PUSH2: case PUSH3: case PUSH4: case PUSH5: case PUSH6: case PUSH7: case PUSH8:
|
case PUSH1: case PUSH2: case PUSH3: case PUSH4: case PUSH5: case PUSH6: case PUSH7: case PUSH8:
|
||||||
case PUSH9: case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
|
case PUSH9: case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
|
||||||
case PUSH17: case PUSH18: case PUSH19: case PUSH20: case PUSH21: case PUSH22: case PUSH23: case PUSH24:
|
case PUSH17: case PUSH18: case PUSH19: case PUSH20: case PUSH21: case PUSH22: case PUSH23: case PUSH24:
|
||||||
case PUSH25: case PUSH26: case PUSH27: case PUSH28: case PUSH29: case PUSH30: case PUSH31: case PUSH32:
|
case PUSH25: case PUSH26: case PUSH27: case PUSH28: case PUSH29: case PUSH30: case PUSH31: case PUSH32:
|
||||||
result += ' ' + op.name() + ' ';
|
result += ' ' + op.name() + ' ';
|
||||||
|
|
||||||
int nPush = op.val() - OpCode.PUSH1.val() + 1;
|
int nPush = op.val() - OpCode.PUSH1.val() + 1;
|
||||||
byte[] data = Arrays.copyOfRange(code, index+1, index + nPush + 1);
|
byte[] data = Arrays.copyOfRange(code, index+1, index + nPush + 1);
|
||||||
result += new BigInteger(1, data).toString() + ' ';
|
result += new BigInteger(1, data).toString() + ' ';
|
||||||
|
|
||||||
continuedCode = Arrays.copyOfRange(code, index + nPush + 1, code.length);
|
continuedCode = Arrays.copyOfRange(code, index + nPush + 1, code.length);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
result += ' ' + op.name();
|
result += ' ' + op.name();
|
||||||
continuedCode = Arrays.copyOfRange(code, index + 1, code.length);
|
continuedCode = Arrays.copyOfRange(code, index + 1, code.length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return stringify(continuedCode, 0, result);
|
return stringify(continuedCode, 0, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,13 +856,13 @@ public class Program {
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class OutOfGasException extends RuntimeException {}
|
public class OutOfGasException extends RuntimeException {}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class IllegalOperationException extends RuntimeException {}
|
public class IllegalOperationException extends RuntimeException {}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class BadJumpDestinationException extends RuntimeException {}
|
public class BadJumpDestinationException extends RuntimeException {}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class StackTooSmallException extends RuntimeException {
|
public class StackTooSmallException extends RuntimeException {
|
||||||
public StackTooSmallException(String message) {
|
public StackTooSmallException(String message) {
|
||||||
|
|
|
@ -28,14 +28,14 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
|
||||||
@Autowired
|
@Autowired
|
||||||
private Blockchain blockchain;
|
private Blockchain blockchain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This attribute defines the number of recursive calls allowed in the EVM
|
* This attribute defines the number of recursive calls allowed in the EVM
|
||||||
* Note: For the JVM to reach this level without a StackOverflow exception,
|
* Note: For the JVM to reach this level without a StackOverflow exception,
|
||||||
* ethereumj may need to be started with a JVM argument to increase
|
* ethereumj may need to be started with a JVM argument to increase
|
||||||
* the stack size. For example: -Xss10m
|
* the stack size. For example: -Xss10m
|
||||||
*/
|
*/
|
||||||
private static final int MAX_DEPTH = 1024;
|
private static final int MAX_DEPTH = 1024;
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -188,7 +188,7 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
|
||||||
Hex.toHexString(difficulty.getNoLeadZeroesData()),
|
Hex.toHexString(difficulty.getNoLeadZeroesData()),
|
||||||
gasLimit.longValue());
|
gasLimit.longValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (program.invokeData.getCallDeep() >= MAX_DEPTH)
|
if (program.invokeData.getCallDeep() >= MAX_DEPTH)
|
||||||
throw program.new OutOfGasException();
|
throw program.new OutOfGasException();
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
byte[] msgData;
|
byte[] msgData;
|
||||||
|
|
||||||
/*** BLOCK env ***/
|
/*** BLOCK env ***/
|
||||||
private DataWord prevHash, coinbase, timestamp,
|
private DataWord prevHash, coinbase, timestamp,
|
||||||
number, difficulty, gaslimit;
|
number, difficulty, gaslimit;
|
||||||
|
|
||||||
Map<DataWord, DataWord> storage;
|
Map<DataWord, DataWord> storage;
|
||||||
|
@ -151,7 +151,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
||||||
return new DataWord();
|
return new DataWord();
|
||||||
if (index + size > msgData.length)
|
if (index + size > msgData.length)
|
||||||
size = msgData.length - index;
|
size = msgData.length - index;
|
||||||
|
|
||||||
byte[] data = new byte[32];
|
byte[] data = new byte[32];
|
||||||
System.arraycopy(msgData, index, data, 0, size);
|
System.arraycopy(msgData, index, data, 0, size);
|
||||||
return new DataWord(data);
|
return new DataWord(data);
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
|
|
||||||
// default for most tests. This can be overwritten by the test
|
// default for most tests. This can be overwritten by the test
|
||||||
private long gasLimit = 1000000;
|
private long gasLimit = 1000000;
|
||||||
|
|
||||||
public ProgramInvokeMockImpl(byte[] msgDataRaw) {
|
public ProgramInvokeMockImpl(byte[] msgDataRaw) {
|
||||||
this();
|
this();
|
||||||
this.msgData = msgDataRaw;
|
this.msgData = msgDataRaw;
|
||||||
|
@ -31,9 +31,9 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
public ProgramInvokeMockImpl() {
|
public ProgramInvokeMockImpl() {
|
||||||
this.repository = new RepositoryImpl("detailsMoc", "stateMoc");
|
this.repository = new RepositoryImpl("detailsMoc", "stateMoc");
|
||||||
this.repository.createAccount(ownerAddress);
|
this.repository.createAccount(ownerAddress);
|
||||||
|
|
||||||
this.repository.createAccount(contractAddress);
|
this.repository.createAccount(contractAddress);
|
||||||
this.repository.saveCode(contractAddress,
|
this.repository.saveCode(contractAddress,
|
||||||
Hex.decode("385E60076000396000605f556014600054601e60"
|
Hex.decode("385E60076000396000605f556014600054601e60"
|
||||||
+ "205463abcddcba6040545b51602001600a525451"
|
+ "205463abcddcba6040545b51602001600a525451"
|
||||||
+ "6040016014525451606001601e52545160800160"
|
+ "6040016014525451606001601e52545160800160"
|
||||||
|
@ -84,7 +84,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
|
|
||||||
/* GAS op */
|
/* GAS op */
|
||||||
public DataWord getGas() {
|
public DataWord getGas() {
|
||||||
|
|
||||||
return new DataWord(gasLimit);
|
return new DataWord(gasLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,10 +190,10 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
||||||
public DataWord getGaslimit() {
|
public DataWord getGaslimit() {
|
||||||
return new DataWord(gasLimit);
|
return new DataWord(gasLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGasLimit(long gasLimit) {
|
public void setGasLimit(long gasLimit) {
|
||||||
this.gasLimit = gasLimit;
|
this.gasLimit = gasLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOwnerAddress(byte[] ownerAddress) {
|
public void setOwnerAddress(byte[] ownerAddress) {
|
||||||
this.ownerAddress = ownerAddress;
|
this.ownerAddress = ownerAddress;
|
||||||
|
|
|
@ -26,35 +26,35 @@ import static org.ethereum.vm.OpCode.PUSH1;
|
||||||
/**
|
/**
|
||||||
* The Ethereum Virtual Machine (EVM) is responsible for initialization
|
* The Ethereum Virtual Machine (EVM) is responsible for initialization
|
||||||
* and executing a transaction on a contract.
|
* and executing a transaction on a contract.
|
||||||
*
|
*
|
||||||
* It is a quasi-Turing-complete machine; the quasi qualification
|
* It is a quasi-Turing-complete machine; the quasi qualification
|
||||||
* comes from the fact that the computation is intrinsically bounded
|
* comes from the fact that the computation is intrinsically bounded
|
||||||
* through a parameter, gas, which limits the total amount of computation done.
|
* through a parameter, gas, which limits the total amount of computation done.
|
||||||
*
|
*
|
||||||
* The EVM is a simple stack-based architecture. The word size of the machine
|
* The EVM is a simple stack-based architecture. The word size of the machine
|
||||||
* (and thus size of stack item) is 256-bit. This was chosen to facilitate
|
* (and thus size of stack item) is 256-bit. This was chosen to facilitate
|
||||||
* the SHA3-256 hash scheme and elliptic-curve computations. The memory model
|
* the SHA3-256 hash scheme and elliptic-curve computations. The memory model
|
||||||
* is a simple word-addressed byte array. The stack has an unlimited size.
|
* is a simple word-addressed byte array. The stack has an unlimited size.
|
||||||
* The machine also has an independent storage model; this is similar in concept
|
* The machine also has an independent storage model; this is similar in concept
|
||||||
* to the memory but rather than a byte array, it is a word-addressable word array.
|
* to the memory but rather than a byte array, it is a word-addressable word array.
|
||||||
*
|
*
|
||||||
* Unlike memory, which is volatile, storage is non volatile and is
|
* Unlike memory, which is volatile, storage is non volatile and is
|
||||||
* maintained as part of the system state. All locations in both storage
|
* maintained as part of the system state. All locations in both storage
|
||||||
* and memory are well-defined initially as zero.
|
* and memory are well-defined initially as zero.
|
||||||
*
|
*
|
||||||
* The machine does not follow the standard von Neumann architecture.
|
* The machine does not follow the standard von Neumann architecture.
|
||||||
* Rather than storing program code in generally-accessible memory or storage,
|
* Rather than storing program code in generally-accessible memory or storage,
|
||||||
* it is stored separately in a virtual ROM interactable only though
|
* it is stored separately in a virtual ROM interactable only though
|
||||||
* a specialised instruction.
|
* a specialised instruction.
|
||||||
*
|
*
|
||||||
* The machine can have exceptional execution for several reasons,
|
* The machine can have exceptional execution for several reasons,
|
||||||
* including stack underflows and invalid instructions. These unambiguously
|
* including stack underflows and invalid instructions. These unambiguously
|
||||||
* and validly result in immediate halting of the machine with all state changes
|
* and validly result in immediate halting of the machine with all state changes
|
||||||
* left intact. The one piece of exceptional execution that does not leave
|
* left intact. The one piece of exceptional execution that does not leave
|
||||||
* state changes intact is the out-of-gas (OOG) exception.
|
* state changes intact is the out-of-gas (OOG) exception.
|
||||||
*
|
*
|
||||||
* Here, the machine halts immediately and reports the issue to
|
* Here, the machine halts immediately and reports the issue to
|
||||||
* the execution agent (either the transaction processor or, recursively,
|
* the execution agent (either the transaction processor or, recursively,
|
||||||
* the spawning execution environment) and which will deal with it separately.
|
* the spawning execution environment) and which will deal with it separately.
|
||||||
*
|
*
|
||||||
* www.ethereumJ.com
|
* www.ethereumJ.com
|
||||||
|
@ -62,22 +62,22 @@ import static org.ethereum.vm.OpCode.PUSH1;
|
||||||
* Created on: 01/06/2014 10:44
|
* Created on: 01/06/2014 10:44
|
||||||
*/
|
*/
|
||||||
public class VM {
|
public class VM {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("VM");
|
private static final Logger logger = LoggerFactory.getLogger("VM");
|
||||||
private static final Logger dumpLogger = LoggerFactory.getLogger("dump");
|
private static final Logger dumpLogger = LoggerFactory.getLogger("dump");
|
||||||
private static BigInteger _32_ = BigInteger.valueOf(32);
|
private static BigInteger _32_ = BigInteger.valueOf(32);
|
||||||
private static String logString = "[{}]\t Op: [{}] Gas: [{}] Deep: [{}] Hint: [{}]";
|
private static String logString = "[{}]\t Op: [{}] Gas: [{}] Deep: [{}] Hint: [{}]";
|
||||||
|
|
||||||
private static BigInteger MAX_GAS = BigInteger.valueOf(Long.MAX_VALUE);
|
private static BigInteger MAX_GAS = BigInteger.valueOf(Long.MAX_VALUE);
|
||||||
|
|
||||||
/* Keeps track of the number of steps performed in this VM */
|
/* Keeps track of the number of steps performed in this VM */
|
||||||
private int vmCounter = 0;
|
private int vmCounter = 0;
|
||||||
|
|
||||||
public void step(Program program) {
|
public void step(Program program) {
|
||||||
|
|
||||||
if (CONFIG.vmTrace())
|
if (CONFIG.vmTrace())
|
||||||
program.saveOpTrace();
|
program.saveOpTrace();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
OpCode op = OpCode.code(program.getCurrentOp());
|
OpCode op = OpCode.code(program.getCurrentOp());
|
||||||
if (op == null)
|
if (op == null)
|
||||||
|
@ -96,7 +96,7 @@ public class VM {
|
||||||
long gasCost = GasCost.STEP;
|
long gasCost = GasCost.STEP;
|
||||||
long gasBefore = program.getGas().longValue();
|
long gasBefore = program.getGas().longValue();
|
||||||
int stepBefore = program.getPC();
|
int stepBefore = program.getPC();
|
||||||
|
|
||||||
// Calculate fees and spend gas
|
// Calculate fees and spend gas
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case STOP: case SUICIDE:
|
case STOP: case SUICIDE:
|
||||||
|
@ -123,7 +123,7 @@ public class VM {
|
||||||
case BALANCE:
|
case BALANCE:
|
||||||
gasCost = GasCost.BALANCE;
|
gasCost = GasCost.BALANCE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// These all operate on memory and therefore potentially expand it:
|
// These all operate on memory and therefore potentially expand it:
|
||||||
case MSTORE:
|
case MSTORE:
|
||||||
newMemSize = memNeeded(stack.peek(), new DataWord(32));
|
newMemSize = memNeeded(stack.peek(), new DataWord(32));
|
||||||
|
@ -196,13 +196,13 @@ public class VM {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
program.spendGas(gasCost, op.name());
|
program.spendGas(gasCost, op.name());
|
||||||
|
|
||||||
// Avoid overflows
|
// Avoid overflows
|
||||||
if(newMemSize.compareTo(MAX_GAS) == 1)
|
if(newMemSize.compareTo(MAX_GAS) == 1)
|
||||||
throw program.new OutOfGasException();
|
throw program.new OutOfGasException();
|
||||||
|
|
||||||
// memory gas calc
|
// memory gas calc
|
||||||
long memoryUsage = (newMemSize.longValue() + 31) / 32 * 32;
|
long memoryUsage = (newMemSize.longValue() + 31) / 32 * 32;
|
||||||
if (memoryUsage > oldMemSize) {
|
if (memoryUsage > oldMemSize) {
|
||||||
memWords = (memoryUsage - oldMemSize) / 32;
|
memWords = (memoryUsage - oldMemSize) / 32;
|
||||||
long memGas = GasCost.MEMORY * memWords;
|
long memGas = GasCost.MEMORY * memWords;
|
||||||
|
@ -219,7 +219,7 @@ public class VM {
|
||||||
// Log debugging line for VM
|
// Log debugging line for VM
|
||||||
if(program.getNumber().intValue() == CONFIG.dumpBlock())
|
if(program.getNumber().intValue() == CONFIG.dumpBlock())
|
||||||
this.dumpLine(op, gasBefore, gasCost+callGas, memWords, program);
|
this.dumpLine(op, gasBefore, gasCost+callGas, memWords, program);
|
||||||
|
|
||||||
// Execute operation
|
// Execute operation
|
||||||
switch (op) {
|
switch (op) {
|
||||||
/**
|
/**
|
||||||
|
@ -443,7 +443,7 @@ public class VM {
|
||||||
/**
|
/**
|
||||||
* Bitwise Logic Operations
|
* Bitwise Logic Operations
|
||||||
*/
|
*/
|
||||||
case AND:{
|
case AND:{
|
||||||
DataWord word1 = program.stackPop();
|
DataWord word1 = program.stackPop();
|
||||||
DataWord word2 = program.stackPop();
|
DataWord word2 = program.stackPop();
|
||||||
|
|
||||||
|
@ -454,7 +454,7 @@ public class VM {
|
||||||
program.stackPush(word1);
|
program.stackPush(word1);
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case OR: {
|
case OR: {
|
||||||
DataWord word1 = program.stackPop();
|
DataWord word1 = program.stackPop();
|
||||||
DataWord word2 = program.stackPop();
|
DataWord word2 = program.stackPop();
|
||||||
|
|
||||||
|
@ -465,7 +465,7 @@ public class VM {
|
||||||
program.stackPush(word1);
|
program.stackPush(word1);
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case XOR: {
|
case XOR: {
|
||||||
DataWord word1 = program.stackPop();
|
DataWord word1 = program.stackPop();
|
||||||
DataWord word2 = program.stackPop();
|
DataWord word2 = program.stackPop();
|
||||||
|
|
||||||
|
@ -476,7 +476,7 @@ public class VM {
|
||||||
program.stackPush(word1);
|
program.stackPush(word1);
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case BYTE:{
|
case BYTE:{
|
||||||
DataWord word1 = program.stackPop();
|
DataWord word1 = program.stackPop();
|
||||||
DataWord word2 = program.stackPop();
|
DataWord word2 = program.stackPop();
|
||||||
DataWord result = null;
|
DataWord result = null;
|
||||||
|
@ -515,7 +515,7 @@ public class VM {
|
||||||
/**
|
/**
|
||||||
* SHA3
|
* SHA3
|
||||||
*/
|
*/
|
||||||
case SHA3:{
|
case SHA3:{
|
||||||
DataWord memOffsetData = program.stackPop();
|
DataWord memOffsetData = program.stackPop();
|
||||||
DataWord lengthData = program.stackPop();
|
DataWord lengthData = program.stackPop();
|
||||||
ByteBuffer buffer = program.memoryChunk(memOffsetData, lengthData);
|
ByteBuffer buffer = program.memoryChunk(memOffsetData, lengthData);
|
||||||
|
@ -581,7 +581,7 @@ public class VM {
|
||||||
program.stackPush(callValue);
|
program.stackPush(callValue);
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case CALLDATALOAD:{
|
case CALLDATALOAD:{
|
||||||
DataWord dataOffs = program.stackPop();
|
DataWord dataOffs = program.stackPop();
|
||||||
DataWord value = program.getDataValue(dataOffs);
|
DataWord value = program.getDataValue(dataOffs);
|
||||||
|
|
||||||
|
@ -600,11 +600,11 @@ public class VM {
|
||||||
program.stackPush(dataSize);
|
program.stackPush(dataSize);
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case CALLDATACOPY:{
|
case CALLDATACOPY:{
|
||||||
DataWord memOffsetData = program.stackPop();
|
DataWord memOffsetData = program.stackPop();
|
||||||
DataWord dataOffsetData = program.stackPop();
|
DataWord dataOffsetData = program.stackPop();
|
||||||
DataWord lengthData = program.stackPop();
|
DataWord lengthData = program.stackPop();
|
||||||
|
|
||||||
byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);
|
byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);
|
||||||
|
|
||||||
if (logger.isInfoEnabled())
|
if (logger.isInfoEnabled())
|
||||||
|
@ -614,7 +614,7 @@ public class VM {
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case CODESIZE: case EXTCODESIZE: {
|
case CODESIZE: case EXTCODESIZE: {
|
||||||
|
|
||||||
int length;
|
int length;
|
||||||
if (op == OpCode.CODESIZE)
|
if (op == OpCode.CODESIZE)
|
||||||
length = program.getCode().length;
|
length = program.getCode().length;
|
||||||
|
@ -629,7 +629,7 @@ public class VM {
|
||||||
|
|
||||||
program.stackPush(codeLength);
|
program.stackPush(codeLength);
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case CODECOPY: case EXTCODECOPY: {
|
case CODECOPY: case EXTCODECOPY: {
|
||||||
|
|
||||||
byte[] fullCode = ByteUtil.EMPTY_BYTE_ARRAY;
|
byte[] fullCode = ByteUtil.EMPTY_BYTE_ARRAY;
|
||||||
|
@ -733,19 +733,19 @@ public class VM {
|
||||||
program.step();
|
program.step();
|
||||||
} break;
|
} break;
|
||||||
case DUP1: case DUP2: case DUP3: case DUP4:
|
case DUP1: case DUP2: case DUP3: case DUP4:
|
||||||
case DUP5: case DUP6: case DUP7: case DUP8:
|
case DUP5: case DUP6: case DUP7: case DUP8:
|
||||||
case DUP9: case DUP10: case DUP11: case DUP12:
|
case DUP9: case DUP10: case DUP11: case DUP12:
|
||||||
case DUP13: case DUP14: case DUP15: case DUP16:{
|
case DUP13: case DUP14: case DUP15: case DUP16:{
|
||||||
|
|
||||||
int n = op.val() - OpCode.DUP1.val() + 1;
|
int n = op.val() - OpCode.DUP1.val() + 1;
|
||||||
DataWord word_1 = stack.get(stack.size() - n);
|
DataWord word_1 = stack.get(stack.size() - n);
|
||||||
program.stackPush(word_1.clone());
|
program.stackPush(word_1.clone());
|
||||||
program.step();
|
program.step();
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case SWAP1: case SWAP2: case SWAP3: case SWAP4:
|
case SWAP1: case SWAP2: case SWAP3: case SWAP4:
|
||||||
case SWAP5: case SWAP6: case SWAP7: case SWAP8:
|
case SWAP5: case SWAP6: case SWAP7: case SWAP8:
|
||||||
case SWAP9: case SWAP10: case SWAP11: case SWAP12:
|
case SWAP9: case SWAP10: case SWAP11: case SWAP12:
|
||||||
case SWAP13: case SWAP14: case SWAP15: case SWAP16:{
|
case SWAP13: case SWAP14: case SWAP15: case SWAP16:{
|
||||||
|
|
||||||
int n = op.val() - OpCode.SWAP1.val() + 2;
|
int n = op.val() - OpCode.SWAP1.val() + 2;
|
||||||
|
@ -838,7 +838,7 @@ public class VM {
|
||||||
if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val())
|
if (nextPC != 0 && program.getOp(nextPC) != OpCode.JUMPDEST.val())
|
||||||
throw program.new BadJumpDestinationException();
|
throw program.new BadJumpDestinationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logger.isInfoEnabled())
|
if (logger.isInfoEnabled())
|
||||||
hint = "~> " + nextPC;
|
hint = "~> " + nextPC;
|
||||||
|
|
||||||
|
@ -848,7 +848,7 @@ public class VM {
|
||||||
case JUMPI:{
|
case JUMPI:{
|
||||||
DataWord pos = program.stackPop();
|
DataWord pos = program.stackPop();
|
||||||
DataWord cond = program.stackPop();
|
DataWord cond = program.stackPop();
|
||||||
|
|
||||||
if (!cond.isZero()) {
|
if (!cond.isZero()) {
|
||||||
int nextPC = pos.intValue(); // possible overflow
|
int nextPC = pos.intValue(); // possible overflow
|
||||||
if(program.getPreviouslyExecutedOp() < OpCode.PUSH1.val() || program.getPreviouslyExecutedOp() > OpCode.PUSH32.val()) {
|
if(program.getPreviouslyExecutedOp() < OpCode.PUSH1.val() || program.getPreviouslyExecutedOp() > OpCode.PUSH32.val()) {
|
||||||
|
@ -866,7 +866,7 @@ public class VM {
|
||||||
} else {
|
} else {
|
||||||
program.step();
|
program.step();
|
||||||
}
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case PC:{
|
case PC:{
|
||||||
int pc = program.getPC();
|
int pc = program.getPC();
|
||||||
|
@ -925,7 +925,7 @@ public class VM {
|
||||||
String.format("%-12s", op.name()),
|
String.format("%-12s", op.name()),
|
||||||
program.getGas().value(),
|
program.getGas().value(),
|
||||||
program.invokeData.getCallDeep(), hint);
|
program.invokeData.getCallDeep(), hint);
|
||||||
|
|
||||||
program.createContract(value, inOffset, inSize);
|
program.createContract(value, inOffset, inSize);
|
||||||
|
|
||||||
program.step();
|
program.step();
|
||||||
|
@ -940,9 +940,9 @@ public class VM {
|
||||||
|
|
||||||
DataWord outDataOffs = program.stackPop();
|
DataWord outDataOffs = program.stackPop();
|
||||||
DataWord outDataSize = program.stackPop();
|
DataWord outDataSize = program.stackPop();
|
||||||
|
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes())
|
hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes())
|
||||||
+ " gas: " + gas.shortHex()
|
+ " gas: " + gas.shortHex()
|
||||||
+ " inOff: " + inDataOffs.shortHex()
|
+ " inOff: " + inDataOffs.shortHex()
|
||||||
+ " inSize: " + inDataSize.shortHex();
|
+ " inSize: " + inDataSize.shortHex();
|
||||||
|
@ -951,7 +951,7 @@ public class VM {
|
||||||
program.getGas().value(),
|
program.getGas().value(),
|
||||||
program.invokeData.getCallDeep(), hint);
|
program.invokeData.getCallDeep(), hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageCall msg = new MessageCall(
|
MessageCall msg = new MessageCall(
|
||||||
op.equals(CALL) ? MsgType.CALL : MsgType.STATELESS,
|
op.equals(CALL) ? MsgType.CALL : MsgType.STATELESS,
|
||||||
gas, codeAddress, value, inDataOffs, inDataSize,
|
gas, codeAddress, value, inDataOffs, inDataSize,
|
||||||
|
@ -978,24 +978,24 @@ public class VM {
|
||||||
case SUICIDE:{
|
case SUICIDE:{
|
||||||
DataWord address = program.stackPop();
|
DataWord address = program.stackPop();
|
||||||
program.suicide(address);
|
program.suicide(address);
|
||||||
|
|
||||||
if (logger.isInfoEnabled())
|
if (logger.isInfoEnabled())
|
||||||
hint = "address: " + Hex.toHexString(program.getOwnerAddress().getLast20Bytes());
|
hint = "address: " + Hex.toHexString(program.getOwnerAddress().getLast20Bytes());
|
||||||
|
|
||||||
program.stop();
|
program.stop();
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
program.setPreviouslyExecutedOp(op.val());
|
program.setPreviouslyExecutedOp(op.val());
|
||||||
|
|
||||||
if (logger.isInfoEnabled() && !op.equals(CALL)
|
if (logger.isInfoEnabled() && !op.equals(CALL)
|
||||||
&& !op.equals(CREATE))
|
&& !op.equals(CREATE))
|
||||||
logger.info(logString, stepBefore, String.format("%-12s",
|
logger.info(logString, stepBefore, String.format("%-12s",
|
||||||
op.name()), program.getGas().longValue(),
|
op.name()), program.getGas().longValue(),
|
||||||
program.invokeData.getCallDeep(), hint);
|
program.invokeData.getCallDeep(), hint);
|
||||||
|
|
||||||
vmCounter++;
|
vmCounter++;
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
logger.warn("VM halted: [{}]", e.toString());
|
logger.warn("VM halted: [{}]", e.toString());
|
||||||
|
@ -1022,7 +1022,7 @@ public class VM {
|
||||||
program.spendGas(GasCost.TX_NO_ZERO_DATA * nonZeroesVals, "DATA");
|
program.spendGas(GasCost.TX_NO_ZERO_DATA * nonZeroesVals, "DATA");
|
||||||
program.spendGas(GasCost.TX_ZERO_DATA * zeroVals, "DATA");
|
program.spendGas(GasCost.TX_ZERO_DATA * zeroVals, "DATA");
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!program.isStopped())
|
while(!program.isStopped())
|
||||||
this.step(program);
|
this.step(program);
|
||||||
|
|
||||||
|
@ -1034,7 +1034,7 @@ public class VM {
|
||||||
/**
|
/**
|
||||||
* Utility to calculate new total memory size needed for an operation.
|
* Utility to calculate new total memory size needed for an operation.
|
||||||
* <br/> Basically just offset + size, unless size is 0, in which case the result is also 0.
|
* <br/> Basically just offset + size, unless size is 0, in which case the result is also 0.
|
||||||
*
|
*
|
||||||
* @param offset starting position of the memory
|
* @param offset starting position of the memory
|
||||||
* @param size number of bytes needed
|
* @param size number of bytes needed
|
||||||
* @return offset + size, unless size is 0. In that case memNeeded is also 0.
|
* @return offset + size, unless size is 0. In that case memNeeded is also 0.
|
||||||
|
@ -1044,12 +1044,12 @@ public class VM {
|
||||||
return BigInteger.ZERO;
|
return BigInteger.ZERO;
|
||||||
return offset.value().add(size.value());
|
return offset.value().add(size.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dumping the VM state at the current operation in various styles
|
* Dumping the VM state at the current operation in various styles
|
||||||
* - standard Not Yet Implemented
|
* - standard Not Yet Implemented
|
||||||
* - standard+ (owner address, program counter, operation, gas left)
|
* - standard+ (owner address, program counter, operation, gas left)
|
||||||
* - pretty (stack, memory, storage, level, contract,
|
* - pretty (stack, memory, storage, level, contract,
|
||||||
* vmCounter, internalSteps, operation
|
* vmCounter, internalSteps, operation
|
||||||
gasBefore, gasCost, memWords)
|
gasBefore, gasCost, memWords)
|
||||||
*/
|
*/
|
||||||
|
@ -1057,12 +1057,12 @@ public class VM {
|
||||||
if(CONFIG.dumpStyle().equals("standard+")) {
|
if(CONFIG.dumpStyle().equals("standard+")) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case STOP: case RETURN: case SUICIDE:
|
case STOP: case RETURN: case SUICIDE:
|
||||||
|
|
||||||
ContractDetails details = program.getResult().getRepository()
|
ContractDetails details = program.getResult().getRepository()
|
||||||
.getContractDetails(program.getOwnerAddress().getLast20Bytes());
|
.getContractDetails(program.getOwnerAddress().getLast20Bytes());
|
||||||
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
|
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
|
||||||
Collections.sort((List<DataWord>) storageKeys);
|
Collections.sort((List<DataWord>) storageKeys);
|
||||||
|
|
||||||
for (DataWord key : storageKeys) {
|
for (DataWord key : storageKeys) {
|
||||||
dumpLogger.trace("{} {}",
|
dumpLogger.trace("{} {}",
|
||||||
Hex.toHexString(key.getNoLeadZeroesData()),
|
Hex.toHexString(key.getNoLeadZeroesData()),
|
||||||
|
@ -1086,25 +1086,25 @@ public class VM {
|
||||||
String memoryString = program.memoryToString();
|
String memoryString = program.memoryToString();
|
||||||
if(!"".equals(memoryString))
|
if(!"".equals(memoryString))
|
||||||
dumpLogger.trace("{}", memoryString);
|
dumpLogger.trace("{}", memoryString);
|
||||||
|
|
||||||
dumpLogger.trace(" STORAGE");
|
dumpLogger.trace(" STORAGE");
|
||||||
ContractDetails details = program.getResult().getRepository()
|
ContractDetails details = program.getResult().getRepository()
|
||||||
.getContractDetails(program.getOwnerAddress().getLast20Bytes());
|
.getContractDetails(program.getOwnerAddress().getLast20Bytes());
|
||||||
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
|
List<DataWord> storageKeys = new ArrayList<>(details.getStorage().keySet());
|
||||||
Collections.sort((List<DataWord>) storageKeys);
|
Collections.sort((List<DataWord>) storageKeys);
|
||||||
|
|
||||||
for (DataWord key : storageKeys) {
|
for (DataWord key : storageKeys) {
|
||||||
dumpLogger.trace("{}: {}",
|
dumpLogger.trace("{}: {}",
|
||||||
key.shortHex(),
|
key.shortHex(),
|
||||||
details.getStorage().get(key).shortHex());
|
details.getStorage().get(key).shortHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
int level = program.invokeData.getCallDeep();
|
int level = program.invokeData.getCallDeep();
|
||||||
String contract = Hex.toHexString(program.getOwnerAddress().getLast20Bytes());
|
String contract = Hex.toHexString(program.getOwnerAddress().getLast20Bytes());
|
||||||
String internalSteps = String.format("%4s", Integer.toHexString(program.getPC())).replace(' ', '0').toUpperCase();
|
String internalSteps = String.format("%4s", Integer.toHexString(program.getPC())).replace(' ', '0').toUpperCase();
|
||||||
dumpLogger.trace("{} | {} | #{} | {} : {} | {} | -{} | {}x32",
|
dumpLogger.trace("{} | {} | #{} | {} : {} | {} | -{} | {}x32",
|
||||||
level, contract, vmCounter, internalSteps, op,
|
level, contract, vmCounter, internalSteps, op,
|
||||||
gasBefore, gasCost, memWords);
|
gasBefore, gasCost, memWords);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -42,7 +42,7 @@ peer.connection.timeout = 2
|
||||||
transaction.approve.timeout = 15
|
transaction.approve.timeout = 15
|
||||||
|
|
||||||
# the parameter specifies how much
|
# the parameter specifies how much
|
||||||
# time we will wait for a message
|
# time we will wait for a message
|
||||||
# to come before closing the channel
|
# to come before closing the channel
|
||||||
peer.channel.read.timeout = 30
|
peer.channel.read.timeout = 30
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ peer.channel.read.timeout = 30
|
||||||
samples.dir = samples
|
samples.dir = samples
|
||||||
|
|
||||||
# everytime the application starts
|
# everytime the application starts
|
||||||
# the existing database will be
|
# the existing database will be
|
||||||
# destroyed and all the data will be
|
# destroyed and all the data will be
|
||||||
# downloaded from peers again
|
# downloaded from peers again
|
||||||
database.reset = true
|
database.reset = true
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ public class BlockTest {
|
||||||
+ "9e262996493846a590c7011697dba07bb7680a256ede4034212b7a1ae6c7caea73190cb0"
|
+ "9e262996493846a590c7011697dba07bb7680a256ede4034212b7a1ae6c7caea73190cb0"
|
||||||
+ "7dedb91a07b72f34074e76a00cd22d78d556175604407dc6109797f5c8d990d05f1b352e"
|
+ "7dedb91a07b72f34074e76a00cd22d78d556175604407dc6109797f5c8d990d05f1b352e"
|
||||||
+ "10c71b3dd74bc70f8201f4c0";
|
+ "10c71b3dd74bc70f8201f4c0";
|
||||||
|
|
||||||
String block_32 = "f8f8f8f4a00a312c2b0a8f125c60a3976b6e508e740e095eb59943988d9bbfb8"
|
String block_32 = "f8f8f8f4a00a312c2b0a8f125c60a3976b6e508e740e095eb59943988d9bbfb8"
|
||||||
+ "aa43922e31a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4"
|
+ "aa43922e31a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4"
|
||||||
+ "934794e559de5527492bcb42ec68d07df0742a98ec3f1ea050188ab86bdf164ac90eb283"
|
+ "934794e559de5527492bcb42ec68d07df0742a98ec3f1ea050188ab86bdf164ac90eb283"
|
||||||
|
@ -104,7 +104,7 @@ public class BlockTest {
|
||||||
assertEquals(Hex.toHexString(genesis.getParentHash()), Hex.toHexString(genesisFromRLP.getParentHash()));
|
assertEquals(Hex.toHexString(genesis.getParentHash()), Hex.toHexString(genesisFromRLP.getParentHash()));
|
||||||
assertEquals(Hex.toHexString(genesis.getStateRoot()), Hex.toHexString(genesisFromRLP.getStateRoot()));
|
assertEquals(Hex.toHexString(genesis.getStateRoot()), Hex.toHexString(genesisFromRLP.getStateRoot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenesisFromNew() {
|
public void testGenesisFromNew() {
|
||||||
Block genesis = Genesis.getInstance();
|
Block genesis = Genesis.getInstance();
|
||||||
|
@ -115,7 +115,7 @@ public class BlockTest {
|
||||||
assertEquals(PoC7_GENESIS_HEX_HASH, Hex.toHexString(genesis.getHash()));
|
assertEquals(PoC7_GENESIS_HEX_HASH, Hex.toHexString(genesis.getHash()));
|
||||||
assertEquals(PoC7_GENESIS_HEX_RLP_ENCODED, Hex.toHexString(genesis.getEncoded()));
|
assertEquals(PoC7_GENESIS_HEX_RLP_ENCODED, Hex.toHexString(genesis.getEncoded()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test /* block without transactions - block#32 in PoC5 cpp-chain */
|
@Test /* block without transactions - block#32 in PoC5 cpp-chain */
|
||||||
public void testEmptyBlock() {
|
public void testEmptyBlock() {
|
||||||
byte[] payload = Hex.decode(block_32);
|
byte[] payload = Hex.decode(block_32);
|
||||||
|
@ -137,7 +137,7 @@ public class BlockTest {
|
||||||
Block block = new Block(payload);
|
Block block = new Block(payload);
|
||||||
logger.info(block.toString());
|
logger.info(block.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalcDifficulty() {
|
public void testCalcDifficulty() {
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ public class BlockTest {
|
||||||
logger.info("Block#1 calculated difficulty: [{}] ", calcDifficulty.toString());
|
logger.info("Block#1 calculated difficulty: [{}] ", calcDifficulty.toString());
|
||||||
assertEquals(actualDifficulty, calcDifficulty);
|
assertEquals(actualDifficulty, calcDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalcGasLimit() {
|
public void testCalcGasLimit() {
|
||||||
BlockchainImpl blockchain = (BlockchainImpl)worldManager.getBlockchain();
|
BlockchainImpl blockchain = (BlockchainImpl)worldManager.getBlockchain();
|
||||||
|
@ -240,14 +240,14 @@ public class BlockTest {
|
||||||
// TODO
|
// TODO
|
||||||
fail("Not yet implemented");
|
fail("Not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void testUncleInvalidGenerationGap() {
|
public void testUncleInvalidGenerationGap() {
|
||||||
// TODO
|
// TODO
|
||||||
fail("Not yet implemented");
|
fail("Not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void testUncleInvalidParentGenerationGap() {
|
public void testUncleInvalidParentGenerationGap() {
|
||||||
|
|
|
@ -19,30 +19,30 @@ public class MinerTest {
|
||||||
// Example block#32 from Poc5 chain - rlpEncoded without nonce
|
// Example block#32 from Poc5 chain - rlpEncoded without nonce
|
||||||
private String rlpWithoutNonce = "f894f890a00a312c2b0a8f125c60a3976b6e508e740e095eb59943988d9bbfb8"
|
private String rlpWithoutNonce = "f894f890a00a312c2b0a8f125c60a3976b6e508e740e095eb59943988d9bbfb8"
|
||||||
+ "aa43922e31a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e559de5527492bcb42ec68d07df0742a98ec3f1ea050188ab86bdf164ac90eb2835a04a8930aae5393c3a2ef1166fb95028f9456b880833ee248208609184e72a000830eca0080845387fd2080c0c0";
|
+ "aa43922e31a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e559de5527492bcb42ec68d07df0742a98ec3f1ea050188ab86bdf164ac90eb2835a04a8930aae5393c3a2ef1166fb95028f9456b880833ee248208609184e72a000830eca0080845387fd2080c0c0";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void testMine() {
|
public void testMine() {
|
||||||
boolean miningTestEnabled = false;
|
boolean miningTestEnabled = false;
|
||||||
|
|
||||||
if(miningTestEnabled) {
|
if(miningTestEnabled) {
|
||||||
Block block = createBlock(null);
|
Block block = createBlock(null);
|
||||||
assertEquals(rlpWithoutNonce, Hex.toHexString(block.getEncodedWithoutNonce()));
|
assertEquals(rlpWithoutNonce, Hex.toHexString(block.getEncodedWithoutNonce()));
|
||||||
System.out.println("Searching for nonce of following block: \n" + block.toString());
|
System.out.println("Searching for nonce of following block: \n" + block.toString());
|
||||||
|
|
||||||
Miner miner = new Miner();
|
Miner miner = new Miner();
|
||||||
boolean mined = miner.mine(block, block.getDifficulty());
|
boolean mined = miner.mine(block, block.getDifficulty());
|
||||||
assertTrue(mined);
|
assertTrue(mined);
|
||||||
boolean valid = block.validateNonce();
|
boolean valid = block.validateNonce();
|
||||||
assertTrue(valid);
|
assertTrue(valid);
|
||||||
|
|
||||||
// expectedHash is the actual hash from block#32 in PoC5 chain based on nonce below
|
// expectedHash is the actual hash from block#32 in PoC5 chain based on nonce below
|
||||||
String expectedHash = "ce7201f6cc5eb1a6f35c7dda8acda111647a0f8a5bf0518e46579b03e29fe14b";
|
String expectedHash = "ce7201f6cc5eb1a6f35c7dda8acda111647a0f8a5bf0518e46579b03e29fe14b";
|
||||||
assertEquals(expectedHash, Hex.toHexString(block.getHash()));
|
assertEquals(expectedHash, Hex.toHexString(block.getHash()));
|
||||||
|
|
||||||
// expectedNonce is the actual nonce from block#32 in Poc5 chain
|
// expectedNonce is the actual nonce from block#32 in Poc5 chain
|
||||||
String expectedNonce = "0000000000000000000000000000000000000000000000001f52ebb192c4ea97"; // from Poc5 chain
|
String expectedNonce = "0000000000000000000000000000000000000000000000001f52ebb192c4ea97"; // from Poc5 chain
|
||||||
// Actual is "000000000000000000000000000000000000000000000000000000000098cc15"
|
// Actual is "000000000000000000000000000000000000000000000000000000000098cc15"
|
||||||
// but that might also be a valid nonce in compliance with PoW(H!n, n) < (2^256 / difficulty)
|
// but that might also be a valid nonce in compliance with PoW(H!n, n) < (2^256 / difficulty)
|
||||||
assertEquals(expectedNonce, Hex.toHexString(block.getNonce()));
|
assertEquals(expectedNonce, Hex.toHexString(block.getNonce()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class StateTest {
|
||||||
byte[] minerAddress = Hex.decode("4c5f4d519dff3c16f0d54b6866e256fbbbc1a600");
|
byte[] minerAddress = Hex.decode("4c5f4d519dff3c16f0d54b6866e256fbbbc1a600");
|
||||||
AccountState account_3 = new AccountState(BigInteger.ZERO, new BigInteger("1506260000000000000"));
|
AccountState account_3 = new AccountState(BigInteger.ZERO, new BigInteger("1506260000000000000"));
|
||||||
trie.update(minerAddress, account_3.getEncoded());
|
trie.update(minerAddress, account_3.getEncoded());
|
||||||
|
|
||||||
assertEquals(expected, Hex.toHexString(trie.getRootHash()));
|
assertEquals(expected, Hex.toHexString(trie.getRootHash()));
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ public class StateTest {
|
||||||
Trie trie = new TrieImpl(new MockDB());
|
Trie trie = new TrieImpl(new MockDB());
|
||||||
for (String address : Genesis.getPremine()) {
|
for (String address : Genesis.getPremine()) {
|
||||||
AccountState acct = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200));
|
AccountState acct = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200));
|
||||||
trie.update(Hex.decode(address), acct.getEncoded());
|
trie.update(Hex.decode(address), acct.getEncoded());
|
||||||
}
|
}
|
||||||
return trie;
|
return trie;
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ public class TransactionTest {
|
||||||
|
|
||||||
ECKey ecKey = ECKey.fromPrivate(HashUtil.sha3("cat".getBytes()));
|
ECKey ecKey = ECKey.fromPrivate(HashUtil.sha3("cat".getBytes()));
|
||||||
byte[] senderPrivKey = HashUtil.sha3("cow".getBytes());
|
byte[] senderPrivKey = HashUtil.sha3("cow".getBytes());
|
||||||
|
|
||||||
byte[] nonce = { 0x01 };
|
byte[] nonce = { 0x01 };
|
||||||
byte[] gasPrice = Hex.decode("09184e72a000");
|
byte[] gasPrice = Hex.decode("09184e72a000");
|
||||||
byte[] gasLimit = Hex.decode("4255");
|
byte[] gasLimit = Hex.decode("4255");
|
||||||
|
@ -152,14 +152,14 @@ public class TransactionTest {
|
||||||
byte[] testValue = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(10000000000000000L));
|
byte[] testValue = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(10000000000000000L));
|
||||||
byte[] testData = Hex.decode("");
|
byte[] testData = Hex.decode("");
|
||||||
byte[] testInit = Hex.decode("");
|
byte[] testInit = Hex.decode("");
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionFromSignedRLP() throws Exception {
|
public void testTransactionFromSignedRLP() throws Exception {
|
||||||
Transaction txSigned = new Transaction(Hex.decode(RLP_ENCODED_SIGNED_TX));
|
Transaction txSigned = new Transaction(Hex.decode(RLP_ENCODED_SIGNED_TX));
|
||||||
|
|
||||||
assertEquals(HASH_TX, Hex.toHexString(txSigned.getHash()));
|
assertEquals(HASH_TX, Hex.toHexString(txSigned.getHash()));
|
||||||
assertEquals(RLP_ENCODED_SIGNED_TX, Hex.toHexString(txSigned.getEncoded()));
|
assertEquals(RLP_ENCODED_SIGNED_TX, Hex.toHexString(txSigned.getEncoded()));
|
||||||
|
|
||||||
assertEquals(BigInteger.ZERO, new BigInteger(1, txSigned.getNonce()));
|
assertEquals(BigInteger.ZERO, new BigInteger(1, txSigned.getNonce()));
|
||||||
assertEquals(new BigInteger(1, testGasPrice), new BigInteger(1, txSigned.getGasPrice()));
|
assertEquals(new BigInteger(1, testGasPrice), new BigInteger(1, txSigned.getGasPrice()));
|
||||||
assertEquals(new BigInteger(1, testGasLimit), new BigInteger(1, txSigned.getGasLimit()));
|
assertEquals(new BigInteger(1, testGasLimit), new BigInteger(1, txSigned.getGasLimit()));
|
||||||
|
@ -170,16 +170,16 @@ public class TransactionTest {
|
||||||
assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().r)));
|
assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().r)));
|
||||||
assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().s)));
|
assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionFromUnsignedRLP() throws Exception {
|
public void testTransactionFromUnsignedRLP() throws Exception {
|
||||||
Transaction txUnsigned = new Transaction(Hex.decode(RLP_ENCODED_UNSIGNED_TX));
|
Transaction txUnsigned = new Transaction(Hex.decode(RLP_ENCODED_UNSIGNED_TX));
|
||||||
|
|
||||||
assertEquals(HASH_TX, Hex.toHexString(txUnsigned.getHash()));
|
assertEquals(HASH_TX, Hex.toHexString(txUnsigned.getHash()));
|
||||||
assertEquals(RLP_ENCODED_UNSIGNED_TX, Hex.toHexString(txUnsigned.getEncoded()));
|
assertEquals(RLP_ENCODED_UNSIGNED_TX, Hex.toHexString(txUnsigned.getEncoded()));
|
||||||
txUnsigned.sign(Hex.decode(KEY));
|
txUnsigned.sign(Hex.decode(KEY));
|
||||||
assertEquals(RLP_ENCODED_SIGNED_TX, Hex.toHexString(txUnsigned.getEncoded()));
|
assertEquals(RLP_ENCODED_SIGNED_TX, Hex.toHexString(txUnsigned.getEncoded()));
|
||||||
|
|
||||||
assertEquals(BigInteger.ZERO, new BigInteger(1, txUnsigned.getNonce()));
|
assertEquals(BigInteger.ZERO, new BigInteger(1, txUnsigned.getNonce()));
|
||||||
assertEquals(new BigInteger(1, testGasPrice), new BigInteger(1, txUnsigned.getGasPrice()));
|
assertEquals(new BigInteger(1, testGasPrice), new BigInteger(1, txUnsigned.getGasPrice()));
|
||||||
assertEquals(new BigInteger(1, testGasLimit), new BigInteger(1, txUnsigned.getGasLimit()));
|
assertEquals(new BigInteger(1, testGasLimit), new BigInteger(1, txUnsigned.getGasLimit()));
|
||||||
|
@ -190,11 +190,11 @@ public class TransactionTest {
|
||||||
assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().r)));
|
assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().r)));
|
||||||
assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().s)));
|
assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionFromNew1() throws MissingPrivateKeyException {
|
public void testTransactionFromNew1() throws MissingPrivateKeyException {
|
||||||
Transaction txNew = new Transaction(testNonce, testGasPrice, testGasLimit, testReceiveAddress, testValue, testData);
|
Transaction txNew = new Transaction(testNonce, testGasPrice, testGasLimit, testReceiveAddress, testValue, testData);
|
||||||
|
|
||||||
assertEquals("", Hex.toHexString(txNew.getNonce()));
|
assertEquals("", Hex.toHexString(txNew.getNonce()));
|
||||||
assertEquals(new BigInteger(1, testGasPrice), new BigInteger(1, txNew.getGasPrice()));
|
assertEquals(new BigInteger(1, testGasPrice), new BigInteger(1, txNew.getGasPrice()));
|
||||||
assertEquals(new BigInteger(1, testGasLimit), new BigInteger(1, txNew.getGasLimit()));
|
assertEquals(new BigInteger(1, testGasLimit), new BigInteger(1, txNew.getGasLimit()));
|
||||||
|
@ -202,35 +202,35 @@ public class TransactionTest {
|
||||||
assertEquals(new BigInteger(1, testValue), new BigInteger(1, txNew.getValue()));
|
assertEquals(new BigInteger(1, testValue), new BigInteger(1, txNew.getValue()));
|
||||||
assertEquals("", Hex.toHexString(txNew.getData()));
|
assertEquals("", Hex.toHexString(txNew.getData()));
|
||||||
assertNull(txNew.getSignature());
|
assertNull(txNew.getSignature());
|
||||||
|
|
||||||
assertEquals(RLP_ENCODED_RAW_TX, Hex.toHexString(txNew.getEncodedRaw()));
|
assertEquals(RLP_ENCODED_RAW_TX, Hex.toHexString(txNew.getEncodedRaw()));
|
||||||
assertEquals(HASH_TX, Hex.toHexString(txNew.getHash()));
|
assertEquals(HASH_TX, Hex.toHexString(txNew.getHash()));
|
||||||
assertEquals(RLP_ENCODED_UNSIGNED_TX, Hex.toHexString(txNew.getEncoded()));
|
assertEquals(RLP_ENCODED_UNSIGNED_TX, Hex.toHexString(txNew.getEncoded()));
|
||||||
txNew.sign(Hex.decode(KEY));
|
txNew.sign(Hex.decode(KEY));
|
||||||
assertEquals(RLP_ENCODED_SIGNED_TX, Hex.toHexString(txNew.getEncoded()));
|
assertEquals(RLP_ENCODED_SIGNED_TX, Hex.toHexString(txNew.getEncoded()));
|
||||||
|
|
||||||
assertEquals(27, txNew.getSignature().v);
|
assertEquals(27, txNew.getSignature().v);
|
||||||
assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txNew.getSignature().r)));
|
assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txNew.getSignature().r)));
|
||||||
assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txNew.getSignature().s)));
|
assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txNew.getSignature().s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionFromNew2() throws MissingPrivateKeyException {
|
public void testTransactionFromNew2() throws MissingPrivateKeyException {
|
||||||
byte[] privKeyBytes = Hex.decode("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4");
|
byte[] privKeyBytes = Hex.decode("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4");
|
||||||
|
|
||||||
String RLP_TX_UNSIGNED = "eb8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc1000080808080";
|
String RLP_TX_UNSIGNED = "eb8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc1000080808080";
|
||||||
String RLP_TX_SIGNED = "f86b8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc10000801ba0eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4a014a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1";
|
String RLP_TX_SIGNED = "f86b8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc10000801ba0eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4a014a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1";
|
||||||
String HASH_TX_UNSIGNED = "328ea6d24659dec48adea1aced9a136e5ebdf40258db30d1b1d97ed2b74be34e";
|
String HASH_TX_UNSIGNED = "328ea6d24659dec48adea1aced9a136e5ebdf40258db30d1b1d97ed2b74be34e";
|
||||||
|
|
||||||
byte[] nonce = BigIntegers.asUnsignedByteArray(BigInteger.ZERO);
|
byte[] nonce = BigIntegers.asUnsignedByteArray(BigInteger.ZERO);
|
||||||
byte[] gasPrice = Hex.decode("e8d4a51000"); // 1000000000000
|
byte[] gasPrice = Hex.decode("e8d4a51000"); // 1000000000000
|
||||||
byte[] gas = Hex.decode("2710"); // 10000
|
byte[] gas = Hex.decode("2710"); // 10000
|
||||||
byte[] recieveAddress = Hex.decode("13978aee95f38490e9769c39b2773ed763d9cd5f");
|
byte[] recieveAddress = Hex.decode("13978aee95f38490e9769c39b2773ed763d9cd5f");
|
||||||
byte[] value = Hex.decode("2386f26fc10000"); //10000000000000000"
|
byte[] value = Hex.decode("2386f26fc10000"); //10000000000000000"
|
||||||
byte[] data = new byte[0];
|
byte[] data = new byte[0];
|
||||||
|
|
||||||
Transaction tx = new Transaction(nonce, gasPrice, gas, recieveAddress, value, data);
|
Transaction tx = new Transaction(nonce, gasPrice, gas, recieveAddress, value, data);
|
||||||
|
|
||||||
// Testing unsigned
|
// Testing unsigned
|
||||||
String encodedUnsigned = Hex.toHexString(tx.getEncoded());
|
String encodedUnsigned = Hex.toHexString(tx.getEncoded());
|
||||||
assertEquals(RLP_TX_UNSIGNED, encodedUnsigned);
|
assertEquals(RLP_TX_UNSIGNED, encodedUnsigned);
|
||||||
|
@ -238,7 +238,7 @@ public class TransactionTest {
|
||||||
|
|
||||||
// Testing signed
|
// Testing signed
|
||||||
tx.sign(privKeyBytes);
|
tx.sign(privKeyBytes);
|
||||||
String encodedSigned = Hex.toHexString(tx.getEncoded());
|
String encodedSigned = Hex.toHexString(tx.getEncoded());
|
||||||
assertEquals(RLP_TX_SIGNED, encodedSigned);
|
assertEquals(RLP_TX_SIGNED, encodedSigned);
|
||||||
assertEquals(HASH_TX_UNSIGNED, Hex.toHexString(tx.getHash()));
|
assertEquals(HASH_TX_UNSIGNED, Hex.toHexString(tx.getHash()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,19 +28,19 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
|
||||||
public class ECKeyTest {
|
public class ECKeyTest {
|
||||||
private static final Logger log = LoggerFactory.getLogger(ECKeyTest.class);
|
private static final Logger log = LoggerFactory.getLogger(ECKeyTest.class);
|
||||||
|
|
||||||
private String privString = "3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4";
|
private String privString = "3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4";
|
||||||
private BigInteger privateKey = new BigInteger(Hex.decode(privString));
|
private BigInteger privateKey = new BigInteger(Hex.decode(privString));
|
||||||
|
|
||||||
private String pubString = "0497466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f";
|
private String pubString = "0497466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f";
|
||||||
private String compressedPubString = "0397466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce7";
|
private String compressedPubString = "0397466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce7";
|
||||||
private byte[] pubKey = Hex.decode(pubString);
|
private byte[] pubKey = Hex.decode(pubString);
|
||||||
private byte[] compressedPubKey = Hex.decode(compressedPubString);
|
private byte[] compressedPubKey = Hex.decode(compressedPubString);
|
||||||
private String address = "8a40bfaa73256b60764c1bf40675a99083efb075";
|
private String address = "8a40bfaa73256b60764c1bf40675a99083efb075";
|
||||||
|
|
||||||
private String exampleMessage = new String("This is an example of a signed message.");
|
private String exampleMessage = new String("This is an example of a signed message.");
|
||||||
private String sigBase64 = "HD5AsBr4wuH6UU9tXuSJhUvgfGayfwoY0cKT03sFUjnpQsupHznd/3mCIRfLuNHlRCVGdAyHecdyM8IVZMtc1I8=";
|
private String sigBase64 = "HD5AsBr4wuH6UU9tXuSJhUvgfGayfwoY0cKT03sFUjnpQsupHznd/3mCIRfLuNHlRCVGdAyHecdyM8IVZMtc1I8=";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHashCode() {
|
public void testHashCode() {
|
||||||
Assert.assertEquals(1866897155, ECKey.fromPrivate(privateKey).hashCode());
|
Assert.assertEquals(1866897155, ECKey.fromPrivate(privateKey).hashCode());
|
||||||
|
@ -84,7 +84,7 @@ public class ECKeyTest {
|
||||||
byte[] pubFromPriv = ECKey.publicKeyFromPrivate(privateKey, false);
|
byte[] pubFromPriv = ECKey.publicKeyFromPrivate(privateKey, false);
|
||||||
assertArrayEquals(pubKey, pubFromPriv);
|
assertArrayEquals(pubKey, pubFromPriv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPublicKeyFromPrivateCompressed() {
|
public void testPublicKeyFromPrivateCompressed() {
|
||||||
byte[] pubFromPriv = ECKey.publicKeyFromPrivate(privateKey, true);
|
byte[] pubFromPriv = ECKey.publicKeyFromPrivate(privateKey, true);
|
||||||
|
@ -105,7 +105,7 @@ public class ECKeyTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEthereumSign() throws IOException {
|
public void testEthereumSign() throws IOException {
|
||||||
// TODO: Understand why key must be decompressed for this to work
|
// TODO: Understand why key must be decompressed for this to work
|
||||||
ECKey key = ECKey.fromPrivate(privateKey).decompress();
|
ECKey key = ECKey.fromPrivate(privateKey).decompress();
|
||||||
System.out.println("Secret\t: " + Hex.toHexString(key.getPrivKeyBytes()));
|
System.out.println("Secret\t: " + Hex.toHexString(key.getPrivKeyBytes()));
|
||||||
System.out.println("Pubkey\t: " + Hex.toHexString(key.getPubKey()));
|
System.out.println("Pubkey\t: " + Hex.toHexString(key.getPubKey()));
|
||||||
|
@ -116,7 +116,7 @@ public class ECKeyTest {
|
||||||
System.out.println("Signtr\t: " + output + " (Base64, length: " + output.length() + ")");
|
System.out.println("Signtr\t: " + output + " (Base64, length: " + output.length() + ")");
|
||||||
assertEquals(sigBase64, output);
|
assertEquals(sigBase64, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVerifySignature1() {
|
public void testVerifySignature1() {
|
||||||
ECKey key = ECKey.fromPublicOnly(pubKey);
|
ECKey key = ECKey.fromPublicOnly(pubKey);
|
||||||
|
@ -125,7 +125,7 @@ public class ECKeyTest {
|
||||||
ECDSASignature sig = ECDSASignature.fromComponents(r.toByteArray(), s.toByteArray(), (byte) 28);
|
ECDSASignature sig = ECDSASignature.fromComponents(r.toByteArray(), s.toByteArray(), (byte) 28);
|
||||||
key.verify(HashUtil.sha3(exampleMessage.getBytes()), sig);
|
key.verify(HashUtil.sha3(exampleMessage.getBytes()), sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVerifySignature2() {
|
public void testVerifySignature2() {
|
||||||
BigInteger r = new BigInteger("c52c114d4f5a3ba904a9b3036e5e118fe0dbb987fe3955da20f2cd8f6c21ab9c", 16);
|
BigInteger r = new BigInteger("c52c114d4f5a3ba904a9b3036e5e118fe0dbb987fe3955da20f2cd8f6c21ab9c", 16);
|
||||||
|
@ -158,7 +158,7 @@ public class ECKeyTest {
|
||||||
// todo: add test assertion when the sign/verify part actually works.
|
// todo: add test assertion when the sign/verify part actually works.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSValue() throws Exception {
|
public void testSValue() throws Exception {
|
||||||
// Check that we never generate an S value that is larger than half the curve order. This avoids a malleability
|
// Check that we never generate an S value that is larger than half the curve order. This avoids a malleability
|
||||||
|
@ -184,7 +184,7 @@ public class ECKeyTest {
|
||||||
assertEquals(sigs.get(0), duplicate);
|
assertEquals(sigs.get(0), duplicate);
|
||||||
assertEquals(sigs.get(0).hashCode(), duplicate.hashCode());
|
assertEquals(sigs.get(0).hashCode(), duplicate.hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignVerify() {
|
public void testSignVerify() {
|
||||||
ECKey key = ECKey.fromPrivate(privateKey);
|
ECKey key = ECKey.fromPrivate(privateKey);
|
||||||
|
@ -192,7 +192,7 @@ public class ECKeyTest {
|
||||||
ECDSASignature output = key.doSign(message.getBytes());
|
ECDSASignature output = key.doSign(message.getBytes());
|
||||||
assertTrue(key.verify(message.getBytes(), output));
|
assertTrue(key.verify(message.getBytes(), output));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsPubKeyCanonicalCorect() {
|
public void testIsPubKeyCanonicalCorect() {
|
||||||
// Test correct prefix 4, right length 65
|
// Test correct prefix 4, right length 65
|
||||||
|
@ -205,7 +205,7 @@ public class ECKeyTest {
|
||||||
byte[] canonicalPubkey3 = new byte[33]; canonicalPubkey3[0] = 0x03;
|
byte[] canonicalPubkey3 = new byte[33]; canonicalPubkey3[0] = 0x03;
|
||||||
assertTrue(ECKey.isPubKeyCanonical(canonicalPubkey3));
|
assertTrue(ECKey.isPubKeyCanonical(canonicalPubkey3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsPubKeyCanonicalWrongLength() {
|
public void testIsPubKeyCanonicalWrongLength() {
|
||||||
// Test correct prefix 4, but wrong length !65
|
// Test correct prefix 4, but wrong length !65
|
||||||
|
@ -218,7 +218,7 @@ public class ECKeyTest {
|
||||||
byte[] nonCanonicalPubkey3 = new byte[32]; nonCanonicalPubkey3[0] = 0x03;
|
byte[] nonCanonicalPubkey3 = new byte[32]; nonCanonicalPubkey3[0] = 0x03;
|
||||||
assertFalse(ECKey.isPubKeyCanonical(nonCanonicalPubkey3));
|
assertFalse(ECKey.isPubKeyCanonical(nonCanonicalPubkey3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsPubKeyCanonicalWrongPrefix() {
|
public void testIsPubKeyCanonicalWrongPrefix() {
|
||||||
// Test wrong prefix 4, right length 65
|
// Test wrong prefix 4, right length 65
|
||||||
|
@ -231,7 +231,7 @@ public class ECKeyTest {
|
||||||
byte[] nonCanonicalPubkey6 = new byte[33];
|
byte[] nonCanonicalPubkey6 = new byte[33];
|
||||||
assertFalse(ECKey.isPubKeyCanonical(nonCanonicalPubkey6));
|
assertFalse(ECKey.isPubKeyCanonical(nonCanonicalPubkey6));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void keyRecovery() throws Exception {
|
public void keyRecovery() throws Exception {
|
||||||
ECKey key = new ECKey();
|
ECKey key = new ECKey();
|
||||||
|
@ -250,7 +250,7 @@ public class ECKeyTest {
|
||||||
}
|
}
|
||||||
assertTrue(found);
|
assertTrue(found);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignedMessageToKey() throws SignatureException {
|
public void testSignedMessageToKey() throws SignatureException {
|
||||||
byte[] messageHash = HashUtil.sha3(exampleMessage.getBytes());
|
byte[] messageHash = HashUtil.sha3(exampleMessage.getBytes());
|
||||||
|
@ -258,7 +258,7 @@ public class ECKeyTest {
|
||||||
assertNotNull(key);
|
assertNotNull(key);
|
||||||
assertArrayEquals(pubKey, key.getPubKey());
|
assertArrayEquals(pubKey, key.getPubKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetPrivKeyBytes() {
|
public void testGetPrivKeyBytes() {
|
||||||
ECKey key = new ECKey();
|
ECKey key = new ECKey();
|
||||||
|
@ -271,9 +271,9 @@ public class ECKeyTest {
|
||||||
ECKey key0 = new ECKey();
|
ECKey key0 = new ECKey();
|
||||||
ECKey key1 = ECKey.fromPrivate(privateKey);
|
ECKey key1 = ECKey.fromPrivate(privateKey);
|
||||||
ECKey key2 = ECKey.fromPrivate(privateKey);
|
ECKey key2 = ECKey.fromPrivate(privateKey);
|
||||||
|
|
||||||
assertFalse(key0.equals(key1));
|
assertFalse(key0.equals(key1));
|
||||||
assertTrue(key1.equals(key1));
|
assertTrue(key1.equals(key1));
|
||||||
assertTrue(key1.equals(key2));
|
assertTrue(key1.equals(key2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,10 @@ public class ByteArrayWrapperTest {
|
||||||
static ByteArrayWrapper wrapper2;
|
static ByteArrayWrapper wrapper2;
|
||||||
static ByteArrayWrapper wrapper3;
|
static ByteArrayWrapper wrapper3;
|
||||||
static ByteArrayWrapper wrapper4;
|
static ByteArrayWrapper wrapper4;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void loadByteArrays() {
|
public static void loadByteArrays() {
|
||||||
|
|
||||||
String block = "f9072df8d3a077ef4fdaf389dca53236bcf7f72698e154eab2828f86fbc4fc6c"
|
String block = "f9072df8d3a077ef4fdaf389dca53236bcf7f72698e154eab2828f86fbc4fc6c"
|
||||||
+ "d9225d285c89a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0"
|
+ "d9225d285c89a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0"
|
||||||
+ "a142fd40d493479476f5eabe4b342ee56b8ceba6ab2a770c3e2198e7a0faa0ca"
|
+ "a142fd40d493479476f5eabe4b342ee56b8ceba6ab2a770c3e2198e7a0faa0ca"
|
||||||
|
@ -81,18 +81,18 @@ public class ByteArrayWrapperTest {
|
||||||
+ "2ba37af8e83c3741225da066ae0ec1217b0ca698a5369d4881e1c4cbde56af99"
|
+ "2ba37af8e83c3741225da066ae0ec1217b0ca698a5369d4881e1c4cbde56af99"
|
||||||
+ "31ebf9281580a23b659c08a051f947cb2315d0259f55848c630caa10cd91d6e4"
|
+ "31ebf9281580a23b659c08a051f947cb2315d0259f55848c630caa10cd91d6e4"
|
||||||
+ "4ff8bad7758c65b25e2191308227d2c0";
|
+ "4ff8bad7758c65b25e2191308227d2c0";
|
||||||
|
|
||||||
byte[] test1 = Hex.decode(block);
|
byte[] test1 = Hex.decode(block);
|
||||||
byte[] test2 = Hex.decode(block);
|
byte[] test2 = Hex.decode(block);
|
||||||
byte[] test3 = Hex.decode("4ff8bad7758c65b25e2191308227d2c0");
|
byte[] test3 = Hex.decode("4ff8bad7758c65b25e2191308227d2c0");
|
||||||
byte[] test4 = Hex.decode("");
|
byte[] test4 = Hex.decode("");
|
||||||
|
|
||||||
wrapper1 = new ByteArrayWrapper(test1);
|
wrapper1 = new ByteArrayWrapper(test1);
|
||||||
wrapper2 = new ByteArrayWrapper(test2);
|
wrapper2 = new ByteArrayWrapper(test2);
|
||||||
wrapper3 = new ByteArrayWrapper(test3);
|
wrapper3 = new ByteArrayWrapper(test3);
|
||||||
wrapper4 = new ByteArrayWrapper(test4);
|
wrapper4 = new ByteArrayWrapper(test4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEqualsObject() {
|
public void testEqualsObject() {
|
||||||
assertTrue(wrapper1.equals(wrapper2));
|
assertTrue(wrapper1.equals(wrapper2));
|
||||||
|
@ -109,11 +109,11 @@ public class ByteArrayWrapperTest {
|
||||||
assertTrue(wrapper1.compareTo(wrapper4) > 1);
|
assertTrue(wrapper1.compareTo(wrapper4) > 1);
|
||||||
assertTrue(wrapper2.compareTo(wrapper3) > 1);
|
assertTrue(wrapper2.compareTo(wrapper3) > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEqualsPerformance() {
|
public void testEqualsPerformance() {
|
||||||
boolean testEnabled = false;
|
boolean testEnabled = false;
|
||||||
|
|
||||||
if(testEnabled) {
|
if(testEnabled) {
|
||||||
final int ITERATIONS = 10000000;
|
final int ITERATIONS = 10000000;
|
||||||
long start1 = System.currentTimeMillis();
|
long start1 = System.currentTimeMillis();
|
||||||
|
@ -125,18 +125,18 @@ public class ByteArrayWrapperTest {
|
||||||
wrapper2.getData());
|
wrapper2.getData());
|
||||||
}
|
}
|
||||||
System.out.println(System.currentTimeMillis() - start1 + "ms");
|
System.out.println(System.currentTimeMillis() - start1 + "ms");
|
||||||
|
|
||||||
long start2 = System.currentTimeMillis();
|
long start2 = System.currentTimeMillis();
|
||||||
for (int i = 0; i < ITERATIONS; i++) {
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
Arrays.equals(wrapper1.getData(), wrapper2.getData());
|
Arrays.equals(wrapper1.getData(), wrapper2.getData());
|
||||||
}
|
}
|
||||||
System.out.println(System.currentTimeMillis() - start2 + "ms");
|
System.out.println(System.currentTimeMillis() - start2 + "ms");
|
||||||
|
|
||||||
long start3 = System.currentTimeMillis();
|
long start3 = System.currentTimeMillis();
|
||||||
for (int i = 0; i < ITERATIONS; i++) {
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
FastByteComparisons.compareTo(wrapper1.getData(), 0, wrapper1.getData().length, wrapper2.getData(), 0, wrapper1.getData().length);
|
FastByteComparisons.compareTo(wrapper1.getData(), 0, wrapper1.getData().length, wrapper2.getData(), 0, wrapper1.getData().length);
|
||||||
}
|
}
|
||||||
System.out.println(System.currentTimeMillis() - start3 + "ms");
|
System.out.println(System.currentTimeMillis() - start3 + "ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import org.spongycastle.util.encoders.Hex;
|
||||||
* Created on: 11/06/2014 14:54
|
* Created on: 11/06/2014 14:54
|
||||||
*/
|
*/
|
||||||
public class TrackDatabaseTest {
|
public class TrackDatabaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test1() {
|
public void test1() {
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public class TrackDatabaseTest {
|
||||||
|
|
||||||
db1.close();
|
db1.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void destroyDB() {
|
public static void destroyDB() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -18,9 +18,9 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test file specific for tests maintained in the GitHub repository
|
* Test file specific for tests maintained in the GitHub repository
|
||||||
* by the Ethereum DEV team. <br/>
|
* by the Ethereum DEV team. <br/>
|
||||||
*
|
*
|
||||||
* @see <a href="https://github.com/ethereum/tests/">https://github.com/ethereum/tests/</a>
|
* @see <a href="https://github.com/ethereum/tests/">https://github.com/ethereum/tests/</a>
|
||||||
*/
|
*/
|
||||||
@RunWith(Suite.class)
|
@RunWith(Suite.class)
|
||||||
|
@ -31,7 +31,7 @@ public class GitHubJSONTestSuite {
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger("TCK-Test");
|
private static Logger logger = LoggerFactory.getLogger("TCK-Test");
|
||||||
|
|
||||||
|
|
||||||
protected static void runGitHubJsonTest(String json) throws ParseException {
|
protected static void runGitHubJsonTest(String json) throws ParseException {
|
||||||
Assume.assumeFalse("Online test is not available", json.equals(""));
|
Assume.assumeFalse("Online test is not available", json.equals(""));
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ public class GitHubJSONTestSuite {
|
||||||
while (testIterator.hasNext()){
|
while (testIterator.hasNext()){
|
||||||
|
|
||||||
TestCase testCase = testIterator.next();
|
TestCase testCase = testIterator.next();
|
||||||
|
|
||||||
TestRunner runner = new TestRunner();
|
TestRunner runner = new TestRunner();
|
||||||
List<String> result = runner.runTestCase(testCase);
|
List<String> result = runner.runTestCase(testCase);
|
||||||
Assert.assertTrue(result.isEmpty());
|
Assert.assertTrue(result.isEmpty());
|
||||||
|
|
|
@ -97,6 +97,6 @@ public class GitHubStateTest {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,56 +16,56 @@ public class GitHubVMTest {
|
||||||
String json = JSONReader.loadJSON("VMTests/vmArithmeticTest.json");
|
String json = JSONReader.loadJSON("VMTests/vmArithmeticTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testBitwiseLogicOperationFromGitHub() throws ParseException {
|
public void testBitwiseLogicOperationFromGitHub() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("VMTests/vmBitwiseLogicOperationTest.json");
|
String json = JSONReader.loadJSON("VMTests/vmBitwiseLogicOperationTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testBlockInfoFromGitHub() throws ParseException {
|
public void testBlockInfoFromGitHub() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("VMTests/vmBlockInfoTest.json");
|
String json = JSONReader.loadJSON("VMTests/vmBlockInfoTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testEnvironmentalInfoFromGitHub() throws ParseException {
|
public void testEnvironmentalInfoFromGitHub() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("VMTests/vmEnvironmentalInfoTest.json");
|
String json = JSONReader.loadJSON("VMTests/vmEnvironmentalInfoTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testIOandFlowOperationsFromGitHub() throws ParseException {
|
public void testIOandFlowOperationsFromGitHub() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("VMTests/vmIOandFlowOperationsTest.json");
|
String json = JSONReader.loadJSON("VMTests/vmIOandFlowOperationsTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testPushDupSwapFromGitHub() throws ParseException {
|
public void testPushDupSwapFromGitHub() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("VMTests/vmPushDupSwapTest.json");
|
String json = JSONReader.loadJSON("VMTests/vmPushDupSwapTest.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testShaFromGitHub() throws ParseException {
|
public void testShaFromGitHub() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("VMTests/vmSha3Test.json");
|
String json = JSONReader.loadJSON("VMTests/vmSha3Test.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testVMGitHub() throws ParseException {
|
public void testVMGitHub() throws ParseException {
|
||||||
|
|
||||||
String json = JSONReader.loadJSON("VMTests/vmtests.json");
|
String json = JSONReader.loadJSON("VMTests/vmtests.json");
|
||||||
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
GitHubJSONTestSuite.runGitHubJsonTest(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // testing full suite
|
@Test // testing full suite
|
||||||
public void testVMLogGitHub() throws ParseException {
|
public void testVMLogGitHub() throws ParseException {
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* Created on: 22/05/2014 09:26
|
* Created on: 22/05/2014 09:26
|
||||||
*/
|
*/
|
||||||
public class MinerThread implements Runnable {
|
public class MinerThread implements Runnable {
|
||||||
|
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue