├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── test │ ├── resources │ │ └── dsl │ │ │ ├── blocks01.txt │ │ │ ├── accounts01.txt │ │ │ ├── blockchain01.txt │ │ │ ├── blockchain02.txt │ │ │ ├── blockchain03.txt │ │ │ ├── transactions02.txt │ │ │ ├── transactions01.txt │ │ │ ├── blockchain04.txt │ │ │ ├── blockchain05.txt │ │ │ └── blockchain06.txt │ └── java │ │ └── com │ │ └── ajlopez │ │ └── blockchain │ │ ├── test │ │ ├── dsl │ │ │ ├── DslException.java │ │ │ ├── DslExpression.java │ │ │ ├── WorldDslProcessor.java │ │ │ ├── commands │ │ │ │ ├── DslConnectCommand.java │ │ │ │ ├── DslProcessCommand.java │ │ │ │ ├── DslTransactionCommand.java │ │ │ │ ├── DslAccountCommand.java │ │ │ │ ├── DslBlockCommand.java │ │ │ │ ├── DslBlockHeaderCommand.java │ │ │ │ └── DslAssertCommand.java │ │ │ ├── DslTerm.java │ │ │ └── WorldDslProcessorTest.java │ │ ├── BlockConsumer.java │ │ ├── BlocksConsumer.java │ │ └── simples │ │ │ ├── SimpleBlockValidator.java │ │ │ └── SimpleMessageChannel.java │ │ ├── math │ │ └── ec │ │ │ └── FieldTest.java │ │ ├── json │ │ ├── TokenTest.java │ │ ├── JsonNullValueTest.java │ │ ├── JsonNumericValueTest.java │ │ ├── JsonStringValueTest.java │ │ ├── JsonBooleanValueTest.java │ │ └── JsonArrayValueTest.java │ │ ├── config │ │ └── NetworkConfigurationTest.java │ │ ├── net │ │ ├── messages │ │ │ ├── GetBlockHashesMessageTest.java │ │ │ ├── GetBlockByNumberMessageTest.java │ │ │ ├── GetBlockByHashMessageTest.java │ │ │ ├── TransactionMessageTest.java │ │ │ ├── GetStoredValueMessageTest.java │ │ │ ├── GetTrieNodeMessageTest.java │ │ │ ├── TrieNodeMessageTest.java │ │ │ ├── StoredKeyValueMessageTest.java │ │ │ ├── StatusMessageTest.java │ │ │ └── BlockMessageTest.java │ │ ├── http │ │ │ └── HttpRequestTest.java │ │ └── peers │ │ │ ├── PeerTest.java │ │ │ ├── PacketOutputStreamTest.java │ │ │ └── MessageInputStreamTest.java │ │ ├── store │ │ ├── HashMapStoreTest.java │ │ ├── CodeStoreTest.java │ │ ├── DualKeyValueStoreTest.java │ │ └── KeyValueStoresTest.java │ │ ├── vms │ │ ├── newvm │ │ │ └── StorageTest.java │ │ └── eth │ │ │ └── BlockDataTest.java │ │ ├── bc │ │ ├── BlockInformationTest.java │ │ ├── WalletCreatorTest.java │ │ ├── ExtendedBlockInformationTest.java │ │ ├── BlockStoreTest.java │ │ └── WalletTest.java │ │ ├── jsonrpc │ │ ├── JsonRpcResponseTest.java │ │ ├── JsonRpcRequestTest.java │ │ └── TransactionsProviderTest.java │ │ ├── storage │ │ └── ChunkStoreTest.java │ │ ├── utils │ │ └── ByteArrayWrapperTest.java │ │ ├── state │ │ ├── TriePathTest.java │ │ ├── TrieNodeCounterVisitorTest.java │ │ └── TrieNodeStatsVisitorTest.java │ │ ├── processors │ │ ├── TransactionProcessorTest.java │ │ └── PeerProcessorTest.java │ │ └── encoding │ │ └── BlockInformationEncoderTest.java └── main │ └── java │ └── com │ └── ajlopez │ └── blockchain │ ├── store │ ├── TrieType.java │ ├── KeyValueStoreType.java │ ├── MemoryStores.java │ ├── KeyValueResolver.java │ ├── KeyValueStore.java │ ├── AccountStoreProvider.java │ ├── KeyInformation.java │ ├── CodeStore.java │ ├── HashMapStore.java │ ├── DualKeyValueStore.java │ ├── KeyValueStores.java │ ├── TrieStore.java │ ├── AccountStore.java │ ├── MemoryKeyValueStores.java │ └── RemoteKeyValueStore.java │ ├── json │ ├── TokenType.java │ ├── JsonValueType.java │ ├── JsonLexerException.java │ ├── JsonParserException.java │ ├── JsonBooleanValue.java │ ├── Token.java │ ├── JsonNumericValue.java │ ├── JsonNullValue.java │ ├── JsonArrayValue.java │ ├── JsonConverter.java │ ├── JsonValue.java │ ├── JsonStringValue.java │ ├── JsonObjectValue.java │ ├── JsonArrayBuilder.java │ ├── JsonBuilder.java │ └── JsonObjectBuilder.java │ ├── core │ ├── types │ │ ├── TransactionHash.java │ │ ├── BlockHash.java │ │ ├── Hash.java │ │ ├── Address.java │ │ ├── Coin.java │ │ ├── Bloom.java │ │ ├── AbstractBytesValue.java │ │ └── Difficulty.java │ ├── TransactionReceipt.java │ └── Account.java │ ├── jsonrpc │ ├── JsonRpcException.java │ ├── JsonRpcProcessor.java │ ├── AbstractJsonRpcProcessor.java │ ├── TopProcessor.java │ ├── AccountsProvider.java │ ├── WalletProcessor.java │ ├── BlocksProvider.java │ ├── TransactionsProvider.java │ └── NetworkProcessor.java │ ├── vms │ ├── eth │ │ ├── VirtualMachineException.java │ │ ├── Storage.java │ │ ├── FeeSchedule.java │ │ ├── TrieStorageProvider.java │ │ ├── Log.java │ │ ├── MapStorage.java │ │ ├── BlockData.java │ │ ├── TrieStorage.java │ │ └── ChildMapStorage.java │ └── newvm │ │ ├── VirtualMachineException.java │ │ ├── Storage.java │ │ ├── OpCodes.java │ │ └── Memory.java │ ├── net │ ├── PeerId.java │ ├── peers │ │ ├── Protocols.java │ │ ├── PeerNode.java │ │ ├── Packet.java │ │ ├── MessageInputStream.java │ │ ├── Peer.java │ │ ├── MessageOutputStream.java │ │ ├── PacketOutputStream.java │ │ └── TcpPeerClient.java │ ├── MessageChannel.java │ ├── messages │ │ ├── MessageType.java │ │ ├── GetBlockByHashMessage.java │ │ ├── GetStatusMessage.java │ │ ├── BlockMessage.java │ │ ├── Message.java │ │ ├── StatusMessage.java │ │ ├── GetBlockByNumberMessage.java │ │ ├── TransactionMessage.java │ │ ├── GetStoredValueMessage.java │ │ ├── StoredKeyValueMessage.java │ │ ├── GetTrieNodeMessage.java │ │ ├── TrieNodeMessage.java │ │ └── GetBlockHashesMessage.java │ ├── http │ │ ├── HttpRequest.java │ │ └── HttpRequestParser.java │ └── Status.java │ ├── db │ ├── ValueInfo.java │ └── ValueFile.java │ ├── math │ └── ec │ │ ├── Field.java │ │ └── Curve.java │ ├── config │ ├── NetworkConfiguration.java │ ├── NodeConfiguration.java │ └── MinerConfiguration.java │ ├── state │ ├── TrieNodeStatsVisitor.java │ ├── TrieNodeCounterVisitor.java │ ├── TrieHashCopierVisitor.java │ ├── TriePath.java │ ├── TrieNodeVisitor.java │ └── TrieHashVisitor.java │ ├── bc │ ├── Wallet.java │ ├── BlockProvider.java │ ├── BlockInformation.java │ ├── ExtendedBlockInformation.java │ ├── GenesisGenerator.java │ ├── BlockStore.java │ ├── WalletCreator.java │ └── ObjectContext.java │ ├── storage │ ├── Chunk.java │ ├── ChunkStore.java │ ├── Chunker.java │ └── StreamProcessor.java │ ├── processors │ ├── TransactionValidator.java │ ├── MessageTask.java │ ├── TransactionProcessor.java │ └── PeerProcessor.java │ ├── execution │ ├── AccountProvider.java │ ├── ExecutionContext.java │ ├── ChildExecutionContext.java │ └── BlockExecutionResult.java │ ├── utils │ └── ByteArrayWrapper.java │ ├── crypto │ └── SpongyCastleProvider.java │ └── encoding │ ├── BlockEncoder.java │ ├── BlockInformationEncoder.java │ ├── TransactionReceiptEncoder.java │ ├── StatusEncoder.java │ └── LogEncoder.java ├── .gitignore ├── trietest2.cmd ├── LICENSE └── trietest1.cmd /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'blockchainj' 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajlopez/BlockchainJ/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/test/resources/dsl/blocks01.txt: -------------------------------------------------------------------------------- 1 | 2 | block 3 | name=b1 4 | parent=genesis 5 | end 6 | 7 | assert b1.number == 1 8 | assert 1 == b1.number 9 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/TrieType.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | /** 4 | * Created by ajlopez on 07/06/2019. 5 | */ 6 | public enum TrieType { 7 | ACCOUNT, 8 | STORAGE 9 | } 10 | -------------------------------------------------------------------------------- /src/test/resources/dsl/accounts01.txt: -------------------------------------------------------------------------------- 1 | 2 | account 3 | name=acc1 4 | balance=1000 5 | end 6 | 7 | assert acc1.balance == 1000 8 | assert 1000 == acc1.balance 9 | 10 | assert acc1.nonce == 0 11 | assert 0 == acc1.nonce -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/TokenType.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 27/10/2018. 5 | */ 6 | public enum TokenType { 7 | NAME, 8 | STRING, 9 | NUMBER, 10 | SYMBOL 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonValueType.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 27/10/2018. 5 | */ 6 | public enum JsonValueType { 7 | STRING, 8 | NUMBER, 9 | BOOLEAN, 10 | ARRAY, 11 | OBJECT, 12 | NULL 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/KeyValueStoreType.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | /** 4 | * Created by ajlopez on 03/05/2020. 5 | */ 6 | public enum KeyValueStoreType { 7 | BLOCKS, 8 | BLOCKS_INFORMATION, 9 | CODES, 10 | ACCOUNTS, 11 | STORAGE 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/DslException.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl; 2 | 3 | /** 4 | * Created by ajlopez on 20/05/2019. 5 | */ 6 | public class DslException extends Exception { 7 | public DslException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/TransactionHash.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | /** 4 | * Created by ajlopez on 22/08/2019. 5 | */ 6 | public class TransactionHash extends Hash { 7 | public TransactionHash(byte[] bytes) { 8 | super(bytes); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonLexerException.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 27/10/2018. 5 | */ 6 | public class JsonLexerException extends Exception { 7 | public JsonLexerException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/JsonRpcException.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | /** 4 | * Created by ajlopez on 01/12/2018. 5 | */ 6 | public class JsonRpcException extends Exception { 7 | public JsonRpcException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonParserException.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 28/10/2018. 5 | */ 6 | public class JsonParserException extends Exception { 7 | public JsonParserException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/resources/dsl/blockchain01.txt: -------------------------------------------------------------------------------- 1 | 2 | block 3 | name=b1 4 | parent=genesis 5 | end 6 | 7 | connect b1 8 | 9 | assert blockchain.bestBlock.number == b1.number 10 | assert b1.number == blockchain.bestBlock.number 11 | assert blockchain.bestBlock.hash == b1.hash 12 | assert b1.hash == blockchain.bestBlock.hash 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/VirtualMachineException.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | /** 4 | * Created by ajlopez on 15/12/2018. 5 | */ 6 | public class VirtualMachineException extends Exception { 7 | public VirtualMachineException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/MemoryStores.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | /** 4 | * Created by Angel on 01/01/2020. 5 | */ 6 | public class MemoryStores extends Stores { 7 | 8 | // TODO remove usage 9 | public MemoryStores() { 10 | super(new MemoryKeyValueStores()); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/newvm/VirtualMachineException.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.newvm; 2 | 3 | /** 4 | * Created by ajlopez on 03/01/2018. 5 | */ 6 | public class VirtualMachineException extends Exception { 7 | public VirtualMachineException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/PeerId.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | 5 | /** 6 | * Created by ajlopez on 20/03/2018. 7 | */ 8 | public class PeerId extends Hash { 9 | public PeerId(byte[] bytes) { 10 | super(bytes); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/Protocols.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | /** 4 | * Created by ajlopez on 14/07/2019. 5 | */ 6 | 7 | public class Protocols { 8 | private Protocols() { } 9 | 10 | public static final short BASE = 0; 11 | public static final short BLOCKCHAIN = 1; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/JsonRpcProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Created by ajlopez on 30/11/2018. 7 | */ 8 | public interface JsonRpcProcessor { 9 | JsonRpcResponse processRequest(JsonRpcRequest request) throws JsonRpcException, IOException; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/PeerNode.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import com.ajlopez.blockchain.net.MessageChannel; 4 | import com.ajlopez.blockchain.net.Status; 5 | 6 | /** 7 | * Created by ajlopez on 19/11/2018. 8 | */ 9 | public interface PeerNode extends MessageChannel { 10 | Peer getPeer(); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/DslExpression.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl; 2 | 3 | import com.ajlopez.blockchain.test.World; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 11/12/2020. 9 | */ 10 | public interface DslExpression { 11 | Object evaluate(World world) throws IOException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/KeyValueResolver.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | /** 6 | * Created by ajlopez on 03/05/2020. 7 | */ 8 | public interface KeyValueResolver { 9 | void resolve(KeyValueStoreType storeType, byte[] key, CompletableFuture future); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/MessageChannel.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net; 2 | 3 | import com.ajlopez.blockchain.net.messages.Message; 4 | import com.ajlopez.blockchain.net.peers.Peer; 5 | 6 | /** 7 | * Created by ajlopez on 02/02/2018. 8 | */ 9 | public interface MessageChannel { 10 | void postMessage(Peer sender, Message message); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/KeyValueStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Created by ajlopez on 06/01/2018. 7 | */ 8 | public interface KeyValueStore { 9 | void setValue(byte[] key, byte[] value) throws IOException; 10 | 11 | byte[] getValue(byte[] key) throws IOException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/BlockHash.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | /** 4 | * Created by ajlopez on 22/03/2018. 5 | */ 6 | public class BlockHash extends Hash { 7 | public static BlockHash EMPTY_BLOCK_HASH = new BlockHash(new byte[HASH_BYTES]); 8 | 9 | public BlockHash(byte[] bytes) { 10 | super(bytes); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/db/ValueInfo.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.db; 2 | 3 | /** 4 | * Created by ajlopez on 20/10/2019. 5 | */ 6 | public class ValueInfo { 7 | public final long position; 8 | public final int length; 9 | 10 | public ValueInfo(long position, int length) { 11 | this.position = position; 12 | this.length = length; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/resources/dsl/blockchain02.txt: -------------------------------------------------------------------------------- 1 | 2 | block 3 | name=b1 4 | parent=genesis 5 | end 6 | 7 | block 8 | name=b2 9 | parent=b1 10 | end 11 | 12 | connect b1 13 | connect b2 14 | 15 | assert blockchain.bestBlock.number == b2.number 16 | assert b2.number == blockchain.bestBlock.number 17 | assert blockchain.bestBlock.hash == b2.hash 18 | assert b2.hash == blockchain.bestBlock.hash 19 | 20 | -------------------------------------------------------------------------------- /src/test/resources/dsl/blockchain03.txt: -------------------------------------------------------------------------------- 1 | 2 | block 3 | name=b1 4 | parent=genesis 5 | end 6 | 7 | block 8 | name=b2 9 | parent=b1 10 | end 11 | 12 | process b2 13 | process b1 14 | 15 | assert blockchain.bestBlock.number == b2.number 16 | assert b2.number == blockchain.bestBlock.number 17 | assert blockchain.bestBlock.hash == b2.hash 18 | assert b2.hash == blockchain.bestBlock.hash 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/math/ec/Field.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.math.ec; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * Created by ajlopez on 24/09/2020. 7 | */ 8 | public class Field { 9 | private final BigInteger prime; 10 | 11 | public Field(BigInteger prime) { 12 | this.prime = prime; 13 | } 14 | 15 | public BigInteger getPrime() { 16 | return this.prime; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/resources/dsl/transactions02.txt: -------------------------------------------------------------------------------- 1 | 2 | account 3 | name=acc1 4 | balance=1000 5 | end 6 | 7 | account 8 | name=acc2 9 | balance=100 10 | end 11 | 12 | transaction 13 | name=tx1 14 | from=acc1 15 | to=acc2 16 | value=500 17 | nonce=0 18 | end 19 | 20 | block 21 | name=b1 22 | transactions=tx1 23 | parent=genesis 24 | end 25 | 26 | connect b1 27 | 28 | assert acc1.balance = 500 29 | assert acc2.balance = 600 30 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/config/NetworkConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.config; 2 | 3 | /** 4 | * Created by ajlopez on 30/12/2018. 5 | */ 6 | public class NetworkConfiguration { 7 | private final short networkNumber; 8 | 9 | public NetworkConfiguration(short networkNumber) { 10 | this.networkNumber = networkNumber; 11 | } 12 | 13 | public short getNetworkNumber() { 14 | return this.networkNumber; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/MessageType.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | /** 4 | * Created by ajlopez on 19/01/2018. 5 | */ 6 | public enum MessageType { 7 | STATUS, 8 | TRANSACTION, 9 | BLOCK, 10 | GET_BLOCK_BY_HASH, 11 | GET_BLOCK_BY_NUMBER, 12 | TRIE_NODE, 13 | GET_TRIE_NODE, 14 | GET_STORED_VALUE, 15 | STORED_KEY_VALUE, 16 | GET_BLOCK_HASHES, 17 | GET_STATUS 18 | } 19 | -------------------------------------------------------------------------------- /src/test/resources/dsl/transactions01.txt: -------------------------------------------------------------------------------- 1 | 2 | account 3 | name=acc1 4 | balance=1000 5 | end 6 | 7 | account 8 | name=acc2 9 | balance=100 10 | end 11 | 12 | transaction 13 | name=tx1 14 | from=acc1 15 | to=acc2 16 | value=500 17 | nonce=42 18 | end 19 | 20 | assert tx1.value == 500 21 | assert 500 == tx1.value 22 | assert tx1.nonce == 42 23 | assert 42 == tx1.nonce 24 | 25 | assert acc1 == tx1.from 26 | assert acc2 == tx1.to 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/math/ec/FieldTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.math.ec; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.math.BigInteger; 7 | 8 | /** 9 | * Created by ajlopez on 24/09/2020. 10 | */ 11 | public class FieldTest { 12 | @Test 13 | public void createField() { 14 | Field field = new Field(BigInteger.valueOf(7)); 15 | 16 | Assert.assertEquals(BigInteger.valueOf(7), field.getPrime()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/json/TokenTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 27/10/2018. 8 | */ 9 | public class TokenTest { 10 | @Test 11 | public void createToken() { 12 | Token token = new Token(TokenType.NAME, "adam"); 13 | 14 | Assert.assertEquals(TokenType.NAME, token.getType()); 15 | Assert.assertEquals("adam", token.getValue()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonBooleanValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 27/10/2018. 5 | */ 6 | public class JsonBooleanValue extends JsonValue { 7 | public JsonBooleanValue(boolean value) { 8 | super(JsonValueType.BOOLEAN, value); 9 | } 10 | 11 | @Override 12 | public String toString() { 13 | if (this.getValue().equals(true)) 14 | return "true"; 15 | 16 | return "false"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/AbstractJsonRpcProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Created by ajlopez on 01/12/2018. 7 | */ 8 | public abstract class AbstractJsonRpcProcessor implements JsonRpcProcessor { 9 | public JsonRpcResponse processRequest(JsonRpcRequest request) throws JsonRpcException, IOException { 10 | throw new JsonRpcException(String.format("Unknown method '%s'", request.getMethod())); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/state/TrieNodeStatsVisitor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | /** 4 | * Created by ajlopez on 22/10/2020. 5 | */ 6 | public class TrieNodeStatsVisitor extends TrieNodeCounterVisitor { 7 | private long encodedSize; 8 | 9 | @Override 10 | public void processNode(Trie trie) { 11 | super.processNode(trie); 12 | 13 | encodedSize += trie.getEncoded().length; 14 | } 15 | 16 | public long getEncodedSize() { return this.encodedSize; } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/config/NetworkConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.config; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 30/12/2018. 8 | */ 9 | public class NetworkConfigurationTest { 10 | @Test 11 | public void simpleCreationTest() { 12 | NetworkConfiguration networkConfiguration = new NetworkConfiguration((short)42); 13 | 14 | Assert.assertEquals(42, networkConfiguration.getNetworkNumber()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/Token.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 27/10/2018. 5 | */ 6 | public class Token { 7 | private TokenType type; 8 | private String value; 9 | 10 | public Token(TokenType type, String value) { 11 | this.type = type; 12 | this.value = value; 13 | } 14 | 15 | public TokenType getType() { 16 | return this.type; 17 | } 18 | 19 | public String getValue() { 20 | return this.value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/Storage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.DataWord; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 09/12/2018. 9 | */ 10 | 11 | public interface Storage { 12 | boolean hasValue(DataWord address) throws IOException; 13 | 14 | void setValue(DataWord address, DataWord value) throws IOException; 15 | 16 | DataWord getValue(DataWord address) throws IOException; 17 | 18 | void commit() throws IOException; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonNumericValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 27/10/2018. 5 | */ 6 | public class JsonNumericValue extends JsonValue { 7 | public JsonNumericValue(String value) { 8 | super(JsonValueType.NUMBER, value); 9 | } 10 | 11 | public JsonNumericValue(Integer value) { 12 | super(JsonValueType.NUMBER, value.toString()); 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return (String)this.getValue(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/BlockConsumer.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * Created by ajlopez on 20/02/2018. 9 | */ 10 | public class BlockConsumer implements Consumer { 11 | private Block block; 12 | 13 | @Override 14 | public void accept(Block block) { 15 | this.block = block; 16 | } 17 | 18 | public Block getBlock() { 19 | return this.block; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/Wallet.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by ajlopez on 24/03/2021. 10 | */ 11 | public class Wallet { 12 | private final List
addresses = new ArrayList<>(); 13 | 14 | public void addAddress(Address address) { 15 | this.addresses.add(address); 16 | } 17 | 18 | public List
getAddresses() { 19 | return this.addresses; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonNullValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 23/04/2019. 5 | */ 6 | public class JsonNullValue extends JsonValue { 7 | private static final JsonNullValue instance = new JsonNullValue(); 8 | 9 | private JsonNullValue() { 10 | super(JsonValueType.NULL, null); 11 | } 12 | 13 | public static JsonNullValue getInstance() { 14 | return instance; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return "null"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/BlockProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.types.BlockHash; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 17/07/2019. 10 | */ 11 | public interface BlockProvider { 12 | Block getBlockByHash(BlockHash blockHash) throws IOException; 13 | 14 | Block getBlockByNumber(long number) throws IOException; 15 | 16 | BlockInformation getBlockInformation(long number, BlockHash blockHash) throws IOException; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/json/JsonNullValueTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by Angel on 4/23/2019. 8 | */ 9 | public class JsonNullValueTest { 10 | @Test 11 | public void createJsonNullValue() { 12 | JsonValue jsonValue = JsonNullValue.getInstance(); 13 | 14 | Assert.assertEquals(JsonValueType.NULL, jsonValue.getType()); 15 | Assert.assertNull(jsonValue.getValue()); 16 | Assert.assertEquals("null", jsonValue.toString()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/newvm/Storage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.newvm; 2 | 3 | import com.ajlopez.blockchain.state.Trie; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 20/11/2017. 9 | */ 10 | public class Storage { 11 | private Trie trie = new Trie(); 12 | 13 | public byte[] getValue(byte[] address) throws IOException { 14 | return this.trie.get(address); 15 | } 16 | 17 | public void setValue(byte[] address, byte[] value) throws IOException { 18 | this.trie = this.trie.put(address, value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/storage/Chunk.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.storage; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.utils.HashUtils; 5 | 6 | /** 7 | * Created by ajlopez on 12/08/2019. 8 | */ 9 | public class Chunk { 10 | private final Hash hash; 11 | private byte[] data; 12 | 13 | public Chunk(byte[] data) { 14 | this.data = data; 15 | this.hash = HashUtils.calculateHash(data); 16 | } 17 | 18 | public Hash getHash() { return this.hash; } 19 | 20 | public byte[] getData() { return this.data; } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/processors/TransactionValidator.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.processors; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | 5 | /** 6 | * Created by ajlopez on 01/07/2020. 7 | */ 8 | public class TransactionValidator { 9 | public boolean isValid(Transaction transaction) { 10 | if (transaction.isRichTransaction()) { 11 | if (transaction.getData() == null) 12 | return false; 13 | 14 | if (!transaction.getValue().isZero()) 15 | return false; 16 | } 17 | 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # local credentials 2 | credentials.properties 3 | 4 | out 5 | 6 | # Vim 7 | *.sw[op] 8 | 9 | # Gradle 10 | .gradle 11 | build 12 | 13 | # IDEA 14 | .idea 15 | !.idea/codeStyleSettings.xml 16 | *.iml 17 | *.iws 18 | 19 | # Eclipse 20 | .project 21 | .classpath 22 | .settings/ 23 | bin 24 | 25 | # Maven 26 | *.log 27 | log/ 28 | target/ 29 | 30 | # Output 31 | 32 | # Mac 33 | .DS_Store 34 | 35 | 36 | # Local configuration 37 | **/user.conf 38 | **/test-user.conf 39 | **/out.log 40 | **/user.log4j.properties 41 | 42 | #Classes 43 | classes/ 44 | 45 | *.data 46 | *.values 47 | *.keys 48 | 49 | trietest*.txt 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/state/TrieNodeCounterVisitor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | /** 4 | * Created by ajlopez on 21/10/2020. 5 | */ 6 | public class TrieNodeCounterVisitor extends TrieNodeVisitor { 7 | private int nodeCounter; 8 | private int valueCounter; 9 | 10 | @Override 11 | public void processNode(Trie trie) { 12 | this.nodeCounter++; 13 | 14 | if (trie.hasValue()) 15 | this.valueCounter++; 16 | } 17 | 18 | public int getNodeCounter() { return this.nodeCounter; } 19 | 20 | public int getValueCounter() { return this.valueCounter; } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/BlocksConsumer.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.function.Consumer; 8 | 9 | /** 10 | * Created by ajlopez on 02/05/2020. 11 | */ 12 | public class BlocksConsumer implements Consumer { 13 | private final List blocks = new ArrayList<>(); 14 | 15 | @Override 16 | public void accept(Block block) { 17 | this.blocks.add(block); 18 | } 19 | 20 | public List getBlocks() { 21 | return this.blocks; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/config/NodeConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.config; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by ajlopez on 01/05/2021. 7 | */ 8 | public class NodeConfiguration { 9 | private final int port; 10 | private final List hosts; 11 | 12 | public NodeConfiguration(int port, List hosts) { 13 | this.port = port; 14 | this.hosts = hosts; 15 | } 16 | 17 | public boolean isTcpServer() { return this.port > 0; } 18 | 19 | public int getPort() { return this.port; } 20 | 21 | public List getHosts() { return this.hosts; } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/AccountStoreProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 01/12/2018. 9 | */ 10 | public class AccountStoreProvider { 11 | private final TrieStore accountTrieStore; 12 | 13 | public AccountStoreProvider(TrieStore accountTrieStore) { 14 | this.accountTrieStore = accountTrieStore; 15 | } 16 | 17 | public AccountStore retrieve(Hash hash) throws IOException { 18 | return new AccountStore(this.accountTrieStore.retrieve(hash)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/GetBlockHashesMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class GetBlockHashesMessageTest { 7 | @Test 8 | public void createMessage() { 9 | GetBlockHashesMessage message = new GetBlockHashesMessage(42, 10, 2); 10 | 11 | Assert.assertEquals(MessageType.GET_BLOCK_HASHES, message.getMessageType()); 12 | Assert.assertEquals(42, message.getBlockHeight()); 13 | Assert.assertEquals(10, message.getNoBlocks()); 14 | Assert.assertEquals(2, message.getBlockGap()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/simples/SimpleBlockValidator.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.simples; 2 | 3 | import com.ajlopez.blockchain.bc.BlockValidator; 4 | import com.ajlopez.blockchain.core.Block; 5 | 6 | /** 7 | * Created by ajlopez on 05/01/2021. 8 | */ 9 | public class SimpleBlockValidator extends BlockValidator { 10 | public SimpleBlockValidator() { 11 | super(null); 12 | } 13 | 14 | @Override 15 | public boolean isValid(Block block) { 16 | return true; 17 | } 18 | 19 | @Override 20 | public boolean isValid(Block block, Block parent) { 21 | return true; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/dsl/blockchain04.txt: -------------------------------------------------------------------------------- 1 | 2 | block 3 | name=b1 4 | parent=genesis 5 | end 6 | 7 | block 8 | name=b1b 9 | parent=genesis 10 | end 11 | 12 | block 13 | name=b1c 14 | parent=genesis 15 | end 16 | 17 | block 18 | name=b2 19 | parent=b1 20 | end 21 | 22 | block 23 | name=b2plus 24 | uncles=b1b,b1c 25 | parent=b1 26 | end 27 | 28 | connect b1 29 | connect b2 30 | connect b2plus 31 | 32 | assert blockchain.bestBlock.number == b2plus.number 33 | assert b2plus.number == blockchain.bestBlock.number 34 | assert blockchain.bestBlock.hash == b2plus.hash 35 | assert b2plus.hash == blockchain.bestBlock.hash 36 | 37 | -------------------------------------------------------------------------------- /src/test/resources/dsl/blockchain05.txt: -------------------------------------------------------------------------------- 1 | 2 | block 3 | name=b1 4 | parent=genesis 5 | end 6 | 7 | block 8 | name=b1b 9 | parent=genesis 10 | end 11 | 12 | block 13 | name=b1c 14 | parent=genesis 15 | end 16 | 17 | block 18 | name=b2 19 | parent=b1 20 | end 21 | 22 | block 23 | name=b2plus 24 | uncles=b1b,b1c 25 | parent=b1 26 | end 27 | 28 | process b2 29 | process b2plus 30 | process b1 31 | 32 | assert blockchain.bestBlock.number == b2plus.number 33 | assert b2plus.number == blockchain.bestBlock.number 34 | assert blockchain.bestBlock.hash == b2plus.hash 35 | assert b2plus.hash == blockchain.bestBlock.hash 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/Packet.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | /** 4 | * Created by ajlopez on 14/07/2019. 5 | */ 6 | public class Packet { 7 | private final short protocol; 8 | private final short network; 9 | private final byte[] bytes; 10 | 11 | public Packet(short protocol, short network, byte[] bytes) { 12 | this.protocol = protocol; 13 | this.network = network; 14 | this.bytes = bytes; 15 | } 16 | 17 | public short getProtocol() { return this.protocol; } 18 | 19 | public short getNetwork() { return this.network; } 20 | 21 | public byte[] getBytes() { return this.bytes; } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonArrayValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by ajlopez on 29/10/2018. 7 | */ 8 | public class JsonArrayValue extends JsonValue { 9 | private List values; 10 | 11 | public JsonArrayValue(List values) { 12 | super(JsonValueType.ARRAY, values); 13 | this.values = values; 14 | } 15 | 16 | public int size() { 17 | return this.values.size(); 18 | } 19 | 20 | public JsonValue getValue(int index) { 21 | return this.values.get(index); 22 | } 23 | 24 | public List getValues() { return this.values; } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/GetBlockByHashMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.types.BlockHash; 4 | 5 | /** 6 | * Created by ajlopez on 29/01/2018. 7 | */ 8 | public class GetBlockByHashMessage extends Message { 9 | private BlockHash hash; 10 | 11 | public GetBlockByHashMessage(BlockHash hash) { 12 | super(MessageType.GET_BLOCK_BY_HASH); 13 | this.hash = hash; 14 | } 15 | 16 | public BlockHash getHash() { 17 | return this.hash; 18 | } 19 | 20 | @Override 21 | public byte[] getPayload() { 22 | return this.hash.getBytes(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/GetStatusMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.encoding.StatusEncoder; 4 | import com.ajlopez.blockchain.net.Status; 5 | 6 | /** 7 | * Created by ajlopez on 01/05/2020. 8 | */ 9 | public class GetStatusMessage extends Message { 10 | private static GetStatusMessage instance = new GetStatusMessage(); 11 | 12 | public static GetStatusMessage getInstance() { 13 | return instance; 14 | } 15 | 16 | private GetStatusMessage() { 17 | super(MessageType.GET_STATUS); 18 | } 19 | 20 | @Override 21 | public byte[] getPayload() { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/KeyInformation.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | 5 | import java.security.Key; 6 | 7 | /** 8 | * Created by ajlopez on 22/07/2020. 9 | */ 10 | public class KeyInformation { 11 | private final KeyValueStoreType keyValueStoreType; 12 | private final Hash hash; 13 | 14 | public KeyInformation(KeyValueStoreType keyValueStoreType, Hash hash) { 15 | this.keyValueStoreType = keyValueStoreType; 16 | this.hash = hash; 17 | } 18 | 19 | public KeyValueStoreType getKeyValueStoreType() { return this.keyValueStoreType; } 20 | 21 | public Hash getHash() { return this.hash; } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/BlockMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.encoding.BlockEncoder; 5 | 6 | /** 7 | * Created by ajlopez on 19/01/2018. 8 | */ 9 | public class BlockMessage extends Message { 10 | private Block block; 11 | 12 | public BlockMessage(Block block) { 13 | super(MessageType.BLOCK); 14 | this.block = block; 15 | } 16 | 17 | public Block getBlock() { 18 | return this.block; 19 | } 20 | 21 | @Override 22 | public byte[] getPayload() { 23 | return BlockEncoder.encode(this.block); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/Message.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | /** 4 | * Created by ajlopez on 19/01/2018. 5 | */ 6 | public abstract class Message { 7 | private final MessageType type; 8 | private byte[] payload; 9 | 10 | public Message(MessageType type) { 11 | this.type = type; 12 | } 13 | 14 | public MessageType getMessageType() { 15 | return this.type; 16 | } 17 | 18 | public byte[] getMessagePayload() { 19 | if (this.payload == null) 20 | this.payload = this.getPayload(); 21 | 22 | return this.payload; 23 | } 24 | 25 | public abstract byte[] getPayload(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/processors/MessageTask.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.processors; 2 | 3 | import com.ajlopez.blockchain.net.peers.Peer; 4 | import com.ajlopez.blockchain.net.messages.Message; 5 | 6 | /** 7 | * Created by ajlopez on 06/02/2018. 8 | */ 9 | public class MessageTask { 10 | private final Message message; 11 | private final Peer sender; 12 | 13 | public MessageTask(Message message, Peer sender) { 14 | this.message = message; 15 | this.sender = sender; 16 | } 17 | 18 | public Message getMessage() { 19 | return this.message; 20 | } 21 | 22 | public Peer getSender() { 23 | return this.sender; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/StatusMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.encoding.StatusEncoder; 4 | import com.ajlopez.blockchain.net.Status; 5 | 6 | /** 7 | * Created by ajlopez on 04/02/2018. 8 | */ 9 | public class StatusMessage extends Message { 10 | private Status status; 11 | 12 | public StatusMessage(Status status) { 13 | super(MessageType.STATUS); 14 | this.status = status; 15 | } 16 | 17 | public Status getStatus() { 18 | return this.status; 19 | } 20 | 21 | @Override 22 | public byte[] getPayload() { 23 | return StatusEncoder.encode(this.status); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/WorldDslProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl; 2 | 3 | import com.ajlopez.blockchain.test.World; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 19/12/2020. 9 | */ 10 | public class WorldDslProcessor { 11 | private final World world; 12 | 13 | public WorldDslProcessor(World world) { 14 | this.world = world; 15 | } 16 | 17 | public World getWorld() { 18 | return this.world; 19 | } 20 | 21 | public void processCommands(DslParser parser) throws IOException, DslException { 22 | for (DslCommand cmd = parser.parse(); cmd != null; cmd = parser.parse()) 23 | cmd.execute(this.world); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/json/JsonNumericValueTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 27/10/2018. 8 | */ 9 | public class JsonNumericValueTest { 10 | @Test 11 | public void createNumericValue() { 12 | JsonNumericValue value = new JsonNumericValue("42"); 13 | 14 | Assert.assertEquals(JsonValueType.NUMBER, value.getType()); 15 | Assert.assertEquals("42", value.getValue()); 16 | } 17 | 18 | @Test 19 | public void simpleNumericValueToString() { 20 | JsonNumericValue value = new JsonNumericValue("42"); 21 | 22 | Assert.assertEquals("42", value.toString()); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/BlockInformation.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.types.BlockHash; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | 6 | /** 7 | * Created by ajlopez on 09/03/2020. 8 | */ 9 | public class BlockInformation { 10 | private final BlockHash blockHash; 11 | private final Difficulty totalDifficulty; 12 | 13 | public BlockInformation(BlockHash blockHash, Difficulty totalDifficulty) { 14 | this.blockHash = blockHash; 15 | this.totalDifficulty = totalDifficulty; 16 | } 17 | 18 | public BlockHash getBlockHash() { return this.blockHash; } 19 | 20 | public Difficulty getTotalDifficulty() { return this.totalDifficulty; } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/CodeStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.state.Trie; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 21/01/2019. 10 | */ 11 | public class CodeStore { 12 | private final KeyValueStore store; 13 | 14 | public CodeStore(KeyValueStore store) { 15 | this.store = store; 16 | } 17 | 18 | public byte[] getCode(Hash codeHash) throws IOException { 19 | return this.store.getValue(codeHash.getBytes()); 20 | } 21 | 22 | public void putCode(Hash codeHash, byte[] code) throws IOException { 23 | this.store.setValue(codeHash.getBytes(), code); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/GetBlockByNumberMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.utils.ByteUtils; 4 | 5 | /** 6 | * Created by ajlopez on 30/01/2018. 7 | */ 8 | public class GetBlockByNumberMessage extends Message { 9 | private long number; 10 | 11 | public GetBlockByNumberMessage(long number) { 12 | super(MessageType.GET_BLOCK_BY_NUMBER); 13 | this.number = number; 14 | } 15 | 16 | public long getNumber() { 17 | return ByteUtils.bytesToUnsignedLong(this.getPayload()); 18 | } 19 | 20 | @Override 21 | public byte[] getPayload() { 22 | return ByteUtils.unsignedLongToNormalizedBytes(this.number); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/HashMapStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.utils.ByteArrayWrapper; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * Created by ajlopez on 06/01/2018. 10 | */ 11 | public class HashMapStore implements KeyValueStore { 12 | private Map values = new HashMap<>(); 13 | 14 | public byte[] getValue(byte[] key) { 15 | return this.values.get(new ByteArrayWrapper(key)); 16 | } 17 | 18 | public void setValue(byte[] key, byte[] value) { 19 | this.values.put(new ByteArrayWrapper(key), value); 20 | } 21 | 22 | public boolean isEmpty() { return this.values.isEmpty(); } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/GetBlockByNumberMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.utils.ByteUtils; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | /** 8 | * Created by ajlopez on 30/01/2018. 9 | */ 10 | public class GetBlockByNumberMessageTest { 11 | @Test 12 | public void createWithNumber() { 13 | GetBlockByNumberMessage message = new GetBlockByNumberMessage(12345678L); 14 | 15 | Assert.assertEquals(MessageType.GET_BLOCK_BY_NUMBER, message.getMessageType()); 16 | Assert.assertEquals(12345678L, message.getNumber()); 17 | Assert.assertArrayEquals(ByteUtils.unsignedLongToNormalizedBytes(12345678L), message.getPayload()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/state/TrieHashCopierVisitor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | import com.ajlopez.blockchain.store.TrieStore; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 24/10/2020. 9 | */ 10 | public class TrieHashCopierVisitor extends TrieHashVisitor { 11 | private final TrieStore targetTrieStore; 12 | 13 | public TrieHashCopierVisitor(TrieStore trieStore, TrieStore targetTrieStore) { 14 | super(trieStore); 15 | this.targetTrieStore = targetTrieStore; 16 | } 17 | 18 | @Override 19 | public void processNode(Trie trie) throws IOException { 20 | if (this.targetTrieStore.exists(trie.getHash())) 21 | return; 22 | 23 | this.targetTrieStore.save(trie); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/http/HttpRequestTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.http; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.io.Reader; 7 | import java.io.StringReader; 8 | 9 | /** 10 | * Created by ajlopez on 03/12/2018. 11 | */ 12 | public class HttpRequestTest { 13 | @Test 14 | public void createRequest() { 15 | String method = "POST"; 16 | String resource = "/"; 17 | Reader reader = new StringReader("foo"); 18 | 19 | HttpRequest request = new HttpRequest(method, resource, null, reader); 20 | 21 | Assert.assertEquals(method, request.getMethod()); 22 | Assert.assertEquals(resource, request.getResource()); 23 | Assert.assertEquals(reader, request.getReader()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/TransactionMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | import com.ajlopez.blockchain.encoding.TransactionEncoder; 5 | 6 | /** 7 | * Created by ajlopez on 20/01/2018. 8 | */ 9 | public class TransactionMessage extends Message { 10 | private Transaction transaction; 11 | 12 | public TransactionMessage(Transaction transaction) { 13 | super(MessageType.TRANSACTION); 14 | this.transaction = transaction; 15 | } 16 | 17 | public Transaction getTransaction() { 18 | return this.transaction; 19 | } 20 | 21 | @Override 22 | public byte[] getPayload() { 23 | return TransactionEncoder.encode(this.transaction); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/commands/DslConnectCommand.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl.commands; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.test.World; 5 | import com.ajlopez.blockchain.test.dsl.DslCommand; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by ajlopez on 04/03/2021. 12 | */ 13 | public class DslConnectCommand extends DslCommand { 14 | public DslConnectCommand(List arguments) { 15 | super("block", arguments); 16 | } 17 | 18 | @Override 19 | public void execute(World world) throws IOException { 20 | String name = this.getName(0, "name"); 21 | Block block = world.getBlock(name); 22 | world.getBlockChain().connectBlock(block); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/commands/DslProcessCommand.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl.commands; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.test.World; 5 | import com.ajlopez.blockchain.test.dsl.DslCommand; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by ajlopez on 06/03/2021. 12 | */ 13 | public class DslProcessCommand extends DslCommand { 14 | public DslProcessCommand(List arguments) { 15 | super("block", arguments); 16 | } 17 | 18 | @Override 19 | public void execute(World world) throws IOException { 20 | String name = this.getName(0, "name"); 21 | Block block = world.getBlock(name); 22 | world.getBlockProcessor().processBlock(block); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/ExtendedBlockInformation.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | 6 | /** 7 | * Created by ajlopez on 11/07/2020. 8 | */ 9 | public class ExtendedBlockInformation extends BlockInformation { 10 | private final Block block; 11 | private final long blockNumber; 12 | 13 | public ExtendedBlockInformation(Block block, Difficulty totalDifficulty) { 14 | super(block.getHash(), totalDifficulty); 15 | 16 | this.block = block; 17 | this.blockNumber = block.getNumber(); 18 | } 19 | 20 | public Block getBlock() { 21 | return this.block; 22 | } 23 | 24 | public long getBlockNumber() { 25 | return this.blockNumber; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/FeeSchedule.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | /** 4 | * Created by ajlopez on 05/01/2019. 5 | */ 6 | public enum FeeSchedule { 7 | ZERO(0), 8 | JUMPDEST(1), 9 | BASE(2), 10 | VERYLOW(3), 11 | LOW(5), 12 | MID(8), 13 | HIGH(10), 14 | EXT(20), 15 | SLOAD(50), 16 | SSET(20000), 17 | SRESET(5000), 18 | SCLEAR(15000), 19 | TRANSFER(21000), 20 | DATAZERO(4), 21 | DATANONZERO(68), 22 | EXTCODESIZE(700), 23 | EXTCODEHASH(400), 24 | BALANCE(400), 25 | CODEDEPOSIT(200), 26 | CREATION(32000); 27 | 28 | private long value; 29 | 30 | private FeeSchedule(long value) { 31 | this.value = value; 32 | } 33 | 34 | public long getValue() { 35 | return this.value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/store/HashMapStoreTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 06/01/2018. 8 | */ 9 | public class HashMapStoreTest { 10 | @Test 11 | public void getNullForUndefinedKey() { 12 | HashMapStore store = new HashMapStore(); 13 | 14 | Assert.assertNull(store.getValue(new byte[12])); 15 | } 16 | 17 | @Test 18 | public void setAndGetValue() { 19 | HashMapStore store = new HashMapStore(); 20 | 21 | byte[] key = new byte[] { 0x01, 0x02, 0x03 }; 22 | byte[] value = new byte[] { 0x04, 0x05, 0x06 }; 23 | 24 | store.setValue(key, value); 25 | Assert.assertArrayEquals(value, store.getValue(key)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/Hash.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | import com.ajlopez.blockchain.utils.ByteUtils; 4 | import com.ajlopez.blockchain.utils.HashUtils; 5 | 6 | /** 7 | * Created by ajlopez on 12/08/2017. 8 | */ 9 | public class Hash extends AbstractBytesValue { 10 | public static final int HASH_BYTES = 32; 11 | public static final Hash EMPTY_BYTES_HASH = HashUtils.calculateHash(ByteUtils.EMPTY_BYTE_ARRAY); 12 | 13 | public Hash(byte[] bytes) { 14 | super(bytes, HASH_BYTES); 15 | } 16 | 17 | public long asLong() { 18 | return ByteUtils.bytesToLong(this.bytes, HASH_BYTES - Long.BYTES); 19 | } 20 | 21 | public int asInteger() { 22 | return ByteUtils.bytesToInteger(this.bytes, HASH_BYTES - Integer.BYTES); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/execution/AccountProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.execution; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.core.types.Coin; 5 | import com.ajlopez.blockchain.core.types.Hash; 6 | import com.ajlopez.blockchain.vms.eth.Storage; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by ajlopez on 30/04/2019. 12 | */ 13 | public interface AccountProvider { 14 | byte[] getCode(Address address) throws IOException; 15 | 16 | long getCodeLength(Address address) throws IOException; 17 | 18 | Hash getCodeHash(Address address) throws IOException; 19 | 20 | Coin getBalance(Address address) throws IOException; 21 | 22 | long getNonce(Address address) throws IOException; 23 | 24 | Storage getAccountStorage(Address address) throws IOException; 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/GetBlockByHashMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.types.BlockHash; 4 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | /** 9 | * Created by ajlopez on 29/01/2018. 10 | */ 11 | public class GetBlockByHashMessageTest { 12 | @Test 13 | public void createWithHash() { 14 | BlockHash hash = FactoryHelper.createRandomBlockHash(); 15 | 16 | GetBlockByHashMessage message = new GetBlockByHashMessage(hash); 17 | 18 | Assert.assertEquals(MessageType.GET_BLOCK_BY_HASH, message.getMessageType()); 19 | Assert.assertArrayEquals(hash.getBytes(), message.getPayload()); 20 | Assert.assertEquals(hash, message.getHash()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/TransactionMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | import com.ajlopez.blockchain.encoding.TransactionEncoder; 5 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Created by ajlopez on 20/01/2018. 11 | */ 12 | public class TransactionMessageTest { 13 | @Test 14 | public void createWithTransaction() { 15 | Transaction tx = FactoryHelper.createTransaction(42); 16 | 17 | TransactionMessage message = new TransactionMessage(tx); 18 | 19 | Assert.assertEquals(MessageType.TRANSACTION, message.getMessageType()); 20 | Assert.assertArrayEquals(TransactionEncoder.encode(tx), message.getPayload()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/vms/newvm/StorageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.newvm; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 20/11/2017. 10 | */ 11 | public class StorageTest { 12 | @Test 13 | public void getUndefinedValue() throws IOException { 14 | Storage storage = new Storage(); 15 | 16 | Assert.assertNull(storage.getValue(new byte[] { 0x01, 0x02, 0x03 })); 17 | } 18 | 19 | @Test 20 | public void setAndGetValue() throws IOException { 21 | byte[] key = new byte[] { 0x01, 0x02, 0x03 }; 22 | byte[] value = new byte[] { 0x04, 0x05 }; 23 | 24 | Storage storage = new Storage(); 25 | 26 | storage.setValue(key, value); 27 | Assert.assertArrayEquals(value, storage.getValue(key)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/storage/ChunkStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.storage; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.store.KeyValueStore; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 13/08/2019. 10 | */ 11 | public class ChunkStore { 12 | private final KeyValueStore store; 13 | 14 | public ChunkStore(KeyValueStore store) { 15 | this.store = store; 16 | } 17 | 18 | public Chunk getChunk(Hash hash) throws IOException { 19 | byte[] data = this.store.getValue(hash.getBytes()); 20 | 21 | if (data == null) 22 | return null; 23 | 24 | return new Chunk(data); 25 | } 26 | 27 | public void saveChunk(Chunk chunk) throws IOException { 28 | this.store.setValue(chunk.getHash().getBytes(), chunk.getData()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/utils/ByteArrayWrapper.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.utils; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Created by ajlopez on 21/11/2017. 7 | */ 8 | public class ByteArrayWrapper { 9 | private final byte[] bytes; 10 | private final int hashCode; 11 | 12 | public ByteArrayWrapper(byte[] bytes) { 13 | this.bytes = bytes; 14 | this.hashCode = Arrays.hashCode(bytes); 15 | } 16 | 17 | public byte[] getBytes() { 18 | return this.bytes; 19 | } 20 | 21 | @Override 22 | public int hashCode() { 23 | return this.hashCode; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object obj) { 28 | if (!(obj instanceof ByteArrayWrapper)) 29 | return false; 30 | 31 | return Arrays.equals(this.bytes, ((ByteArrayWrapper)obj).bytes); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/bc/BlockInformationTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.types.BlockHash; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Created by ajlopez on 09/03/2020. 11 | */ 12 | public class BlockInformationTest { 13 | @Test 14 | public void simpleCreate() { 15 | BlockHash blockHash = FactoryHelper.createRandomBlockHash(); 16 | Difficulty totalDifficulty = Difficulty.fromUnsignedLong(42); 17 | 18 | BlockInformation blockInformation = new BlockInformation(blockHash, totalDifficulty); 19 | 20 | Assert.assertEquals(blockHash, blockInformation.getBlockHash()); 21 | Assert.assertEquals(totalDifficulty, blockInformation.getTotalDifficulty()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/GetStoredValueMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.store.KeyValueStoreType; 4 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | /** 9 | * Created by ajlopez on 03/05/2020. 10 | */ 11 | public class GetStoredValueMessageTest { 12 | @Test 13 | public void createMessage() { 14 | KeyValueStoreType storeType = KeyValueStoreType.BLOCKS; 15 | byte[] key = FactoryHelper.createRandomBytes(32); 16 | 17 | GetStoredValueMessage message = new GetStoredValueMessage(storeType, key); 18 | 19 | Assert.assertEquals(MessageType.GET_STORED_VALUE, message.getMessageType()); 20 | Assert.assertEquals(storeType, message.getStoreType()); 21 | Assert.assertArrayEquals(key, message.getKey()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/TrieStorageProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.execution.AccountState; 5 | import com.ajlopez.blockchain.store.TrieStore; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * Created by ajlopez on 23/07/2019. 11 | */ 12 | public class TrieStorageProvider { 13 | private final TrieStore storageTrieStore; 14 | 15 | public TrieStorageProvider(TrieStore storageTrieStore) { 16 | this.storageTrieStore = storageTrieStore; 17 | } 18 | 19 | public TrieStorage retrieve(AccountState accountState) throws IOException { 20 | return this.retrieve(accountState.getStorageHash()); 21 | } 22 | 23 | public TrieStorage retrieve(Hash hash) throws IOException { 24 | return new TrieStorage(this.storageTrieStore.retrieve(hash)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/jsonrpc/JsonRpcResponseTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.json.JsonConverter; 4 | import com.ajlopez.blockchain.json.JsonLexerException; 5 | import com.ajlopez.blockchain.json.JsonParserException; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by ajlopez on 30/11/2018. 13 | */ 14 | public class JsonRpcResponseTest { 15 | @Test 16 | public void createWithConstructor() throws JsonParserException, IOException, JsonLexerException { 17 | JsonRpcResponse response = new JsonRpcResponse("42", "2.0", JsonConverter.convert("foo"), null); 18 | 19 | Assert.assertEquals("42", response.getId()); 20 | Assert.assertEquals("2.0", response.getJsonRpc()); 21 | Assert.assertEquals("\"foo\"", response.getResult().toString()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/TopProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * Created by ajloprez on 02/12/2018. 9 | */ 10 | public class TopProcessor extends AbstractJsonRpcProcessor { 11 | private final Map processors = new HashMap<>(); 12 | 13 | @Override 14 | public JsonRpcResponse processRequest(JsonRpcRequest request) throws JsonRpcException, IOException { 15 | String method = request.getMethod(); 16 | 17 | if (this.processors.containsKey(method)) 18 | return this.processors.get(method).processRequest(request); 19 | 20 | return super.processRequest(request); 21 | } 22 | 23 | public void registerProcess(String name, JsonRpcProcessor processor) { 24 | this.processors.put(name, processor); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/config/MinerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.config; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | 5 | /** 6 | * Created by ajlopez on 23/04/2021. 7 | */ 8 | public class MinerConfiguration { 9 | private final boolean miner; 10 | private final Address coinbase; 11 | private final long gasLimit; 12 | private final int noUncles; 13 | 14 | public MinerConfiguration(boolean miner, Address coinbase, long gasLimit, int noUncles) { 15 | this.miner = miner; 16 | this.coinbase = coinbase; 17 | this.gasLimit = gasLimit; 18 | this.noUncles = noUncles; 19 | } 20 | 21 | public boolean isMiner() { return this.miner; } 22 | 23 | public Address getCoinbase() { return this.coinbase; } 24 | 25 | public long getGasLimit() { return this.gasLimit; } 26 | 27 | public int getNoUncles() { return this.noUncles; } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/processors/TransactionProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.processors; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by ajlopez on 27/01/2018. 10 | */ 11 | public class TransactionProcessor { 12 | private final TransactionPool transactionPool; 13 | private final TransactionValidator transactionValidator = new TransactionValidator(); 14 | 15 | public TransactionProcessor(TransactionPool transactionPool) { 16 | this.transactionPool = transactionPool; 17 | } 18 | 19 | public List processTransaction(Transaction transaction) { 20 | if (!this.transactionValidator.isValid(transaction)) 21 | return Collections.emptyList(); 22 | 23 | return this.transactionPool.addTransaction(transaction); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/Address.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | import com.ajlopez.blockchain.utils.ByteUtils; 4 | 5 | /** 6 | * Created by ajlopez on 31/08/2017. 7 | */ 8 | public class Address extends AbstractBytesValue { 9 | public static final int ADDRESS_BYTES = 20; 10 | public static final Address ADDRESS_RICH_TRANSACTION = new Address(new byte[] { 0x01, 0x00, 0x00, 0x00, 0x01 }); 11 | 12 | public static final Address ZERO = new Address(new byte[0]); 13 | 14 | public Address(byte[] bytes) { 15 | super(bytes, ADDRESS_BYTES); 16 | } 17 | 18 | public boolean isZero() { 19 | return ByteUtils.areZero(this.getBytes()); 20 | } 21 | 22 | public static Address normalizeToNull(Address address) { 23 | if (address != null && address.isZero()) 24 | return null; 25 | 26 | return address; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/http/HttpRequest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.http; 2 | 3 | import java.io.Reader; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by ajlopez on 03/12/2018. 8 | */ 9 | public class HttpRequest { 10 | private final String method; 11 | private final String resource; 12 | private final Map headers; 13 | private final Reader reader; 14 | 15 | public HttpRequest(String method, String resource, Map headers, Reader reader) { 16 | this.method = method; 17 | this.resource = resource; 18 | this.headers = headers; 19 | this.reader = reader; 20 | } 21 | 22 | public String getMethod() { return this.method; } 23 | 24 | public String getResource() { return this.resource; } 25 | 26 | public Map getHeaders() { return this.headers; } 27 | 28 | public Reader getReader() { return this.reader; } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/state/TriePath.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by ajlopez on 05/01/2020. 8 | */ 9 | public class TriePath { 10 | private final List tries = new ArrayList<>(); 11 | private final List positions = new ArrayList<>(); 12 | 13 | public TriePath() { 14 | 15 | } 16 | 17 | public int size() { return this.tries.size(); } 18 | 19 | public Trie getTrie(int n) { return this.tries.get(n); } 20 | 21 | public int getChildPosition(int n) { 22 | return this.positions.get(n); 23 | } 24 | 25 | public void addLastTrie(Trie trie) { 26 | // TODO close the path 27 | this.tries.add(trie); 28 | } 29 | 30 | public void addTrieAndChildPosition(Trie trie, int position) { 31 | this.tries.add(trie); 32 | this.positions.add(position); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/execution/ExecutionContext.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.execution; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.core.types.Coin; 5 | import com.ajlopez.blockchain.core.types.Hash; 6 | import com.ajlopez.blockchain.vms.eth.Storage; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by ajlopez on 16/02/2018. 12 | */ 13 | public interface ExecutionContext extends AccountProvider { 14 | void transfer(Address senderAddress, Address receiverAddress, Coin amount) throws IOException; 15 | 16 | void incrementNonce(Address address) throws IOException; 17 | 18 | void commit() throws IOException; 19 | 20 | void rollback(); 21 | 22 | void setCode(Address address, byte[] code) throws IOException; 23 | 24 | void setCodeData(Address address, long codeLength, Hash codeHash) throws IOException; 25 | 26 | ExecutionContext createChildExecutionContext(); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/storage/Chunker.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.storage; 2 | 3 | import com.ajlopez.blockchain.utils.ByteUtils; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | /** 9 | * Created by ajlopez on 11/08/2019. 10 | */ 11 | public class Chunker { 12 | private final int chunkLength; 13 | private final InputStream inputStream; 14 | 15 | public Chunker(int chunkLength, InputStream inputStream) { 16 | this.chunkLength = chunkLength; 17 | this.inputStream = inputStream; 18 | } 19 | 20 | public Chunk nextChunk() throws IOException { 21 | byte[] bytes = new byte[this.chunkLength]; 22 | 23 | int nbytes = this.inputStream.read(bytes); 24 | 25 | if (nbytes == -1) 26 | return null; 27 | 28 | if (nbytes < this.chunkLength) 29 | return new Chunk(ByteUtils.copyBytes(bytes, nbytes)); 30 | 31 | return new Chunk(bytes); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/GetTrieNodeMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.store.TrieType; 5 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Created by ajlopez on 21/06/2019. 11 | */ 12 | public class GetTrieNodeMessageTest { 13 | @Test 14 | public void createMessage() { 15 | Hash topHash = FactoryHelper.createRandomHash(); 16 | TrieType trieType = TrieType.ACCOUNT; 17 | Hash trieHash = FactoryHelper.createRandomHash(); 18 | 19 | GetTrieNodeMessage message = new GetTrieNodeMessage(topHash, trieType, trieHash); 20 | 21 | Assert.assertEquals(topHash, message.getTopHash()); 22 | Assert.assertEquals(trieType, message.getTrieType()); 23 | Assert.assertEquals(trieHash, message.getTrieHash()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/GenesisGenerator.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.types.Address; 5 | import com.ajlopez.blockchain.core.types.BlockHash; 6 | import com.ajlopez.blockchain.core.types.Difficulty; 7 | import com.ajlopez.blockchain.merkle.MerkleTree; 8 | import com.ajlopez.blockchain.state.Trie; 9 | import com.ajlopez.blockchain.store.AccountStore; 10 | 11 | /** 12 | * Created by ajlopez on 29/11/2018. 13 | */ 14 | public class GenesisGenerator { 15 | private GenesisGenerator() { 16 | 17 | } 18 | 19 | public static Block generateGenesis() { 20 | return generateGenesis(new AccountStore(new Trie())); 21 | } 22 | 23 | public static Block generateGenesis(AccountStore accountStore) { 24 | return new Block(0, BlockHash.EMPTY_BLOCK_HASH, MerkleTree.EMPTY_MERKLE_TREE_HASH, accountStore.getRootHash(), 0, Address.ZERO, Difficulty.ONE, 0, 0, null, 0); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/db/ValueFile.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.db; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.io.RandomAccessFile; 6 | 7 | /** 8 | * Created by ajlopez on 20/10/2019. 9 | */ 10 | public class ValueFile { 11 | private final RandomAccessFile file; 12 | 13 | public ValueFile(String name) throws FileNotFoundException { 14 | this.file = new RandomAccessFile(name, "rw"); 15 | } 16 | 17 | public void close() throws IOException { 18 | this.file.close(); 19 | } 20 | 21 | public long writeValue(byte[] value) throws IOException { 22 | long length = this.file.length(); 23 | 24 | this.file.seek(length); 25 | this.file.write(value); 26 | 27 | return length; 28 | } 29 | 30 | public int readValue(long position, byte[] buffer) throws IOException { 31 | this.file.seek(position); 32 | 33 | return this.file.read(buffer); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonConverter.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import com.ajlopez.blockchain.utils.ByteUtils; 4 | import com.ajlopez.blockchain.utils.HexUtils; 5 | 6 | import java.math.BigInteger; 7 | 8 | /** 9 | * Created by ajlopez on 14/11/2018. 10 | */ 11 | public class JsonConverter { 12 | private JsonConverter() { 13 | 14 | } 15 | 16 | public static JsonValue convert(String value) { 17 | return new JsonStringValue(value); 18 | } 19 | 20 | public static JsonValue convert(int value) { 21 | return new JsonNumericValue(value); 22 | } 23 | 24 | public static JsonValue convert(BigInteger value) { 25 | byte[] bytes = value.toByteArray(); 26 | bytes = ByteUtils.copyBytes(bytes, 32); 27 | return new JsonStringValue(HexUtils.bytesToHexString(bytes)); 28 | } 29 | 30 | public static JsonValue convert(Object value) { 31 | return new JsonStringValue(value.toString()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/GetStoredValueMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.encoding.RLP; 4 | import com.ajlopez.blockchain.store.KeyValueStoreType; 5 | 6 | /** 7 | * Created by ajlopez on 03/05/2020. 8 | */ 9 | public class GetStoredValueMessage extends Message { 10 | private final KeyValueStoreType storeType; 11 | private final byte[] key; 12 | 13 | public GetStoredValueMessage(KeyValueStoreType storeType, byte[] key) { 14 | super(MessageType.GET_STORED_VALUE); 15 | this.storeType = storeType; 16 | this.key = key; 17 | } 18 | 19 | public KeyValueStoreType getStoreType() { return this.storeType; } 20 | 21 | public byte[] getKey() { return this.key; } 22 | 23 | @Override 24 | public byte[] getPayload() { 25 | byte[] type = new byte[] { (byte)this.storeType.ordinal() }; 26 | return RLP.encodeList(RLP.encode(type), RLP.encode(this.key)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/json/JsonStringValueTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 27/10/2018. 8 | */ 9 | public class JsonStringValueTest { 10 | @Test 11 | public void createStringValue() { 12 | JsonStringValue value = new JsonStringValue("foo"); 13 | 14 | Assert.assertEquals(JsonValueType.STRING, value.getType()); 15 | Assert.assertEquals("foo", value.getValue()); 16 | } 17 | 18 | @Test 19 | public void simpleStringValueToString() { 20 | JsonStringValue value = new JsonStringValue("foo"); 21 | 22 | Assert.assertEquals("\"foo\"", value.toString()); 23 | } 24 | 25 | @Test 26 | public void simpleStringValueToStringWithEscapedCharacters() { 27 | JsonStringValue value = new JsonStringValue("\n\r\tfoo\\\""); 28 | 29 | Assert.assertEquals("\"\\n\\r\\tfoo\\\\\\\"\"", value.toString()); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/processors/PeerProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.processors; 2 | 3 | import com.ajlopez.blockchain.net.PeerId; 4 | import com.ajlopez.blockchain.net.Status; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by ajlopez on 10/02/2018. 11 | */ 12 | public class PeerProcessor { 13 | private final long networkNumber; 14 | private final Map statuses = new HashMap<>(); 15 | 16 | public PeerProcessor(long networkNumber) { 17 | this.networkNumber = networkNumber; 18 | } 19 | 20 | public long getNetworkNumber() { return this.networkNumber; } 21 | 22 | public Status getStatus(PeerId peerId) { 23 | return statuses.get(peerId); 24 | } 25 | 26 | public void registerStatus(Status status) { 27 | if (status.getNetworkNumber() != this.networkNumber) 28 | return; 29 | 30 | statuses.put(status.getPeerId(), status); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/MessageInputStream.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import com.ajlopez.blockchain.net.messages.Message; 4 | import com.ajlopez.blockchain.net.messages.MessageEncoder; 5 | 6 | /** 7 | * Created by ajlopez on 19/11/2018. 8 | */ 9 | public class MessageInputStream { 10 | private final short network; 11 | private final PacketInputStream packetInputStream; 12 | 13 | public MessageInputStream(short network, PacketInputStream packetInputStream) { 14 | this.network = network; 15 | this.packetInputStream = packetInputStream; 16 | } 17 | 18 | public Message readMessage() { 19 | // TODO process protocol 20 | Packet packet = this.packetInputStream.readPacket(); 21 | 22 | if (packet == null) 23 | return null; 24 | 25 | if (packet.getNetwork() != this.network) 26 | this.packetInputStream.close(); 27 | 28 | return MessageEncoder.decode(packet.getBytes()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/TrieNodeMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.store.TrieType; 5 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Created by ajlopez on 07/06/2019. 11 | */ 12 | public class TrieNodeMessageTest { 13 | @Test 14 | public void createWithData() { 15 | Hash topHash = FactoryHelper.createRandomHash(); 16 | byte[] trieNode = FactoryHelper.createRandomBytes(42); 17 | 18 | TrieNodeMessage message = new TrieNodeMessage(topHash, TrieType.ACCOUNT, trieNode); 19 | 20 | Assert.assertEquals(MessageType.TRIE_NODE, message.getMessageType()); 21 | Assert.assertEquals(topHash, message.getTopHash()); 22 | Assert.assertEquals(TrieType.ACCOUNT, message.getTrieType()); 23 | Assert.assertArrayEquals(trieNode, message.getTrieNode()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/Log.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.core.types.DataWord; 5 | import com.ajlopez.blockchain.utils.ByteUtils; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by ajlopez on 28/01/2019. 13 | */ 14 | public class Log { 15 | private Address address; 16 | private byte[] data; 17 | private List topics; 18 | 19 | public Log(Address address, byte[] data, List topics) { 20 | this.address = address; 21 | this.data = ByteUtils.normalizeBytesToNull(data); 22 | this.topics = topics == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(new ArrayList<>(topics)); 23 | } 24 | 25 | public Address getAddress() { return this.address; } 26 | 27 | public byte[] getData() { return this.data; } 28 | 29 | public List getTopics() { return this.topics; } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/MapStorage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.DataWord; 4 | 5 | import java.io.IOException; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by ajlopez on 09/12/2018. 11 | */ 12 | 13 | public class MapStorage implements Storage { 14 | private Map values = new HashMap<>(); 15 | 16 | @Override 17 | public boolean hasValue(DataWord address) throws IOException { 18 | return this.values.containsKey(address); 19 | } 20 | 21 | @Override 22 | public DataWord getValue(DataWord address) throws IOException { 23 | if (this.hasValue(address)) 24 | return this.values.get(address); 25 | 26 | return DataWord.ZERO; 27 | } 28 | 29 | @Override 30 | public void setValue(DataWord address, DataWord value) { 31 | this.values.put(address, value); 32 | } 33 | 34 | @Override 35 | public void commit() throws IOException { } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/StoredKeyValueMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.store.KeyValueStoreType; 4 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | /** 9 | * Created by ajlopez on 03/05/2020. 10 | */ 11 | public class StoredKeyValueMessageTest { 12 | @Test 13 | public void createMessage() { 14 | KeyValueStoreType storeType = KeyValueStoreType.BLOCKS; 15 | byte[] key = FactoryHelper.createRandomBytes(32); 16 | byte[] value = FactoryHelper.createRandomBytes(42); 17 | 18 | StoredKeyValueMessage message = new StoredKeyValueMessage(storeType, key, value); 19 | 20 | Assert.assertEquals(MessageType.STORED_KEY_VALUE, message.getMessageType()); 21 | Assert.assertEquals(storeType, message.getStoreType()); 22 | Assert.assertArrayEquals(key, message.getKey()); 23 | Assert.assertArrayEquals(value, message.getValue()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/crypto/SpongyCastleProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.crypto; 2 | 3 | import org.spongycastle.jce.provider.BouncyCastleProvider; 4 | 5 | import java.security.Provider; 6 | import java.security.Security; 7 | 8 | /** 9 | * Created by ajlopez on 28/10/2017. 10 | * Copy from EthereumJ package org.ethereum.crypto.jce.SpongyCastleProvider 11 | */ 12 | 13 | public final class SpongyCastleProvider { 14 | private static class Holder { 15 | private static final Provider INSTANCE; 16 | static{ 17 | Provider p = Security.getProvider("SC"); 18 | 19 | INSTANCE = (p != null) ? p : new BouncyCastleProvider(); 20 | 21 | INSTANCE.put("MessageDigest.ETH-KECCAK-256", "org.ethereum.crypto.cryptohash.Keccak256"); 22 | INSTANCE.put("MessageDigest.ETH-KECCAK-512", "org.ethereum.crypto.cryptohash.Keccak512"); 23 | } 24 | } 25 | 26 | public static Provider getInstance() { 27 | return Holder.INSTANCE; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import java.io.IOException; 4 | import java.io.StringWriter; 5 | 6 | /** 7 | * Created by ajlopez on 27/10/2018. 8 | */ 9 | public abstract class JsonValue { 10 | private JsonValueType type; 11 | private Object value; 12 | 13 | public JsonValue(JsonValueType type, Object value) { 14 | this.type = type; 15 | this.value = value; 16 | } 17 | 18 | public JsonValueType getType() { 19 | return this.type; 20 | } 21 | 22 | public Object getValue() { 23 | return this.value; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | StringWriter writer = new StringWriter(); 29 | JsonWriter jsonWriter = new JsonWriter(writer); 30 | 31 | try { 32 | jsonWriter.write(this); 33 | writer.close(); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | 38 | return writer.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/store/CodeStoreTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by ajlopez on 21/01/2019. 12 | */ 13 | public class CodeStoreTest { 14 | @Test 15 | public void getUnknownCode() throws IOException { 16 | CodeStore codeStore = new CodeStore(new HashMapStore()); 17 | 18 | Assert.assertNull(codeStore.getCode(FactoryHelper.createRandomHash())); 19 | } 20 | 21 | @Test 22 | public void putAndGetCode() throws IOException { 23 | Hash codeHash = FactoryHelper.createRandomHash(); 24 | byte[] code = FactoryHelper.createRandomBytes(42); 25 | 26 | CodeStore codeStore = new CodeStore(new HashMapStore()); 27 | 28 | codeStore.putCode(codeHash, code); 29 | Assert.assertArrayEquals(code, codeStore.getCode(codeHash)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/encoding/BlockEncoder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.encoding; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.BlockHeader; 5 | 6 | /** 7 | * Created by ajlopez on 10/10/2017. 8 | */ 9 | public class BlockEncoder { 10 | private BlockEncoder() {} 11 | 12 | public static byte[] encode(Block block) { 13 | byte[] rlpHeader = BlockHeaderEncoder.encode(block.getHeader()); 14 | 15 | return RLP.encodeList(rlpHeader, BlockHeaderEncoder.encode(block.getUncles()), TransactionEncoder.encode(block.getTransactions())); 16 | } 17 | 18 | public static Block decode(byte[] encoded) { 19 | byte[][] bytes = RLP.decodeList(encoded); 20 | 21 | if (bytes.length != 3) 22 | throw new IllegalArgumentException("Invalid block encoding"); 23 | 24 | BlockHeader header = BlockHeaderEncoder.decode(bytes[0]); 25 | 26 | return new Block(header, BlockHeaderEncoder.decodeList(bytes[1]), TransactionEncoder.decodeList(bytes[2])); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/state/TrieNodeVisitor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayDeque; 5 | import java.util.Queue; 6 | 7 | /** 8 | * Created by ajlopez on 21/10/2020. 9 | */ 10 | public abstract class TrieNodeVisitor { 11 | private final Queue queue = new ArrayDeque<>(); 12 | 13 | public void process(Trie trie) throws IOException { 14 | this.toProcess(trie); 15 | this.process(); 16 | } 17 | 18 | public void process() throws IOException { 19 | while (!this.queue.isEmpty()) { 20 | Trie node = this.queue.poll(); 21 | 22 | processNode(node); 23 | 24 | for (int k = 0; k < Trie.ARITY; k++) { 25 | Trie subnode = node.getSubNode(k); 26 | 27 | if (subnode != null) 28 | this.toProcess(subnode); 29 | } 30 | } 31 | } 32 | 33 | public void toProcess(Trie trie) { 34 | this.queue.add(trie); 35 | } 36 | 37 | public abstract void processNode(Trie trie); 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/StatusMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.types.BlockHash; 4 | import com.ajlopez.blockchain.net.PeerId; 5 | import com.ajlopez.blockchain.net.Status; 6 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | /** 11 | * Created by ajlopez on 05/02/2018. 12 | */ 13 | public class StatusMessageTest { 14 | @Test 15 | public void createWithData() { 16 | PeerId nodeid = FactoryHelper.createRandomPeerId(); 17 | BlockHash blockHash = FactoryHelper.createRandomBlockHash(); 18 | 19 | StatusMessage message = new StatusMessage(new Status(nodeid, 2, 3, blockHash, null)); 20 | 21 | Assert.assertEquals(nodeid, message.getStatus().getPeerId()); 22 | Assert.assertEquals(2, message.getStatus().getNetworkNumber()); 23 | Assert.assertEquals(blockHash, message.getStatus().getBestBlockHash()); 24 | Assert.assertEquals(3, message.getStatus().getBestBlockNumber()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/DualKeyValueStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Created by ajlopez on 19/04/2020. 7 | */ 8 | public class DualKeyValueStore implements KeyValueStore { 9 | private final KeyValueStore originalStore; 10 | private final KeyValueStore newStore; 11 | 12 | public DualKeyValueStore(KeyValueStore originalStore, KeyValueStore newStore) { 13 | this.originalStore = originalStore; 14 | this.newStore = newStore; 15 | } 16 | 17 | @Override 18 | public void setValue(byte[] key, byte[] value) throws IOException { 19 | this.newStore.setValue(key, value); 20 | } 21 | 22 | @Override 23 | public byte[] getValue(byte[] key) throws IOException { 24 | byte[] value = this.newStore.getValue(key); 25 | 26 | if (value != null) 27 | return value; 28 | 29 | value = this.originalStore.getValue(key); 30 | 31 | if (value == null) 32 | return null; 33 | 34 | this.newStore.setValue(key, value); 35 | 36 | return value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/StoredKeyValueMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.encoding.RLP; 4 | import com.ajlopez.blockchain.store.KeyValueStoreType; 5 | 6 | /** 7 | * Created by ajlopez on 03/05/2020. 8 | */ 9 | public class StoredKeyValueMessage extends Message { 10 | private final KeyValueStoreType storeType; 11 | private final byte[] key; 12 | private final byte[] value; 13 | 14 | public StoredKeyValueMessage(KeyValueStoreType storeType, byte[] key, byte[] value) { 15 | super(MessageType.STORED_KEY_VALUE); 16 | this.storeType = storeType; 17 | this.key = key; 18 | this.value = value; 19 | } 20 | 21 | public KeyValueStoreType getStoreType() { return this.storeType; } 22 | 23 | public byte[] getKey() { return this.key; } 24 | 25 | public byte[] getValue() { return this.value; } 26 | 27 | @Override 28 | public byte[] getPayload() { 29 | byte[] type = new byte[] { (byte)this.storeType.ordinal() }; 30 | return RLP.encodeList(RLP.encode(type), RLP.encode(this.key), RLP.encode(this.value)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/json/JsonBooleanValueTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 27/10/2018. 8 | */ 9 | public class JsonBooleanValueTest { 10 | @Test 11 | public void createFalseBooleanValue() { 12 | JsonBooleanValue value = new JsonBooleanValue(false); 13 | 14 | Assert.assertEquals(JsonValueType.BOOLEAN, value.getType()); 15 | Assert.assertEquals(false, value.getValue()); 16 | } 17 | 18 | @Test 19 | public void createTrueBooleanValue() { 20 | JsonBooleanValue value = new JsonBooleanValue(true); 21 | 22 | Assert.assertEquals(JsonValueType.BOOLEAN, value.getType()); 23 | Assert.assertEquals(true, value.getValue()); 24 | } 25 | 26 | @Test 27 | public void booleanValuesToString() { 28 | JsonBooleanValue trueValue = new JsonBooleanValue(true); 29 | JsonBooleanValue falseValue = new JsonBooleanValue(false); 30 | 31 | Assert.assertEquals("true", trueValue.toString()); 32 | Assert.assertEquals("false", falseValue.toString()); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /trietest2.cmd: -------------------------------------------------------------------------------- 1 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 20 --csv > trietest2.txt 2 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 2097152 32 20 --csv >> trietest2.txt 3 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 3145728 32 20 --csv >> trietest2.txt 4 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 4194304 32 20 --csv >> trietest2.txt 5 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 5242880 32 20 --csv >> trietest2.txt 6 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 6291456 32 20 --csv >> trietest2.txt 7 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 7340032 32 20 --csv >> trietest2.txt 8 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 8388608 32 20 --csv >> trietest2.txt 9 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/bc/WalletCreatorTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Account; 4 | import com.ajlopez.blockchain.core.types.Coin; 5 | import com.ajlopez.blockchain.state.Trie; 6 | import com.ajlopez.blockchain.store.AccountStore; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * Created by ajlopez on 24/03/2021. 14 | */ 15 | public class WalletCreatorTest { 16 | @Test 17 | public void createWallet() throws IOException { 18 | AccountStore accountStore = new AccountStore(new Trie()); 19 | 20 | WalletCreator walletCreator = new WalletCreator(accountStore); 21 | 22 | Wallet wallet = walletCreator.createWallet(10, Coin.TEN); 23 | 24 | Assert.assertNotNull(wallet); 25 | Assert.assertEquals(10, wallet.getAddresses().size()); 26 | 27 | for (int k = 0; k < 10; k++) { 28 | Account account = accountStore.getAccount(wallet.getAddresses().get(k)); 29 | 30 | Assert.assertNotNull(account); 31 | Assert.assertEquals(Coin.TEN, account.getBalance()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/storage/ChunkStoreTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.storage; 2 | 3 | import com.ajlopez.blockchain.store.HashMapStore; 4 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by ajlopez on 13/08/2019. 12 | */ 13 | public class ChunkStoreTest { 14 | @Test 15 | public void getUnknownChunk() throws IOException { 16 | ChunkStore chunkStore = new ChunkStore(new HashMapStore()); 17 | 18 | Assert.assertNull(chunkStore.getChunk(FactoryHelper.createRandomHash())); 19 | } 20 | 21 | @Test 22 | public void setAndGetChunk() throws IOException { 23 | Chunk chunk = new Chunk(FactoryHelper.createRandomBytes(42)); 24 | ChunkStore chunkStore = new ChunkStore(new HashMapStore()); 25 | 26 | chunkStore.saveChunk(chunk); 27 | 28 | Chunk result = chunkStore.getChunk(chunk.getHash()); 29 | 30 | Assert.assertNotNull(result); 31 | Assert.assertEquals(chunk.getHash(), result.getHash()); 32 | Assert.assertArrayEquals(chunk.getData(), result.getData()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/DslTerm.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl; 2 | 3 | import com.ajlopez.blockchain.test.World; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 08/12/2020. 9 | */ 10 | public class DslTerm implements DslExpression { 11 | private final String term; 12 | 13 | public DslTerm(String term) { 14 | this.term = term; 15 | } 16 | 17 | @Override 18 | public Object evaluate(World world) throws IOException { 19 | if (Character.isDigit(this.term.charAt(0))) 20 | return Integer.parseInt(this.term); 21 | 22 | if ("true".equals(this.term)) 23 | return true; 24 | 25 | if ("false".equals(this.term)) 26 | return false; 27 | 28 | if (this.term.equals("blockchain")) 29 | return world.getBlockChain(); 30 | 31 | Object result = world.getAccountAddress(this.term); 32 | 33 | if (result != null) 34 | return result; 35 | 36 | result = world.getTransaction(this.term); 37 | 38 | if (result != null) 39 | return result; 40 | 41 | return world.getBlock(this.term); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonStringValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 27/10/2018. 5 | */ 6 | public class JsonStringValue extends JsonValue { 7 | public JsonStringValue(String value) { 8 | super(JsonValueType.STRING, value); 9 | } 10 | 11 | @Override 12 | public String toString() { 13 | String value = (String)this.getValue(); 14 | StringBuffer buffer = new StringBuffer(value.length() + 2); 15 | 16 | buffer.append('"'); 17 | 18 | for (int k = 0; k < value.length(); k++) { 19 | char ch = value.charAt(k); 20 | 21 | if (ch == '\t') 22 | buffer.append("\\t"); 23 | else if (ch == '\r') 24 | buffer.append("\\r"); 25 | else if (ch == '\n') 26 | buffer.append("\\n"); 27 | else if (ch == '"') 28 | buffer.append("\\\""); 29 | else if (ch == '\\') 30 | buffer.append("\\\\"); 31 | else 32 | buffer.append(ch); 33 | } 34 | 35 | buffer.append('"'); 36 | 37 | return buffer.toString(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/AccountsProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.core.Account; 4 | import com.ajlopez.blockchain.core.Block; 5 | import com.ajlopez.blockchain.core.types.Address; 6 | import com.ajlopez.blockchain.store.AccountStore; 7 | import com.ajlopez.blockchain.store.AccountStoreProvider; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by ajlopez on 15/09/2019. 13 | */ 14 | public class AccountsProvider { 15 | private final BlocksProvider blocksProvider; 16 | private final AccountStoreProvider accountStoreProvider; 17 | 18 | public AccountsProvider(BlocksProvider blocksProvider, AccountStoreProvider accountStoreProvider) { 19 | this.blocksProvider = blocksProvider; 20 | this.accountStoreProvider = accountStoreProvider; 21 | } 22 | 23 | public Account getAccount(Address address, String blockId) throws JsonRpcException, IOException { 24 | Block block = this.blocksProvider.getBlock(blockId); 25 | AccountStore accountStore = this.accountStoreProvider.retrieve(block.getStateRootHash()); 26 | 27 | return accountStore.getAccount(address); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (C) 2017-present, Angel J Lopez 3 | http://www.ajlopez.com 4 | http://ajlopez.wordpress.com 5 | http://twitter.com/ajlopez 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/BlockStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.types.BlockHash; 5 | import com.ajlopez.blockchain.encoding.BlockEncoder; 6 | import com.ajlopez.blockchain.store.KeyValueStore; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by ajlopez on 03/02/2018. 12 | */ 13 | public class BlockStore { 14 | private final KeyValueStore keyValueStore; 15 | 16 | public BlockStore(KeyValueStore keyValueStore) { 17 | this.keyValueStore = keyValueStore; 18 | } 19 | 20 | public void saveBlock(Block block) throws IOException { 21 | this.keyValueStore.setValue(block.getHash().getBytes(), BlockEncoder.encode(block)); 22 | } 23 | 24 | public Block getBlock(BlockHash hash) throws IOException { 25 | byte[] encoded = this.keyValueStore.getValue(hash.getBytes()); 26 | 27 | if (encoded == null) 28 | return null; 29 | 30 | return BlockEncoder.decode(encoded); 31 | } 32 | 33 | public boolean containsBlock(BlockHash hash) throws IOException { 34 | return this.keyValueStore.getValue(hash.getBytes()) != null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/GetTrieNodeMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.encoding.RLP; 5 | import com.ajlopez.blockchain.store.TrieType; 6 | 7 | /** 8 | * Created by ajlopez on 21/06/2019. 9 | */ 10 | public class GetTrieNodeMessage extends Message { 11 | private final Hash topHash; 12 | private final TrieType trieType; 13 | private final Hash trieHash; 14 | 15 | public GetTrieNodeMessage(Hash topHash, TrieType trieType, Hash trieHash) { 16 | super(MessageType.GET_TRIE_NODE); 17 | this.topHash = topHash; 18 | this.trieType = trieType; 19 | this.trieHash = trieHash; 20 | } 21 | 22 | public Hash getTopHash() { return this.topHash; } 23 | 24 | public TrieType getTrieType() { return this.trieType; } 25 | 26 | public Hash getTrieHash() { return this.trieHash; } 27 | 28 | @Override 29 | public byte[] getPayload() { 30 | byte[] type = new byte[] { (byte)this.trieType.ordinal() }; 31 | return RLP.encodeList(RLP.encode(this.topHash.getBytes()), RLP.encode(type), RLP.encode(this.trieHash.getBytes())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/jsonrpc/JsonRpcRequestTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.json.JsonLexerException; 4 | import com.ajlopez.blockchain.json.JsonParserException; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | import java.io.Reader; 10 | import java.io.StringReader; 11 | 12 | /** 13 | * Created by ajlopez on 10/11/2018. 14 | */ 15 | public class JsonRpcRequestTest { 16 | @Test 17 | public void createSimpleRequestFromStringReader() throws JsonParserException, IOException, JsonLexerException { 18 | String text = "{ \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [] } "; 19 | Reader reader = new StringReader(text); 20 | 21 | JsonRpcRequest request = JsonRpcRequest.fromReader(reader); 22 | 23 | Assert.assertNotNull(request); 24 | Assert.assertEquals("1", request.getId()); 25 | Assert.assertEquals("2.0", request.getJsonRpc()); 26 | Assert.assertEquals("eth_blockNumber", request.getMethod()); 27 | Assert.assertNotNull(request.getParams()); 28 | Assert.assertTrue(request.getParams().isEmpty()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/resources/dsl/blockchain06.txt: -------------------------------------------------------------------------------- 1 | 2 | block 3 | name=b1 4 | parent=genesis 5 | end 6 | 7 | block 8 | name=b1b 9 | parent=genesis 10 | end 11 | 12 | block 13 | name=b1c 14 | parent=genesis 15 | end 16 | 17 | block 18 | name=b1d 19 | parent=genesis 20 | end 21 | 22 | block 23 | name=b2 24 | uncles=b1d 25 | parent=b1 26 | end 27 | 28 | block 29 | name=b2b 30 | parent=b1b 31 | end 32 | 33 | block 34 | name=b2plus 35 | uncles=b1b,b1c 36 | parent=b1 37 | end 38 | 39 | process b1 40 | process b1b 41 | process b2 42 | process b2b 43 | process b2plus 44 | 45 | assert blockchain.bestBlock.number == b2plus.number 46 | assert b2plus.number == blockchain.bestBlock.number 47 | assert blockchain.bestBlock.hash == b2plus.hash 48 | assert b2plus.hash == blockchain.bestBlock.hash 49 | 50 | block 51 | name=b3 52 | parent=b2 53 | end 54 | 55 | block 56 | name=b3plus 57 | parent=b2plus 58 | end 59 | 60 | process b3 61 | process b3plus 62 | 63 | assert blockchain.bestBlock.number == b3plus.number 64 | assert b3plus.number == blockchain.bestBlock.number 65 | assert blockchain.bestBlock.hash == b3plus.hash 66 | assert b3plus.hash == blockchain.bestBlock.hash 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/newvm/OpCodes.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.newvm; 2 | 3 | /** 4 | * Created by ajlopez on 12/08/2017. 5 | */ 6 | public class OpCodes { 7 | private OpCodes() { } 8 | 9 | public static final byte OP_STOP = 0; 10 | public static final byte OP_PUSH = 1; 11 | public static final byte OP_POP = 2; 12 | public static final byte OP_ADD = 3; 13 | public static final byte OP_SUBTRACT = 4; 14 | public static final byte OP_MULTIPLY = 5; 15 | public static final byte OP_DIVIDE = 6; 16 | public static final byte OP_MOD = 7; 17 | public static final byte OP_EXP = 8; 18 | 19 | public static final byte OP_DUP = 16; 20 | public static final byte OP_SWAP = 17; 21 | public static final byte OP_SSTORE = 32; 22 | public static final byte OP_SLOAD = 33; 23 | public static final byte OP_MSTORE = 34; 24 | public static final byte OP_MLOAD = 35; 25 | 26 | public static final byte OP_EQUAL = 48; 27 | public static final byte OP_LT = 49; 28 | public static final byte OP_GT = 50; 29 | 30 | public static final byte OP_JUMP = 64; 31 | public static final byte OP_JUMPI = 65; 32 | public static final byte OP_JUMPDEST = 66; 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/messages/BlockMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.types.Address; 5 | import com.ajlopez.blockchain.core.types.BlockHash; 6 | import com.ajlopez.blockchain.core.types.Difficulty; 7 | import com.ajlopez.blockchain.encoding.BlockEncoder; 8 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | 12 | /** 13 | * Created by ajlopez on 19/01/2018. 14 | */ 15 | public class BlockMessageTest { 16 | @Test 17 | public void createWithBlock() { 18 | BlockHash hash = FactoryHelper.createRandomBlockHash(); 19 | Address coinbase = FactoryHelper.createRandomAddress(); 20 | 21 | Block block = new Block(1L, hash, null, FactoryHelper.createRandomHash(), System.currentTimeMillis() / 1000, coinbase, Difficulty.ONE, 0, 0, null, 0); 22 | 23 | BlockMessage message = new BlockMessage(block); 24 | 25 | Assert.assertEquals(MessageType.BLOCK, message.getMessageType()); 26 | Assert.assertArrayEquals(BlockEncoder.encode(block), message.getPayload()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonObjectValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | /** 7 | * Created by ajlopez on 27/10/2018. 8 | */ 9 | public class JsonObjectValue extends JsonValue { 10 | private Map properties; 11 | 12 | public JsonObjectValue(Map properties) { 13 | super(JsonValueType.OBJECT, properties); 14 | this.properties = properties; 15 | } 16 | 17 | public JsonValue getProperty(String name) { 18 | return this.properties.get(name); 19 | } 20 | 21 | public JsonValue getProperty(String name, String ...names) { 22 | JsonValue value = this.getProperty(name); 23 | 24 | for (int k = 0; k < names.length; k++) 25 | value = ((JsonObjectValue)value).getProperty(names[k]); 26 | 27 | return value; 28 | } 29 | 30 | public boolean hasProperty(String name) { 31 | return this.properties.containsKey(name); 32 | } 33 | 34 | public int noProperties() { 35 | return this.properties.size(); 36 | } 37 | 38 | public Set getPropertyNames() { 39 | return this.properties.keySet(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/WalletProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.json.JsonBuilder; 5 | import com.ajlopez.blockchain.json.JsonValue; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by ajlopez on 01/12/2018. 12 | */ 13 | public class WalletProcessor extends AbstractJsonRpcProcessor { 14 | private final List
addresses; 15 | 16 | public WalletProcessor(List
addresses) { 17 | this.addresses = addresses; 18 | } 19 | 20 | @Override 21 | public JsonRpcResponse processRequest(JsonRpcRequest request) throws JsonRpcException, IOException { 22 | if (request.check("eth_accounts", 0)) 23 | return JsonRpcResponse.createResponse(request, this.addressToJsonArray()); 24 | 25 | return super.processRequest(request); 26 | } 27 | 28 | private JsonValue addressToJsonArray() { 29 | JsonBuilder builder = new JsonBuilder().array(); 30 | 31 | for (Address address : this.addresses) 32 | builder.value(address.toString()); 33 | 34 | return builder.end().build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/Peer.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.net.PeerId; 5 | 6 | import java.util.Random; 7 | 8 | /** 9 | * Created by ajlopez on 04/02/2018. 10 | */ 11 | public class Peer { 12 | private static final Random random = new Random(); 13 | 14 | private final PeerId id; 15 | 16 | // TODO remove usage 17 | public static Peer createRandomPeer() { 18 | byte[] hashBytes = new byte[Hash.HASH_BYTES]; 19 | random.nextBytes(hashBytes); 20 | return new Peer(new PeerId(hashBytes)); 21 | } 22 | 23 | public Peer(PeerId id) { 24 | this.id = id; 25 | } 26 | 27 | public PeerId getId() { 28 | return this.id; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | if (obj == null) 34 | return false; 35 | 36 | if (!(obj instanceof Peer)) 37 | return false; 38 | 39 | Peer peer = (Peer)obj; 40 | 41 | return this.getId().equals(peer.getId()); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return this.getId().hashCode(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/commands/DslTransactionCommand.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl.commands; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | import com.ajlopez.blockchain.core.types.Address; 5 | import com.ajlopez.blockchain.core.types.Coin; 6 | import com.ajlopez.blockchain.test.World; 7 | import com.ajlopez.blockchain.test.dsl.DslCommand; 8 | 9 | import java.io.IOException; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by ajlopez on 02/03/2021. 14 | */ 15 | public class DslTransactionCommand extends DslCommand { 16 | public DslTransactionCommand(List arguments) { 17 | super("transaction", arguments); 18 | } 19 | 20 | @Override 21 | public void execute(World world) throws IOException { 22 | String name = this.getName(0, "name"); 23 | Address from = this.getAddress(world, 1, "from"); 24 | Address to = this.getAddress(world, 2, "to"); 25 | Coin value = this.getCoin(3, "value"); 26 | long nonce = this.getLongInteger(4, "nonce"); 27 | 28 | Transaction transaction = new Transaction(from, to, value, nonce, null, 6000000, Coin.ZERO); 29 | 30 | world.setTransaction(name, transaction); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/TrieNodeMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.encoding.RLP; 5 | import com.ajlopez.blockchain.store.TrieType; 6 | 7 | /** 8 | * Created by ajlopez on 07/06/2019. 9 | */ 10 | public class TrieNodeMessage extends Message { 11 | private final Hash topHash; 12 | private final TrieType trieType; 13 | private final byte[] trieNode; 14 | 15 | public TrieNodeMessage(Hash topHash, TrieType trieType, byte[] trieNode) { 16 | super(MessageType.TRIE_NODE); 17 | this.topHash = topHash; 18 | this.trieType = trieType; 19 | this.trieNode = trieNode; 20 | } 21 | 22 | public Hash getTopHash() { return this.topHash; } 23 | 24 | public TrieType getTrieType() { 25 | return this.trieType; 26 | } 27 | 28 | public byte[] getTrieNode() { 29 | return this.trieNode; 30 | } 31 | 32 | @Override 33 | public byte[] getPayload() { 34 | byte[] type = new byte[] { (byte)this.trieType.ordinal() }; 35 | return RLP.encodeList(RLP.encode(this.topHash.getBytes()), RLP.encode(type), RLP.encode(this.trieNode)); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/KeyValueStores.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | 4 | import java.io.IOException; 5 | 6 | /** 7 | * Created by ajlopez on 04/05/2020. 8 | */ 9 | public interface KeyValueStores { 10 | KeyValueStore getAccountKeyValueStore(); 11 | 12 | KeyValueStore getStorageKeyValueStore(); 13 | 14 | KeyValueStore getBlockKeyValueStore(); 15 | 16 | KeyValueStore getCodeKeyValueStore(); 17 | 18 | KeyValueStore getBlockInformationKeyValueStore(); 19 | 20 | default byte[] getValue(KeyValueStoreType keyValueStoreType, byte[] key) throws IOException { 21 | switch (keyValueStoreType) { 22 | case BLOCKS: 23 | return this.getBlockKeyValueStore().getValue(key); 24 | 25 | case CODES: 26 | return this.getCodeKeyValueStore().getValue(key); 27 | 28 | case ACCOUNTS: 29 | return this.getAccountKeyValueStore().getValue(key); 30 | 31 | case STORAGE: 32 | return this.getStorageKeyValueStore().getValue(key); 33 | 34 | case BLOCKS_INFORMATION: 35 | return this.getBlockInformationKeyValueStore().getValue(key); 36 | } 37 | 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/utils/ByteArrayWrapperTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.utils; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 21/11/2017. 8 | */ 9 | public class ByteArrayWrapperTest { 10 | @Test 11 | public void createWithData() { 12 | byte[] data = new byte[] { 0x01, 0x02, 0x03 }; 13 | 14 | ByteArrayWrapper wrapper = new ByteArrayWrapper(data); 15 | 16 | Assert.assertArrayEquals(data, wrapper.getBytes()); 17 | } 18 | 19 | @Test 20 | public void createWithSameData() { 21 | byte[] data1 = new byte[] { 0x01, 0x02, 0x03 }; 22 | byte[] data2 = new byte[] { 0x01, 0x02, 0x03 }; 23 | 24 | ByteArrayWrapper wrapper1 = new ByteArrayWrapper(data1); 25 | ByteArrayWrapper wrapper2 = new ByteArrayWrapper(data2); 26 | 27 | Assert.assertEquals(wrapper1, wrapper2); 28 | Assert.assertEquals(wrapper1.hashCode(), wrapper2.hashCode()); 29 | 30 | Assert.assertTrue(wrapper1.equals(wrapper2)); 31 | Assert.assertTrue(wrapper2.equals(wrapper1)); 32 | 33 | Assert.assertFalse(wrapper1.equals(null)); 34 | Assert.assertFalse(wrapper1.equals("foo")); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/WorldDslProcessorTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl; 2 | 3 | import com.ajlopez.blockchain.core.Account; 4 | import com.ajlopez.blockchain.core.types.Coin; 5 | import com.ajlopez.blockchain.test.World; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.io.IOException; 10 | import java.math.BigInteger; 11 | 12 | /** 13 | * Created by ajlopez on 19/12/2020. 14 | */ 15 | public class WorldDslProcessorTest { 16 | @Test 17 | public void createProcessorWithWorld() throws IOException { 18 | World world = new World(); 19 | 20 | WorldDslProcessor processor = new WorldDslProcessor(world); 21 | 22 | Assert.assertSame(world, processor.getWorld()); 23 | } 24 | 25 | @Test 26 | public void processAccountNewCommand() throws IOException, DslException { 27 | World world = new World(); 28 | 29 | WorldDslProcessor processor = new WorldDslProcessor(world); 30 | 31 | DslParser parser = new DslParser("account acc1"); 32 | 33 | processor.processCommands(parser); 34 | 35 | Account account = world.getAccount("acc1"); 36 | 37 | Assert.assertNotNull(account); 38 | Assert.assertEquals(Coin.ZERO, account.getBalance()); 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/encoding/BlockInformationEncoder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.encoding; 2 | 3 | import com.ajlopez.blockchain.bc.BlockInformation; 4 | import com.ajlopez.blockchain.core.types.BlockHash; 5 | import com.ajlopez.blockchain.core.types.Difficulty; 6 | 7 | /** 8 | * Created by ajlopez on 17/03/2020. 9 | */ 10 | public class BlockInformationEncoder { 11 | private BlockInformationEncoder() {} 12 | 13 | public static byte[] encode(BlockInformation blockInformation) { 14 | byte[] rlpBlockHash = RLPEncoder.encodeBlockHash(blockInformation.getBlockHash()); 15 | byte[] rlpTotalDifficulty = RLPEncoder.encodeDifficulty(blockInformation.getTotalDifficulty()); 16 | 17 | return RLP.encodeList(rlpBlockHash, rlpTotalDifficulty); 18 | } 19 | 20 | public static BlockInformation decode(byte[] encoded) { 21 | byte[][] bytes = RLP.decodeList(encoded); 22 | 23 | if (bytes.length != 2) 24 | throw new IllegalArgumentException("Invalid block information encoding"); 25 | 26 | BlockHash blockHash = RLPEncoder.decodeBlockHash(bytes[0]); 27 | Difficulty totalDifficulty = RLPEncoder.decodeDifficulty(bytes[1]); 28 | 29 | return new BlockInformation(blockHash, totalDifficulty); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/Status.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net; 2 | 3 | import com.ajlopez.blockchain.core.types.BlockHash; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | 6 | public class Status { 7 | private final PeerId peerId; 8 | private final long networkNumber; 9 | private final long bestBlockNumber; 10 | private final BlockHash bestBlockHash; 11 | private final Difficulty bestTotalDifficulty; 12 | 13 | public Status(PeerId peerId, long networkNumber, long bestBlockNumber, BlockHash bestBlockHash, Difficulty bestTotalDifficulty) { 14 | this.peerId = peerId; 15 | this.networkNumber = networkNumber; 16 | this.bestBlockNumber = bestBlockNumber; 17 | this.bestBlockHash = bestBlockHash; 18 | this.bestTotalDifficulty = bestTotalDifficulty; 19 | } 20 | 21 | public PeerId getPeerId() { 22 | return this.peerId; 23 | } 24 | 25 | public long getNetworkNumber() { 26 | return this.networkNumber; 27 | } 28 | 29 | public long getBestBlockNumber() { 30 | return this.bestBlockNumber; 31 | } 32 | 33 | public BlockHash getBestBlockHash() { return this.bestBlockHash; } 34 | 35 | public Difficulty getBestTotalDifficulty() { return this.bestTotalDifficulty; } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/TrieStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.state.Trie; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 04/03/2018. 10 | */ 11 | public class TrieStore { 12 | private KeyValueStore store; 13 | 14 | public TrieStore(KeyValueStore store) { 15 | this.store = store; 16 | } 17 | 18 | public void save(Trie trie) throws IOException { 19 | this.store.setValue(trie.getHash().getBytes(), trie.getEncoded()); 20 | } 21 | 22 | public boolean exists(Hash hash) throws IOException { 23 | if (hash == null || hash.equals(Trie.EMPTY_TRIE_HASH)) 24 | return true; 25 | 26 | return this.store.getValue(hash.getBytes()) != null; 27 | } 28 | 29 | public Trie retrieve(Hash hash) throws IOException { 30 | if (hash == null || hash.equals(Trie.EMPTY_TRIE_HASH)) 31 | return new Trie(this); 32 | 33 | byte[] encoded = this.store.getValue(hash.getBytes()); 34 | 35 | if (encoded == null) 36 | throw new RuntimeException("Unknown trie"); 37 | 38 | return Trie.fromEncoded(encoded, this); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/BlockData.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | 6 | /** 7 | * Created by ajlopez on 21/12/2018. 8 | */ 9 | public class BlockData { 10 | private final long number; 11 | private final long timestamp; 12 | private final Address coinbase; 13 | private final Difficulty difficulty; 14 | private final long gasLimit; 15 | private final int chainId; 16 | 17 | public BlockData(long number, long timestamp, Address coinbase, Difficulty difficulty, long gasLimit, int chainId) { 18 | this.number = number; 19 | this.timestamp = timestamp; 20 | this.coinbase = coinbase; 21 | this.difficulty = difficulty; 22 | this.gasLimit = gasLimit; 23 | this.chainId = chainId; 24 | } 25 | 26 | public long getNumber() { return this.number; } 27 | 28 | public long getTimestamp() { return this.timestamp; } 29 | 30 | public Address getCoinbase() { return this.coinbase; } 31 | 32 | public Difficulty getDifficulty() { return this.difficulty; } 33 | 34 | public long getGasLimit() { return this.gasLimit; } 35 | 36 | public int getChainId() { return this.chainId; } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/BlocksProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.bc.BlockChain; 4 | import com.ajlopez.blockchain.core.Block; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 02/12/2018. 10 | */ 11 | public class BlocksProvider { 12 | private final BlockChain blockChain; 13 | 14 | public BlocksProvider(BlockChain blockChain) { 15 | this.blockChain = blockChain; 16 | } 17 | 18 | public Block getBlock(String blockId) throws JsonRpcException, IOException { 19 | if ("latest".equals(blockId)) 20 | return blockChain.getBestBlockInformation().getBlock(); 21 | if ("earliest".equals(blockId)) 22 | return blockChain.getBlockByNumber(0); 23 | if ("pending".equals(blockId)) 24 | throw new JsonRpcException("Unsupported block id 'pending'"); 25 | 26 | return this.blockChain.getBlockByNumber(toLongNumber(blockId)); 27 | } 28 | 29 | private static long toLongNumber(String text) throws JsonRpcException { 30 | try { 31 | return Long.decode(text); 32 | } 33 | catch (NumberFormatException ex) { 34 | throw new JsonRpcException("Invalid number format"); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/MessageOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import com.ajlopez.blockchain.net.messages.Message; 4 | import com.ajlopez.blockchain.net.messages.MessageEncoder; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 18/11/2018. 10 | */ 11 | public class MessageOutputStream { 12 | private final short network; 13 | private final PacketOutputStream packetOutputStream; 14 | 15 | private boolean closed; 16 | 17 | public MessageOutputStream(short network, PacketOutputStream packetOutputStream) { 18 | this.network = network; 19 | this.packetOutputStream = packetOutputStream; 20 | } 21 | 22 | public boolean writeMessage(Peer sender, Message message) { 23 | if (this.closed) 24 | return false; 25 | 26 | byte[] bytes = MessageEncoder.encode(message); 27 | 28 | // TODO sign packet using sender keys 29 | 30 | return this.packetOutputStream.writePacket(new Packet(Protocols.BLOCKCHAIN, network, bytes)); 31 | } 32 | 33 | public void close() throws IOException { 34 | this.packetOutputStream.close(); 35 | this.closed = true; 36 | } 37 | 38 | public boolean isClosed() { 39 | return this.closed; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/state/TriePathTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Created by ajlopez on 05/01/2020. 8 | */ 9 | public class TriePathTest { 10 | @Test 11 | public void createEmptyTriePath() { 12 | TriePath triePath = new TriePath(); 13 | 14 | Assert.assertEquals(0, triePath.size()); 15 | } 16 | 17 | @Test 18 | public void createTriePathWithOnlyOneTrie() { 19 | TriePath triePath = new TriePath(); 20 | Trie trie = new Trie(); 21 | 22 | triePath.addLastTrie(trie); 23 | 24 | Assert.assertEquals(1, triePath.size()); 25 | Assert.assertSame(trie, triePath.getTrie(0)); 26 | } 27 | 28 | @Test 29 | public void createTriePathWithTwoTries() { 30 | TriePath triePath = new TriePath(); 31 | Trie trie1 = new Trie(); 32 | Trie trie2 = new Trie(); 33 | 34 | triePath.addTrieAndChildPosition(trie1, 1); 35 | triePath.addLastTrie(trie2); 36 | 37 | Assert.assertEquals(2, triePath.size()); 38 | Assert.assertSame(trie1, triePath.getTrie(0)); 39 | Assert.assertSame(trie2, triePath.getTrie(1)); 40 | Assert.assertEquals(1, triePath.getChildPosition(0)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/bc/ExtendedBlockInformationTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | import com.ajlopez.blockchain.merkle.MerkleTree; 6 | import com.ajlopez.blockchain.state.Trie; 7 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | /** 12 | * Created by ajlopez on 11/07/2020. 13 | */ 14 | public class ExtendedBlockInformationTest { 15 | @Test 16 | public void simpleCreate() { 17 | Block block = new Block(1, FactoryHelper.createRandomBlockHash(), MerkleTree.EMPTY_MERKLE_TREE_HASH, Trie.EMPTY_TRIE_HASH, System.currentTimeMillis() / 1000, FactoryHelper.createRandomAddress(), Difficulty.ONE, 0, 0, null, 0); 18 | 19 | ExtendedBlockInformation extendedBlockInformation = new ExtendedBlockInformation(block, Difficulty.ONE.TEN); 20 | 21 | Assert.assertEquals(block.getNumber(), extendedBlockInformation.getBlockNumber()); 22 | Assert.assertEquals(block.getHash(), extendedBlockInformation.getBlockHash()); 23 | Assert.assertEquals(Difficulty.TEN, extendedBlockInformation.getTotalDifficulty()); 24 | Assert.assertSame(block, extendedBlockInformation.getBlock()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/http/HttpRequestParser.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.http; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.Reader; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.StringTokenizer; 9 | 10 | /** 11 | * Created by ajlopez on 03/12/2018. 12 | */ 13 | public class HttpRequestParser { 14 | public HttpRequest parse(Reader reader) throws IOException { 15 | BufferedReader breader = new BufferedReader(reader); 16 | 17 | String line = breader.readLine(); 18 | 19 | StringTokenizer tokenizer = new StringTokenizer(line); 20 | 21 | String method = tokenizer.nextToken().toUpperCase(); 22 | String resource = tokenizer.nextToken(); 23 | 24 | Map headers = new HashMap<>(); 25 | 26 | while ((line = breader.readLine()) != null) 27 | if (line.trim().isEmpty()) 28 | break; 29 | else if (line.indexOf(": ") >= 0) { 30 | int p = line.indexOf(": "); 31 | String key = line.substring(0, p).trim(); 32 | String value = line.substring(p + 2).trim(); 33 | headers.put(key, value); 34 | } 35 | 36 | return new HttpRequest(method, resource, headers, breader); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/storage/StreamProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.storage; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | 5 | import java.io.ByteArrayInputStream; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | 10 | /** 11 | * Created by ajlopez on 14/08/2019. 12 | */ 13 | public class StreamProcessor { 14 | private final ChunkStore chunkStore; 15 | 16 | public StreamProcessor(ChunkStore chunkStore) { 17 | this.chunkStore = chunkStore; 18 | } 19 | 20 | public void processStream(InputStream stream, int chunkLength) throws IOException { 21 | Chunker chunker = new Chunker(chunkLength, stream); 22 | ByteArrayOutputStream hashStream = new ByteArrayOutputStream(); 23 | 24 | for (Chunk chunk = chunker.nextChunk(); chunk != null; chunk = chunker.nextChunk()) { 25 | this.chunkStore.saveChunk(chunk); 26 | hashStream.write(chunk.getHash().getBytes()); 27 | } 28 | 29 | hashStream.close(); 30 | 31 | byte[] hashBytes = hashStream.toByteArray(); 32 | 33 | if (hashBytes.length <= Hash.HASH_BYTES) 34 | return; 35 | 36 | InputStream hashes = new ByteArrayInputStream(hashBytes); 37 | 38 | this.processStream(hashes, chunkLength); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/math/ec/Curve.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.math.ec; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * Created by ajlopez on 15/09/2020. 7 | */ 8 | public class Curve { 9 | private final FieldElement a; 10 | private final FieldElement b; 11 | private final BigInteger p; 12 | private final Point infinite; 13 | 14 | public Curve(BigInteger a, BigInteger b, BigInteger p) { 15 | this.a = new FieldElement(p, a); 16 | this.b = new FieldElement(p, b); 17 | this.p = p; 18 | this.infinite = new Point(this, (FieldElement) null, (FieldElement) null); 19 | } 20 | 21 | public FieldElement getA() { return this.a; } 22 | 23 | public FieldElement getB() { return this.b; } 24 | 25 | public BigInteger getP() { return this.p; } 26 | 27 | public Point getInfinite() { return this.infinite; } 28 | 29 | public boolean inCurve(Point point) { 30 | if (point.isInfinite()) 31 | return true; 32 | 33 | FieldElement y = point.getY(); 34 | FieldElement x = point.getX(); 35 | 36 | y = y.multiply(y); 37 | 38 | FieldElement right = x.multiply(x).multiply(x).add(this.a.multiply(x)).add(this.b); 39 | 40 | // TODO Maybe implement FieldElement.equals 41 | return y.toBigInteger().equals(right.toBigInteger()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/WalletCreator.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Account; 4 | import com.ajlopez.blockchain.core.types.Address; 5 | import com.ajlopez.blockchain.core.types.Coin; 6 | import com.ajlopez.blockchain.store.AccountStore; 7 | 8 | import java.io.IOException; 9 | import java.util.Random; 10 | 11 | /** 12 | * Created by ajlopez on 24/03/2021. 13 | */ 14 | public class WalletCreator { 15 | private static Random random = new Random(); 16 | 17 | private final AccountStore accountStore; 18 | 19 | public WalletCreator(AccountStore accountStore) { 20 | this.accountStore = accountStore; 21 | } 22 | 23 | public Wallet createWallet(int naccounts, Coin balance) throws IOException { 24 | Wallet wallet = new Wallet(); 25 | 26 | for (int k = 0; k < naccounts; k++) { 27 | Address address = generateRandomAddress(); 28 | Account account = new Account(balance, 0, 0, null, null); 29 | this.accountStore.putAccount(address, account); 30 | wallet.addAddress(address); 31 | } 32 | 33 | return wallet; 34 | } 35 | 36 | private Address generateRandomAddress() { 37 | byte[] bytes = new byte[Address.ADDRESS_BYTES]; 38 | random.nextBytes(bytes); 39 | return new Address(bytes); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/encoding/TransactionReceiptEncoder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.encoding; 2 | 3 | import com.ajlopez.blockchain.core.TransactionReceipt; 4 | import com.ajlopez.blockchain.vms.eth.Log; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by ajlopez on 26/05/2020. 10 | */ 11 | public class TransactionReceiptEncoder { 12 | private TransactionReceiptEncoder() {} 13 | 14 | public static byte[] encode(TransactionReceipt transactionReceipt) { 15 | byte[] rlpGasUsed = RLPEncoder.encodeUnsignedLong(transactionReceipt.getGasUsed()); 16 | byte[] rlpSuccess = RLPEncoder.encodeBoolean(transactionReceipt.getSuccess()); 17 | byte[] rlpLogs = LogEncoder.encodeList(transactionReceipt.getLogs()); 18 | 19 | return RLP.encodeList(rlpGasUsed, rlpSuccess, rlpLogs); 20 | } 21 | 22 | public static TransactionReceipt decode(byte[] encoded) { 23 | byte[][] bytes = RLP.decodeList(encoded); 24 | 25 | if (bytes.length != 3) 26 | throw new IllegalArgumentException("Invalid transaction receipt encoding"); 27 | 28 | long gasUsed = RLPEncoder.decodeUnsignedLong(bytes[0]); 29 | boolean success = RLPEncoder.decodeBoolean(bytes[1]); 30 | List logs = LogEncoder.decodeList(bytes[2]); 31 | 32 | return new TransactionReceipt(gasUsed, success, logs); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/state/TrieHashVisitor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.store.TrieStore; 5 | 6 | import java.io.IOException; 7 | import java.util.ArrayDeque; 8 | import java.util.Queue; 9 | 10 | /** 11 | * Created by ajlopez on 24/10/2020. 12 | */ 13 | public abstract class TrieHashVisitor { 14 | private final TrieStore trieStore; 15 | private final Queue queue = new ArrayDeque<>(); 16 | 17 | public TrieHashVisitor(TrieStore trieStore) { 18 | this.trieStore = trieStore; 19 | } 20 | 21 | public void process(Hash hash) throws IOException { 22 | this.toProcess(hash); 23 | this.process(); 24 | } 25 | 26 | public void process() throws IOException { 27 | while (!this.queue.isEmpty()) { 28 | Hash hash = this.queue.poll(); 29 | Trie node = this.trieStore.retrieve(hash); 30 | 31 | processNode(node); 32 | 33 | for (Hash subhash : node.getSubHashes()) { 34 | if (subhash != null) 35 | this.toProcess(subhash); 36 | } 37 | } 38 | } 39 | 40 | public void toProcess(Hash hash) { 41 | this.queue.add(hash); 42 | } 43 | 44 | public abstract void processNode(Trie trie) throws IOException; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/TransactionsProvider.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | import com.ajlopez.blockchain.core.types.Address; 5 | import com.ajlopez.blockchain.core.types.TransactionHash; 6 | import com.ajlopez.blockchain.processors.TransactionPool; 7 | import com.ajlopez.blockchain.utils.HexUtils; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by ajlopez on 20/08/2019. 13 | */ 14 | public class TransactionsProvider { 15 | private TransactionPool transactionPool; 16 | 17 | public TransactionsProvider(TransactionPool transactionPool) { 18 | this.transactionPool = transactionPool; 19 | } 20 | 21 | public Transaction getTransaction(String txid) { 22 | TransactionHash hash = new TransactionHash(HexUtils.hexStringToBytes(txid)); 23 | 24 | // TODO improve, add map in transaction pool 25 | List transactions = this.transactionPool.getTransactions(); 26 | 27 | for (Transaction transaction : transactions) 28 | if (transaction.getHash().equals(hash)) 29 | return transaction; 30 | 31 | return null; 32 | } 33 | 34 | public long getNextNonce(Address sender, long fromNonce) { 35 | return this.transactionPool.getTransactionNonceBySenderFromNonce(sender, fromNonce); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/TransactionReceipt.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core; 2 | 3 | import com.ajlopez.blockchain.core.types.Hash; 4 | import com.ajlopez.blockchain.encoding.TransactionReceiptEncoder; 5 | import com.ajlopez.blockchain.utils.HashUtils; 6 | import com.ajlopez.blockchain.vms.eth.Log; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by ajlopez on 25/05/2020. 14 | */ 15 | public class TransactionReceipt { 16 | private final long gasUsed; 17 | private final boolean success; 18 | private final List logs; 19 | 20 | private Hash hash; 21 | 22 | public TransactionReceipt(long gasUsed, boolean success, List logs) { 23 | this.gasUsed = gasUsed; 24 | this.success = success; 25 | this.logs = logs == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(new ArrayList<>(logs)); 26 | } 27 | 28 | public long getGasUsed() { 29 | return this.gasUsed; 30 | } 31 | 32 | public boolean getSuccess() { 33 | return this.success; 34 | } 35 | 36 | public List getLogs() { return this.logs; } 37 | 38 | public Hash getHash() { 39 | if (this.hash == null) 40 | this.hash = HashUtils.calculateHash(TransactionReceiptEncoder.encode(this)); 41 | 42 | return this.hash; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/jsonrpc/NetworkProcessor.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.config.NetworkConfiguration; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by ajlopez on 2019/04/08. 9 | */ 10 | public class NetworkProcessor extends AbstractJsonRpcProcessor { 11 | private final NetworkConfiguration networkConfiguration; 12 | 13 | public NetworkProcessor(NetworkConfiguration networkConfiguration) { 14 | this.networkConfiguration = networkConfiguration; 15 | } 16 | 17 | @Override 18 | public JsonRpcResponse processRequest(JsonRpcRequest request) throws JsonRpcException, IOException { 19 | if (request.check("net_version", 0)) 20 | return getVersion(request); 21 | 22 | if (request.check("eth_gasPrice", 0)) 23 | return getGasPrice(request); 24 | 25 | return super.processRequest(request); 26 | } 27 | 28 | private JsonRpcResponse getVersion(JsonRpcRequest request) throws JsonRpcException { 29 | int result = this.networkConfiguration.getNetworkNumber(); 30 | 31 | return JsonRpcResponse.createResponse(request, result); 32 | } 33 | 34 | private JsonRpcResponse getGasPrice(JsonRpcRequest request) throws JsonRpcException { 35 | long result = 0; 36 | 37 | return JsonRpcResponse.createResponse(request, result); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/messages/GetBlockHashesMessage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.messages; 2 | 3 | import com.ajlopez.blockchain.encoding.RLP; 4 | import com.ajlopez.blockchain.utils.ByteUtils; 5 | 6 | /** 7 | * Created by ajlopez on 12/07/2019. 8 | */ 9 | public class GetBlockHashesMessage extends Message { 10 | private final long blockHeight; 11 | private final int noBlocks; 12 | private final int blockGap; 13 | 14 | public GetBlockHashesMessage(long blockHeight, int noBlocks, int blockGap) { 15 | super(MessageType.GET_BLOCK_HASHES); 16 | this.blockHeight = blockHeight; 17 | this.noBlocks = noBlocks; 18 | this.blockGap = blockGap; 19 | } 20 | 21 | public long getBlockHeight() { 22 | return this.blockHeight; 23 | } 24 | 25 | public int getNoBlocks() { 26 | return this.noBlocks; 27 | } 28 | 29 | public int getBlockGap() { 30 | return this.blockGap; 31 | } 32 | 33 | @Override 34 | public byte[] getPayload() { 35 | byte[] bheight = ByteUtils.unsignedLongToNormalizedBytes(this.blockHeight); 36 | byte[] bno = ByteUtils.unsignedIntegerToNormalizedBytes(this.noBlocks); 37 | byte[] bgap = ByteUtils.unsignedIntegerToNormalizedBytes(this.blockGap); 38 | 39 | return RLP.encodeList(RLP.encode(bheight), RLP.encode(bno), RLP.encode(bgap)); 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/store/DualKeyValueStoreTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * Created by ajlopez on 19/04/2020. 11 | */ 12 | public class DualKeyValueStoreTest { 13 | @Test 14 | public void copyKeyValues() throws IOException { 15 | KeyValueStore originalStore = new HashMapStore(); 16 | KeyValueStore newStore = new HashMapStore(); 17 | 18 | byte[][]keys = new byte[16][]; 19 | byte[][]values = new byte[16][]; 20 | 21 | for (int k = 0; k < keys.length; k++) { 22 | keys[k] = FactoryHelper.createRandomBytes(32); 23 | values[k] = FactoryHelper.createRandomBytes(42); 24 | 25 | originalStore.setValue(keys[k], values[k]); 26 | } 27 | 28 | for (int k = 0; k < keys.length; k++) 29 | Assert.assertNull(newStore.getValue(keys[k])); 30 | 31 | DualKeyValueStore copierStore = new DualKeyValueStore(originalStore, newStore); 32 | 33 | for (int k = 0; k < keys.length; k++) 34 | Assert.assertArrayEquals(values[k], copierStore.getValue(keys[k])); 35 | 36 | for (int k = 0; k < keys.length; k++) 37 | Assert.assertArrayEquals(values[k], newStore.getValue(keys[k])); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/PacketOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import java.io.DataOutputStream; 4 | import java.io.EOFException; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | 8 | /** 9 | * Created by ajlopez on 21/10/2018. 10 | */ 11 | public class PacketOutputStream { 12 | private DataOutputStream dataOutputStream; 13 | 14 | public PacketOutputStream(OutputStream outputStream) { 15 | this.dataOutputStream = new DataOutputStream(outputStream); 16 | } 17 | 18 | public boolean writePacket(Packet packet) { 19 | try { 20 | this.dataOutputStream.writeInt(0x01020304); 21 | 22 | this.dataOutputStream.writeShort(packet.getProtocol()); 23 | this.dataOutputStream.writeShort(packet.getNetwork()); 24 | 25 | byte[] bytes = packet.getBytes(); 26 | this.dataOutputStream.writeInt(bytes.length); 27 | this.dataOutputStream.write(bytes); 28 | 29 | this.dataOutputStream.flush(); 30 | } 31 | catch (EOFException ex) { 32 | return false; 33 | } 34 | catch (IOException ex) { 35 | System.out.println(ex); 36 | return false; 37 | } 38 | 39 | return true; 40 | } 41 | 42 | public void close() throws IOException { 43 | this.dataOutputStream.close();; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/net/peers/TcpPeerClient.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import com.ajlopez.blockchain.net.messages.StatusMessage; 4 | import com.ajlopez.blockchain.processors.NodeProcessor; 5 | 6 | import java.io.IOException; 7 | import java.net.Socket; 8 | 9 | /** 10 | * Created by ajlopez on 19/11/2018. 11 | */ 12 | public class TcpPeerClient { 13 | private final short network; 14 | private final NodeProcessor peerNode; 15 | private final String host; 16 | private final int port; 17 | 18 | public TcpPeerClient(String host, int port, short network, NodeProcessor peerNode) { 19 | this.host = host; 20 | this.port = port; 21 | this.network = network; 22 | this.peerNode = peerNode; 23 | } 24 | 25 | public PeerNode connect() throws IOException { 26 | Socket socket = new Socket(this.host, this.port); 27 | Peer peer = Peer.createRandomPeer(); 28 | 29 | PeerConnection peerConnection = new PeerConnection(this.network, peer, socket.getInputStream(), socket.getOutputStream(), this.peerNode); 30 | 31 | if (this.peerNode != null) { 32 | peerConnection.postMessage(this.peerNode.getPeer(), new StatusMessage(this.peerNode.getStatus())); 33 | this.peerNode.connectTo(peerConnection); 34 | } 35 | 36 | peerConnection.start(); 37 | 38 | return peerConnection; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/simples/SimpleMessageChannel.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.simples; 2 | 3 | import com.ajlopez.blockchain.net.MessageChannel; 4 | import com.ajlopez.blockchain.net.peers.Peer; 5 | import com.ajlopez.blockchain.net.messages.Message; 6 | import javafx.util.Pair; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.function.Consumer; 11 | 12 | /** 13 | * Created by ajlopez on 19/11/2018. 14 | */ 15 | public class SimpleMessageChannel implements MessageChannel { 16 | private List>> consumers = new ArrayList<>(); 17 | private List> peerMessages = new ArrayList<>(); 18 | 19 | public void postMessage(Peer peer, Message message) { 20 | Pair peerMessage = new Pair(peer, message); 21 | this.peerMessages.add(peerMessage); 22 | this.consumers.forEach(consumer -> consumer.accept(peerMessage)); 23 | } 24 | 25 | public void onMessage(Consumer> consumer) { 26 | this.consumers.add(consumer); 27 | } 28 | 29 | public List> getPeerMessages() { 30 | return this.peerMessages; 31 | } 32 | 33 | public Message getLastMessage() { 34 | if (this.peerMessages.isEmpty()) 35 | return null; 36 | 37 | return this.peerMessages.get(this.peerMessages.size() - 1).getValue(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/AccountStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.core.Account; 4 | import com.ajlopez.blockchain.core.types.Address; 5 | import com.ajlopez.blockchain.core.types.Hash; 6 | import com.ajlopez.blockchain.encoding.AccountEncoder; 7 | import com.ajlopez.blockchain.state.Trie; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by ajlopez on 26/11/2018. 13 | */ 14 | public class AccountStore { 15 | private Trie trie; 16 | 17 | public AccountStore(Trie trie) { 18 | this.trie = trie; 19 | } 20 | 21 | public Account getAccount(Address address) throws IOException { 22 | byte[] key = address.getBytes(); 23 | byte[] value = this.trie.get(key); 24 | 25 | if (value == null) 26 | return new Account(); 27 | 28 | return AccountEncoder.decode(value); 29 | } 30 | 31 | public void putAccount(Address address, Account account) throws IOException { 32 | if (account.isEmpty()) 33 | throw new IllegalArgumentException("Empty account"); 34 | 35 | byte[] key = address.getBytes(); 36 | byte[] value = AccountEncoder.encode(account); 37 | 38 | this.trie = this.trie.put(key, value); 39 | } 40 | 41 | public Hash getRootHash() { 42 | return this.trie.getHash(); 43 | } 44 | 45 | public void save() throws IOException { 46 | this.trie.save(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/peers/PeerTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import com.ajlopez.blockchain.net.PeerId; 4 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 5 | import com.ajlopez.blockchain.utils.HashUtilsTest; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Created by ajlopez on 04/02/2018. 11 | */ 12 | public class PeerTest { 13 | @Test 14 | public void createWithPeerId() { 15 | PeerId peerId = FactoryHelper.createRandomPeerId(); 16 | 17 | Peer peer = new Peer(peerId); 18 | 19 | Assert.assertEquals(peerId, peer.getId()); 20 | } 21 | 22 | @Test 23 | public void peerEquals() { 24 | PeerId peerId = FactoryHelper.createRandomPeerId(); 25 | 26 | Peer peer1 = new Peer(peerId); 27 | Peer peer2 = new Peer(peerId); 28 | Peer peer3 = new Peer(FactoryHelper.createRandomPeerId()); 29 | 30 | Assert.assertEquals(peer1, peer1); 31 | Assert.assertEquals(peer1, peer2); 32 | Assert.assertEquals(peer2, peer1); 33 | Assert.assertNotEquals(peer1, peer3); 34 | Assert.assertNotEquals(peer3, peer2); 35 | Assert.assertNotEquals(peer1, null); 36 | Assert.assertNotEquals(peer1, "foo"); 37 | Assert.assertNotEquals(peer1, peerId); 38 | 39 | Assert.assertEquals(peer1.hashCode(), peer2.hashCode()); 40 | Assert.assertNotEquals(peer1.hashCode(), peer3.hashCode()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /trietest1.cmd: -------------------------------------------------------------------------------- 1 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 10 --csv > trietest1.txt 2 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 20 --csv >> trietest1.txt 3 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 30 --csv >> trietest1.txt 4 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 40 --csv >> trietest1.txt 5 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 50 --csv >> trietest1.txt 6 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 60 --csv >> trietest1.txt 7 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 70 --csv >> trietest1.txt 8 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 80 --csv >> trietest1.txt 9 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 90 --csv >> trietest1.txt 10 | java -cp build\libs\all-in-one-jar-1.0-SNAPSHOT.jar -Xms4g com.ajlopez.blockchain.tools.TrieMemoryPerformance 1048576 32 100 --csv >> trietest1.txt 11 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/Coin.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * Created by ajlopez on 03/08/2019. 7 | */ 8 | public class Coin extends NaturalValue implements Comparable { 9 | public static final Coin ZERO = new Coin(BigInteger.ZERO); 10 | public static final Coin ONE = new Coin(BigInteger.ONE); 11 | public static final Coin TWO = Coin.fromUnsignedLong(2); 12 | public static final Coin TEN = new Coin(BigInteger.TEN); 13 | 14 | public static Coin fromUnsignedLong(long value) { 15 | return new Coin(BigInteger.valueOf(value)); 16 | } 17 | 18 | public static Coin fromBytes(byte[] bytes) { 19 | return new Coin(new BigInteger(1, bytes)); 20 | } 21 | 22 | public Coin(BigInteger value) { 23 | super(value); 24 | } 25 | 26 | public Coin add(Coin coin) { 27 | return new Coin(this.asBigInteger().add(coin.asBigInteger())); 28 | } 29 | 30 | public Coin subtract(Coin coin) { 31 | return new Coin(this.asBigInteger().subtract(coin.asBigInteger())); 32 | } 33 | 34 | public Coin multiply(long value) { 35 | return new Coin(this.asBigInteger().multiply(BigInteger.valueOf(value))); 36 | } 37 | 38 | public int compareTo(Coin coin) { 39 | return this.asBigInteger().compareTo(coin.asBigInteger()); 40 | } 41 | 42 | public boolean isZero() { 43 | return this.asBigInteger().signum() == 0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/bc/ObjectContext.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.processors.TransactionPool; 4 | import com.ajlopez.blockchain.store.KeyValueStores; 5 | import com.ajlopez.blockchain.store.Stores; 6 | 7 | /** 8 | * Created by ajlopez on 29/03/2021. 9 | */ 10 | public class ObjectContext { 11 | private final KeyValueStores keyValueStores; 12 | 13 | private Stores stores; 14 | private BlockChain blockChain; 15 | private TransactionPool transactionPool; 16 | 17 | public ObjectContext(KeyValueStores keyValueStores) { 18 | this.keyValueStores = keyValueStores; 19 | } 20 | 21 | public Stores getStores() { 22 | if (this.stores != null) 23 | return this.stores; 24 | 25 | this.stores = new Stores(this.keyValueStores); 26 | 27 | return this.stores; 28 | } 29 | 30 | public BlockChain getBlockChain() { 31 | if (this.blockChain != null) 32 | return this.blockChain; 33 | 34 | this.blockChain = new BlockChain(this.getStores()); 35 | 36 | return this.blockChain; 37 | } 38 | 39 | public TransactionPool getTransactionPool() { 40 | if (this.transactionPool != null) 41 | return this.transactionPool; 42 | 43 | this.transactionPool = new TransactionPool(); 44 | 45 | return this.transactionPool; 46 | } 47 | 48 | public KeyValueStores getKeyValueStores() { 49 | return this.keyValueStores; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/commands/DslAccountCommand.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl.commands; 2 | 3 | import com.ajlopez.blockchain.core.Account; 4 | import com.ajlopez.blockchain.core.types.Coin; 5 | import com.ajlopez.blockchain.core.types.Hash; 6 | import com.ajlopez.blockchain.test.World; 7 | import com.ajlopez.blockchain.test.dsl.DslCommand; 8 | import com.ajlopez.blockchain.utils.HashUtils; 9 | import com.ajlopez.blockchain.utils.HexUtils; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by ajlopez on 28/02/2021. 16 | */ 17 | public class DslAccountCommand extends DslCommand { 18 | public DslAccountCommand(List arguments) { 19 | super("account", arguments); 20 | } 21 | 22 | @Override 23 | public void execute(World world) throws IOException { 24 | String name = this.getName(0, "name"); 25 | Coin balance = this.getCoin(1, "balance"); 26 | long nonce = this.getLongInteger(2, "nonce"); 27 | String bytecodes = this.getName(3, "code"); 28 | Hash codeHash = null; 29 | byte[] code = null; 30 | 31 | if (bytecodes != null) { 32 | code = HexUtils.hexStringToBytes(bytecodes); 33 | codeHash = HashUtils.calculateHash(code); 34 | world.setCode(codeHash, code); 35 | } 36 | 37 | Account account = new Account(balance, nonce, code != null ? code.length : 0, codeHash, null); 38 | 39 | world.setAccount(name, account); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/commands/DslBlockCommand.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl.commands; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.BlockHeader; 5 | import com.ajlopez.blockchain.core.Transaction; 6 | import com.ajlopez.blockchain.test.World; 7 | import com.ajlopez.blockchain.test.dsl.DslCommand; 8 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 9 | 10 | import java.io.IOException; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by ajlopez on 01/03/2021. 15 | */ 16 | public class DslBlockCommand extends DslCommand { 17 | public DslBlockCommand(List arguments) { 18 | super("block", arguments); 19 | } 20 | 21 | @Override 22 | public void execute(World world) throws IOException { 23 | String name = this.getName(0, "name"); 24 | String parentName = this.getName(1, "parent"); 25 | 26 | if (parentName == null) 27 | parentName = "genesis"; 28 | 29 | List transactionNames = this.getNames(2, "transactions"); 30 | List uncleNames = this.getNames(3, "uncles"); 31 | 32 | Block parent = world.getBlock(parentName); 33 | List transactions = world.getTransactions(transactionNames); 34 | List uncles = world.getBlockHeaders(uncleNames); 35 | 36 | Block block = FactoryHelper.createBlock(parent, FactoryHelper.createRandomAddress(), transactions, uncles); 37 | 38 | world.setBlock(name, block); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/bc/BlockStoreTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.Block; 4 | import com.ajlopez.blockchain.core.types.BlockHash; 5 | import com.ajlopez.blockchain.core.types.Difficulty; 6 | import com.ajlopez.blockchain.encoding.BlockEncoder; 7 | import com.ajlopez.blockchain.state.Trie; 8 | import com.ajlopez.blockchain.store.HashMapStore; 9 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | import java.io.IOException; 14 | 15 | public class BlockStoreTest { 16 | @Test 17 | public void retrieveUnknowBlockAsNull() throws IOException { 18 | BlockStore blockStore = new BlockStore(new HashMapStore()); 19 | BlockHash hash = FactoryHelper.createRandomBlockHash(); 20 | 21 | Assert.assertNull(blockStore.getBlock(hash)); 22 | } 23 | 24 | @Test 25 | public void retrieveBlockAsNull() throws IOException { 26 | Block block = new Block(1, FactoryHelper.createRandomBlockHash(), null, Trie.EMPTY_TRIE_HASH, System.currentTimeMillis() / 1000, FactoryHelper.createRandomAddress(), Difficulty.ONE, 0, 0, null, 0); 27 | BlockStore blockStore = new BlockStore(new HashMapStore()); 28 | BlockHash hash = block.getHash(); 29 | 30 | blockStore.saveBlock(block); 31 | 32 | Block result = blockStore.getBlock(hash); 33 | 34 | Assert.assertNotNull(result); 35 | Assert.assertArrayEquals(BlockEncoder.encode(block), BlockEncoder.encode(result)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/peers/PacketOutputStreamTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.io.*; 7 | 8 | /** 9 | * Created by ajlopez on 21/10/2018. 10 | */ 11 | public class PacketOutputStreamTest { 12 | @Test 13 | public void writeSimpleBytes() throws IOException { 14 | byte[] bytes = new byte[] { 0x10, 0x11, 0x12 }; 15 | 16 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 17 | PacketOutputStream messageOutputStream = new PacketOutputStream(outputStream); 18 | 19 | Assert.assertTrue(messageOutputStream.writePacket(new Packet(Protocols.BLOCKCHAIN, (short)1, bytes))); 20 | 21 | outputStream.close(); 22 | 23 | byte[] result = outputStream.toByteArray(); 24 | 25 | Assert.assertNotNull(result); 26 | Assert.assertEquals(Integer.BYTES + Integer.BYTES + 2 * Short.BYTES + bytes.length, result.length); 27 | 28 | DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(result)); 29 | 30 | Assert.assertEquals(0x01020304, dataInputStream.readInt()); 31 | Assert.assertEquals(Protocols.BLOCKCHAIN, dataInputStream.readShort()); 32 | Assert.assertEquals(1, dataInputStream.readShort()); 33 | Assert.assertEquals(bytes.length, dataInputStream.readInt()); 34 | 35 | byte[] bresult = new byte[bytes.length]; 36 | 37 | dataInputStream.read(bresult); 38 | 39 | Assert.assertArrayEquals(bytes, bresult); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/MemoryKeyValueStores.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | /** 4 | * Created by ajlopez on 04/05/2020. 5 | */ 6 | public class MemoryKeyValueStores implements KeyValueStores { 7 | private final KeyValueStore accountKeyValueStore; 8 | private final KeyValueStore storageKeyValueStore; 9 | private final KeyValueStore blockKeyValueStore; 10 | private final KeyValueStore blockInformationKeyValueStore; 11 | private final KeyValueStore codeKeyValueStore; 12 | 13 | public MemoryKeyValueStores() { 14 | this.accountKeyValueStore = new HashMapStore(); 15 | this.storageKeyValueStore = new HashMapStore(); 16 | this.blockKeyValueStore = new HashMapStore(); 17 | this.blockInformationKeyValueStore = new HashMapStore(); 18 | this.codeKeyValueStore = new HashMapStore(); 19 | } 20 | 21 | @Override 22 | public KeyValueStore getAccountKeyValueStore() { 23 | return this.accountKeyValueStore; 24 | } 25 | 26 | @Override 27 | public KeyValueStore getStorageKeyValueStore() { 28 | return this.storageKeyValueStore; 29 | } 30 | 31 | @Override 32 | public KeyValueStore getBlockKeyValueStore() { 33 | return this.blockKeyValueStore; 34 | } 35 | 36 | @Override 37 | public KeyValueStore getCodeKeyValueStore() { 38 | return this.codeKeyValueStore; 39 | } 40 | 41 | @Override 42 | public KeyValueStore getBlockInformationKeyValueStore() { 43 | return this.blockInformationKeyValueStore; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/jsonrpc/TransactionsProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.jsonrpc; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | import com.ajlopez.blockchain.core.types.Hash; 5 | import com.ajlopez.blockchain.processors.TransactionPool; 6 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | /** 11 | * Created by ajlopez on 20/08/2019. 12 | */ 13 | public class TransactionsProviderTest { 14 | @Test 15 | public void getUnknownTransactionAsNull() { 16 | TransactionPool transactionPool = new TransactionPool(); 17 | Hash hash = FactoryHelper.createRandomHash(); 18 | String txid = hash.toString(); 19 | 20 | TransactionsProvider transactionsProvider = new TransactionsProvider(transactionPool); 21 | 22 | Assert.assertNull(transactionsProvider.getTransaction(txid)); 23 | } 24 | 25 | @Test 26 | public void getTransaction() { 27 | TransactionPool transactionPool = new TransactionPool(); 28 | Transaction transaction = FactoryHelper.createTransaction(1000); 29 | transactionPool.addTransaction(transaction); 30 | 31 | Hash hash = transaction.getHash(); 32 | String txid = hash.toString(); 33 | 34 | TransactionsProvider transactionsProvider = new TransactionsProvider(transactionPool); 35 | 36 | Transaction result = transactionsProvider.getTransaction(txid); 37 | 38 | Assert.assertNotNull(result); 39 | Assert.assertEquals(transaction, result); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/execution/ChildExecutionContext.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.execution; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.core.types.Hash; 5 | import com.ajlopez.blockchain.vms.eth.ChildMapStorage; 6 | import com.ajlopez.blockchain.vms.eth.Storage; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by ajlopez on 27/11/2018. 12 | */ 13 | public class ChildExecutionContext extends AbstractExecutionContext { 14 | private final AbstractExecutionContext parentContext; 15 | 16 | public ChildExecutionContext(AbstractExecutionContext parentContext) { 17 | this.parentContext = parentContext; 18 | } 19 | 20 | @Override 21 | protected AccountState retrieveAccountState(Address address) throws IOException { 22 | return this.parentContext.getAccountState(address).cloneState(); 23 | } 24 | 25 | @Override 26 | protected void updateAccountState(Address address, AccountState accountState) { 27 | this.parentContext.setAccountState(address, accountState); 28 | } 29 | 30 | @Override 31 | public Storage retrieveAccountStorage(Address address) throws IOException { 32 | return new ChildMapStorage(this.parentContext.getAccountStorage(address)); 33 | } 34 | 35 | @Override 36 | public void updateCode(Hash hash, byte[] code) throws IOException { 37 | this.parentContext.setCode(hash, code); 38 | } 39 | 40 | @Override 41 | public byte[] retrieveCode(Hash hash) throws IOException { 42 | return this.parentContext.getCode(hash); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/state/TrieNodeCounterVisitorTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 21/10/2020. 10 | */ 11 | public class TrieNodeCounterVisitorTest { 12 | @Test 13 | public void processEmptyTrie() throws IOException { 14 | Trie trie = new Trie(); 15 | 16 | TrieNodeCounterVisitor visitor = new TrieNodeCounterVisitor(); 17 | 18 | visitor.process(trie); 19 | 20 | Assert.assertEquals(1, visitor.getNodeCounter()); 21 | Assert.assertEquals(0, visitor.getValueCounter()); 22 | } 23 | 24 | @Test 25 | public void processTrieWithOneValue() throws IOException { 26 | Trie trie = new Trie().put(new byte[] { 0x01 }, new byte[] { 0x02 }); 27 | 28 | TrieNodeCounterVisitor visitor = new TrieNodeCounterVisitor(); 29 | 30 | visitor.process(trie); 31 | 32 | Assert.assertEquals(1, visitor.getNodeCounter()); 33 | Assert.assertEquals(1, visitor.getValueCounter()); 34 | } 35 | 36 | @Test 37 | public void processTrieWithTwoValues() throws IOException { 38 | Trie trie = new Trie() 39 | .put(new byte[] { 0x01 }, new byte[] { 0x02 }) 40 | .put(new byte[] { 0x02 }, new byte[] { 0x03 }); 41 | 42 | TrieNodeCounterVisitor visitor = new TrieNodeCounterVisitor(); 43 | 44 | visitor.process(trie); 45 | 46 | Assert.assertEquals(3, visitor.getNodeCounter()); 47 | Assert.assertEquals(2, visitor.getValueCounter()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/json/JsonArrayValueTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by ajlopez on 29/10/2018. 11 | */ 12 | public class JsonArrayValueTest { 13 | @Test 14 | public void createArrayValueWithTwoElements() { 15 | JsonValue value1 = new JsonStringValue("foo"); 16 | JsonValue value2 = new JsonNumericValue("42"); 17 | List values = new ArrayList<>(); 18 | values.add(value1); 19 | values.add(value2); 20 | 21 | JsonArrayValue value = new JsonArrayValue(values); 22 | 23 | Assert.assertEquals(2, value.size()); 24 | Assert.assertSame(value1, value.getValue(0)); 25 | Assert.assertSame(value2, value.getValue(1)); 26 | } 27 | 28 | @Test 29 | public void arrayValueWithTwoElementsToString() { 30 | JsonValue value1 = new JsonStringValue("foo"); 31 | JsonValue value2 = new JsonNumericValue("42"); 32 | List values = new ArrayList<>(); 33 | values.add(value1); 34 | values.add(value2); 35 | 36 | JsonArrayValue value = new JsonArrayValue(values); 37 | 38 | Assert.assertEquals("[ \"foo\", 42 ]", value.toString()); 39 | } 40 | 41 | @Test 42 | public void arrayValueWithNoElementsToString() { 43 | List values = new ArrayList<>(); 44 | 45 | JsonArrayValue value = new JsonArrayValue(values); 46 | 47 | Assert.assertEquals("[]", value.toString()); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonArrayBuilder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by ajlopez on 03/11/2018. 8 | */ 9 | public class JsonArrayBuilder extends JsonBuilder { 10 | private final JsonBuilder parent; 11 | private final List elements = new ArrayList<>(); 12 | 13 | public JsonArrayBuilder(JsonBuilder parent) { 14 | this.parent = parent; 15 | } 16 | 17 | @Override 18 | public JsonBuilder value(int value) { 19 | super.value(value); 20 | elements.add(super.build()); 21 | 22 | return this; 23 | } 24 | 25 | @Override 26 | public JsonBuilder value(boolean value) { 27 | super.value(value); 28 | elements.add(super.build()); 29 | 30 | return this; 31 | } 32 | 33 | @Override 34 | public JsonBuilder value(String value) { 35 | super.value(value); 36 | elements.add(super.build()); 37 | 38 | return this; 39 | } 40 | 41 | @Override 42 | public JsonBuilder value(JsonValue value) { 43 | super.value(value); 44 | elements.add(super.build()); 45 | 46 | return this; 47 | } 48 | 49 | @Override 50 | public JsonBuilder value(Object value) { 51 | return this.value(value.toString()); 52 | } 53 | 54 | @Override 55 | public JsonValue build() { 56 | return new JsonArrayValue(this.elements); 57 | } 58 | 59 | @Override 60 | public JsonBuilder end() { 61 | parent.value(this.build()); 62 | 63 | return parent; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/Bloom.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | /** 4 | * Created by ajlopez on 10/04/2020. 5 | */ 6 | public class Bloom { 7 | public static final int BLOOM_BYTES = 256; 8 | public static final int BLOOM_BITS = BLOOM_BYTES * 8; 9 | 10 | private final byte[] data = new byte[BLOOM_BYTES]; 11 | 12 | public Bloom() { 13 | } 14 | 15 | public Bloom(byte[] data) { 16 | System.arraycopy(data, 0, this.data, 0, BLOOM_BYTES); 17 | } 18 | 19 | public int size() { 20 | int count = 0; 21 | 22 | for (int k = 0; k < BLOOM_BYTES; k++) { 23 | byte b = this.data[k]; 24 | 25 | if (b == 0) 26 | continue; 27 | 28 | for (int j = 0; j < 8; j++) { 29 | if ((b & 1) == 1) 30 | count++; 31 | 32 | b >>= 1; 33 | } 34 | } 35 | 36 | return count; 37 | } 38 | 39 | public void add(int nelement) { 40 | if (nelement < 0 || nelement >= BLOOM_BITS) 41 | throw new IllegalArgumentException("Invalid bloom element"); 42 | 43 | int position = nelement / 8; 44 | int offset = 7 - (nelement % 8); 45 | 46 | data[position] |= 1 << offset; 47 | } 48 | 49 | public boolean include(Bloom bloom) { 50 | byte[] bdata = bloom.data; 51 | 52 | for (int k = 0; k < BLOOM_BYTES; k++) 53 | if ((bdata[k] & this.data[k]) != bdata[k]) 54 | return false; 55 | 56 | return true; 57 | } 58 | 59 | public byte[] getBytes() { 60 | return this.data; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/TrieStorage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.DataWord; 4 | import com.ajlopez.blockchain.core.types.Hash; 5 | import com.ajlopez.blockchain.execution.AccountState; 6 | import com.ajlopez.blockchain.state.Trie; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by Angel on 17/02/2019. 12 | */ 13 | public class TrieStorage implements Storage { 14 | private Trie trie; 15 | 16 | public TrieStorage(Trie trie) { 17 | this.trie = trie; 18 | } 19 | 20 | @Override 21 | public boolean hasValue(DataWord address) throws IOException { 22 | byte[] data = this.trie.get(address.getBytes()); 23 | 24 | return data == null; 25 | } 26 | 27 | @Override 28 | public void setValue(DataWord address, DataWord value) throws IOException { 29 | byte[] bkey = address.getBytes(); 30 | 31 | if (value.equals(DataWord.ZERO)) 32 | this.trie = this.trie.delete(bkey); 33 | else { 34 | byte[] bvalue = value.toNormalizedBytes(); 35 | this.trie = this.trie.put(bkey, bvalue); 36 | } 37 | } 38 | 39 | @Override 40 | public DataWord getValue(DataWord address) throws IOException { 41 | byte[] data = this.trie.get(address.getBytes()); 42 | 43 | if (data == null) 44 | return DataWord.ZERO; 45 | 46 | return new DataWord(data); 47 | } 48 | 49 | public Hash getRootHash() { 50 | return this.trie.getHash(); 51 | } 52 | 53 | @Override 54 | public void commit() throws IOException { 55 | this.trie.save(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/eth/ChildMapStorage.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.DataWord; 4 | 5 | import java.io.IOException; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | /** 10 | * Created by ajlopez on 20/02/2019. 11 | */ 12 | public class ChildMapStorage extends MapStorage { 13 | private final Storage parentStorage; 14 | private final Set changed = new HashSet<>(); 15 | 16 | public ChildMapStorage(Storage parentStorage) { 17 | this.parentStorage = parentStorage; 18 | } 19 | 20 | @Override 21 | public void setValue(DataWord address, DataWord value) { 22 | super.setValue(address, value); 23 | 24 | changed.add(address); 25 | } 26 | 27 | @Override 28 | public boolean hasValue(DataWord address) throws IOException { 29 | if (super.hasValue(address)) 30 | return true; 31 | 32 | return parentStorage.hasValue(address); 33 | } 34 | 35 | @Override 36 | public DataWord getValue(DataWord address) throws IOException { 37 | if (super.hasValue(address)) 38 | return super.getValue(address); 39 | 40 | DataWord value = this.parentStorage.getValue(address); 41 | 42 | super.setValue(address, value); 43 | 44 | return value; 45 | } 46 | 47 | @Override 48 | public void commit() throws IOException { 49 | for (DataWord address : changed) 50 | this.parentStorage.setValue(address, super.getValue(address)); 51 | 52 | changed.clear(); 53 | } 54 | 55 | public boolean hasChanges() { 56 | return !this.changed.isEmpty(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/AbstractBytesValue.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | import com.ajlopez.blockchain.utils.ByteUtils; 4 | import com.ajlopez.blockchain.utils.HexUtils; 5 | 6 | import java.util.Arrays; 7 | 8 | /** 9 | * Created by ajlopez on 27/11/2018. 10 | */ 11 | public abstract class AbstractBytesValue { 12 | protected final byte[] bytes; 13 | 14 | public AbstractBytesValue(byte[] bytes, int length) { 15 | this(bytes, length, false); 16 | } 17 | 18 | public AbstractBytesValue(byte[] bytes, int length, boolean signed) { 19 | if (bytes == null) 20 | throw new IllegalArgumentException("Null byte array"); 21 | 22 | if (bytes.length > length) 23 | throw new IllegalArgumentException("Too large byte array"); 24 | 25 | if (bytes.length == length) 26 | this.bytes = bytes; 27 | else 28 | this.bytes = ByteUtils.copyBytes(bytes, length, false, signed); 29 | } 30 | 31 | // TODO consider immutability 32 | public byte[] getBytes() { 33 | return this.bytes; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object obj) { 38 | if (obj == null) 39 | return false; 40 | 41 | if (this.getClass() != obj.getClass()) 42 | return false; 43 | 44 | AbstractBytesValue value = (AbstractBytesValue)obj; 45 | 46 | return Arrays.equals(this.bytes, value.bytes); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | return Arrays.hashCode(this.bytes); 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return HexUtils.bytesToHexString(this.bytes, true, false); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/store/RemoteKeyValueStore.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.net.messages.GetStoredValueMessage; 4 | import com.ajlopez.blockchain.processors.SendProcessor; 5 | 6 | import java.io.IOException; 7 | import java.util.concurrent.CompletableFuture; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * Created by ajlopez on 03/05/2020. 12 | */ 13 | public class RemoteKeyValueStore implements KeyValueStore { 14 | private final KeyValueStoreType storeType; 15 | private final SendProcessor sendProcessor; 16 | private final KeyValueResolver keyValueResolver; 17 | 18 | public RemoteKeyValueStore(KeyValueStoreType storeType, SendProcessor sendProcessor, KeyValueResolver keyValueResolver) { 19 | this.storeType = storeType; 20 | this.sendProcessor = sendProcessor; 21 | this.keyValueResolver = keyValueResolver; 22 | } 23 | 24 | @Override 25 | public void setValue(byte[] key, byte[] value) throws IOException { 26 | throw new UnsupportedOperationException(); 27 | } 28 | 29 | @Override 30 | public byte[] getValue(byte[] key) throws IOException { 31 | CompletableFuture future = new CompletableFuture<>(); 32 | 33 | this.keyValueResolver.resolve(this.storeType, key, future); 34 | 35 | GetStoredValueMessage getStoredValueMessage = new GetStoredValueMessage(this.storeType, key); 36 | this.sendProcessor.postMessage(getStoredValueMessage); 37 | 38 | // TODO improve timeout 39 | try { 40 | return future.get(5, TimeUnit.SECONDS); 41 | } 42 | catch (Exception ex) { 43 | throw new IOException(ex); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/commands/DslBlockHeaderCommand.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl.commands; 2 | 3 | import com.ajlopez.blockchain.bc.BlockBuilder; 4 | import com.ajlopez.blockchain.core.Block; 5 | import com.ajlopez.blockchain.core.BlockHeader; 6 | import com.ajlopez.blockchain.test.World; 7 | import com.ajlopez.blockchain.test.dsl.DslCommand; 8 | 9 | import java.io.IOException; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by ajlopez on 03/03/2021. 14 | */ 15 | public class DslBlockHeaderCommand extends DslCommand { 16 | public DslBlockHeaderCommand(List arguments) { 17 | super("transaction", arguments); 18 | } 19 | 20 | @Override 21 | public void execute(World world) throws IOException { 22 | String name = this.getName(0, "name"); 23 | String parentName = this.getName(1, "parent"); 24 | 25 | if (parentName == null) 26 | parentName = "genesis"; 27 | 28 | List uncleNames = this.getNames(2, "uncles"); 29 | 30 | List uncles = world.getBlockHeaders(uncleNames); 31 | 32 | BlockHeader blockHeader; 33 | 34 | // TODO parent could be a header 35 | Block parent = world.getBlock(parentName); 36 | 37 | if (parent != null) 38 | blockHeader = new BlockBuilder() 39 | .parent(parent) 40 | .uncles(uncles) 41 | .buildHeader(); 42 | else 43 | blockHeader = new BlockBuilder() 44 | .parentHeader(world.getBlockHeader(parentName)) 45 | .uncles(uncles) 46 | .buildHeader(); 47 | 48 | world.setBlockHeader(name, blockHeader); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/Account.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core; 2 | 3 | import com.ajlopez.blockchain.core.types.Coin; 4 | import com.ajlopez.blockchain.core.types.Hash; 5 | 6 | /** 7 | * Created by ajlopez on 09/11/2017. 8 | */ 9 | public class Account { 10 | private final Coin balance; 11 | private final long nonce; 12 | private final long codeLength; 13 | private final Hash codeHash; 14 | private final Hash storageHash; 15 | private final boolean isEmpty; 16 | 17 | public Account() { 18 | this(Coin.ZERO, 0, 0, null, null); 19 | } 20 | 21 | public Account(Coin balance, long nonce, long codeLength, Hash codeHash, Hash storageHash) { 22 | if (balance == null) 23 | balance = Coin.ZERO; 24 | 25 | if (nonce < 0) 26 | throw new IllegalStateException("Negative nonce in account state"); 27 | 28 | this.balance = balance; 29 | this.nonce = nonce; 30 | this.codeLength = codeLength; 31 | this.codeHash = codeHash; 32 | this.storageHash = storageHash; 33 | 34 | this.isEmpty = this.balance.isZero() && 35 | this.nonce == 0 && 36 | this.codeLength == 0 && 37 | this.getCodeHash() == null && 38 | this.getStorageHash() == null; 39 | } 40 | 41 | public boolean isEmpty() { 42 | return this.isEmpty; 43 | } 44 | 45 | public Coin getBalance() { 46 | return this.balance; 47 | } 48 | 49 | public long getNonce() { return this.nonce; } 50 | 51 | public long getCodeLength() { return this.codeLength; } 52 | 53 | public Hash getCodeHash() { return this.codeHash; } 54 | 55 | public Hash getStorageHash() { return this.storageHash; } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/encoding/StatusEncoder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.encoding; 2 | 3 | import com.ajlopez.blockchain.core.types.BlockHash; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | import com.ajlopez.blockchain.net.PeerId; 6 | import com.ajlopez.blockchain.net.Status; 7 | 8 | public class StatusEncoder { 9 | private StatusEncoder() { 10 | 11 | } 12 | 13 | public static byte[] encode(Status status) { 14 | byte[] rlpNodeId = RLPEncoder.encodePeerId(status.getPeerId()); 15 | byte[] rlpNetworkNumber = RLPEncoder.encodeUnsignedLong(status.getNetworkNumber()); 16 | byte[] rlpBestBlockNumber = RLPEncoder.encodeLong(status.getBestBlockNumber()); 17 | byte[] rlpBestBlockHash = RLPEncoder.encodeBlockHash(status.getBestBlockHash()); 18 | byte[] rlpBestTotalDifficulty = RLPEncoder.encodeDifficulty(status.getBestTotalDifficulty()); 19 | 20 | return RLP.encodeList(rlpNodeId, rlpNetworkNumber, rlpBestBlockNumber, rlpBestBlockHash, rlpBestTotalDifficulty); 21 | } 22 | 23 | public static Status decode(byte[] encoded) { 24 | byte[][] bytes = RLP.decodeList(encoded); 25 | 26 | if (bytes.length != 5) 27 | throw new IllegalArgumentException("Invalid status encoding"); 28 | 29 | PeerId nodeid = RLPEncoder.decodePeerId(bytes[0]); 30 | long networkNumber = RLPEncoder.decodeUnsignedLong(bytes[1]); 31 | long bestBlockNumber = RLPEncoder.decodeLong(bytes[2]); 32 | BlockHash bestBlockHash = RLPEncoder.decodeBlockHash(bytes[3]); 33 | Difficulty bestTotalDifficulty = RLPEncoder.decodeDifficulty(bytes[4]); 34 | 35 | return new Status(nodeid, networkNumber, bestBlockNumber, bestBlockHash, bestTotalDifficulty); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/store/KeyValueStoresTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.store; 2 | 3 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * Created by ajlopez on 05/06/2020. 11 | */ 12 | public class KeyValueStoresTest { 13 | @Test 14 | public void getValues() throws IOException { 15 | byte[] key = FactoryHelper.createRandomBytes(32); 16 | byte[] value1 = FactoryHelper.createRandomBytes(42); 17 | byte[] value2 = FactoryHelper.createRandomBytes(42); 18 | byte[] value3 = FactoryHelper.createRandomBytes(42); 19 | byte[] value4 = FactoryHelper.createRandomBytes(42); 20 | byte[] value5 = FactoryHelper.createRandomBytes(42); 21 | 22 | KeyValueStores keyValueStores = new MemoryKeyValueStores(); 23 | 24 | keyValueStores.getBlockKeyValueStore().setValue(key, value1); 25 | keyValueStores.getAccountKeyValueStore().setValue(key, value2); 26 | keyValueStores.getCodeKeyValueStore().setValue(key, value3); 27 | keyValueStores.getStorageKeyValueStore().setValue(key, value4); 28 | keyValueStores.getBlockInformationKeyValueStore().setValue(key, value5); 29 | 30 | Assert.assertArrayEquals(value1, keyValueStores.getValue(KeyValueStoreType.BLOCKS, key)); 31 | Assert.assertArrayEquals(value2, keyValueStores.getValue(KeyValueStoreType.ACCOUNTS, key)); 32 | Assert.assertArrayEquals(value3, keyValueStores.getValue(KeyValueStoreType.CODES, key)); 33 | Assert.assertArrayEquals(value4, keyValueStores.getValue(KeyValueStoreType.STORAGE, key)); 34 | Assert.assertArrayEquals(value5, keyValueStores.getValue(KeyValueStoreType.BLOCKS_INFORMATION, key)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/vms/newvm/Memory.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.newvm; 2 | 3 | /** 4 | * Created by ajlopez on 26/11/2017. 5 | */ 6 | public class Memory { 7 | private byte[] bytes; 8 | 9 | public byte getValue(int offset) { 10 | if (this.bytes == null || this.bytes.length <= offset) 11 | return 0; 12 | 13 | return this.bytes[offset]; 14 | } 15 | 16 | public byte[] getValues(int offset, int length) { 17 | byte[] values = new byte[length]; 18 | 19 | if (this.bytes == null || this.bytes.length <= offset) 20 | return values; 21 | 22 | int size = length; 23 | 24 | if (offset + length > this.bytes.length) 25 | size -= length + offset - this.bytes.length; 26 | 27 | System.arraycopy(this.bytes, offset, values, 0, size); 28 | 29 | return values; 30 | } 31 | 32 | public void setValue(int offset, byte value) 33 | { 34 | ensureCapacity(offset + 1); 35 | 36 | this.bytes[offset] = value; 37 | } 38 | 39 | public void setValues(int offset, byte[] values) { 40 | ensureCapacity(offset + values.length); 41 | 42 | System.arraycopy(values, 0, this.bytes, offset, values.length); 43 | } 44 | 45 | private void ensureCapacity(int length) { 46 | int blocks = (length + 1023) / 1024; 47 | int required = blocks * 1024; 48 | 49 | if (this.bytes == null) { 50 | this.bytes = new byte[required]; 51 | return; 52 | } 53 | 54 | if (this.bytes.length >= required) 55 | return; 56 | 57 | byte[] newbytes = new byte[required]; 58 | System.arraycopy(this.bytes, 0, newbytes, 0, this.bytes.length); 59 | 60 | this.bytes = newbytes; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/core/types/Difficulty.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.core.types; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * Created by ajlopez on 24/08/2019. 7 | */ 8 | public class Difficulty extends NaturalValue { 9 | public static final Difficulty ZERO = new Difficulty(BigInteger.ZERO); 10 | public static final Difficulty ONE = new Difficulty(BigInteger.ONE); 11 | public static final Difficulty TWO = Difficulty.fromUnsignedLong(2); 12 | public static final Difficulty THREE = Difficulty.fromUnsignedLong(3); 13 | public static final Difficulty TEN = new Difficulty(BigInteger.TEN); 14 | 15 | private static final BigInteger MAX = BigInteger.valueOf(2).pow(256); 16 | 17 | public static Difficulty fromUnsignedLong(long value) { 18 | return new Difficulty(BigInteger.valueOf(value)); 19 | } 20 | 21 | public static Difficulty fromBytes(byte[] bytes) { 22 | return new Difficulty(new BigInteger(1, bytes)); 23 | } 24 | 25 | public Difficulty(BigInteger value) { 26 | super(value); 27 | } 28 | 29 | public int compareTo(Difficulty difficulty) { 30 | return this.asBigInteger().compareTo(difficulty.asBigInteger()); 31 | } 32 | 33 | public boolean isZero() { 34 | return this.asBigInteger().signum() == 0; 35 | } 36 | 37 | public Difficulty add(Difficulty difficulty) { 38 | DataWord diff1 = DataWord.fromBigInteger(this.asBigInteger()); 39 | DataWord diff2 = DataWord.fromBigInteger(difficulty.asBigInteger()); 40 | 41 | DataWord result = diff1.add(diff2); 42 | 43 | return Difficulty.fromBytes(result.getBytes()); 44 | } 45 | 46 | public DataWord toTarget() { 47 | return DataWord.fromBigInteger(MAX.divide(this.asBigInteger())); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/test/dsl/commands/DslAssertCommand.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.test.dsl.commands; 2 | 3 | import com.ajlopez.blockchain.test.World; 4 | import com.ajlopez.blockchain.test.dsl.*; 5 | 6 | import java.io.IOException; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by ajlopez on 07/03/2021. 11 | */ 12 | public class DslAssertCommand extends DslCommand { 13 | public DslAssertCommand(List arguments) { 14 | super("account", arguments); 15 | } 16 | 17 | @Override 18 | public void execute(World world) throws IOException, DslException { 19 | List arguments = this.getArguments(); 20 | 21 | DslExpression expression; 22 | 23 | if (arguments.size() == 1) 24 | expression = toDslExpression(arguments.get(0)); 25 | else 26 | expression = new DslComparison(toDslExpression(arguments.get(0)), arguments.get(1), toDslExpression(arguments.get(2))); 27 | 28 | if (Boolean.FALSE.equals(expression.evaluate(world))) 29 | throw new DslException(String.format("unsatisfied assertion '%s'", this.argumentsToString(arguments))); 30 | } 31 | 32 | private static DslExpression toDslExpression(String text) { 33 | int p = text.lastIndexOf('.'); 34 | 35 | if (p > 0) 36 | return new DslDotExpression(toDslExpression(text.substring(0, p)), text.substring(p + 1)); 37 | 38 | return new DslTerm(text); 39 | } 40 | 41 | private String argumentsToString(List arguments) { 42 | String result = ""; 43 | 44 | for (String argument : arguments) 45 | if (result.length() > 0) 46 | result += " " + argument; 47 | else 48 | result = argument; 49 | 50 | return result; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/vms/eth/BlockDataTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.vms.eth; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.core.types.Difficulty; 5 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Created by ajlopez on 21/12/2018. 11 | */ 12 | public class BlockDataTest { 13 | @Test 14 | public void simpleCreation() { 15 | long number = 1; 16 | long timestamp = 2; 17 | Address coinbase = FactoryHelper.createRandomAddress(); 18 | Difficulty difficulty = Difficulty.ONE; 19 | 20 | BlockData blockData = new BlockData(number, timestamp, coinbase, difficulty, 0, 0); 21 | 22 | Assert.assertEquals(number, blockData.getNumber()); 23 | Assert.assertEquals(timestamp, blockData.getTimestamp()); 24 | Assert.assertEquals(coinbase, blockData.getCoinbase()); 25 | Assert.assertEquals(difficulty, blockData.getDifficulty()); 26 | Assert.assertEquals(0L, blockData.getGasLimit()); 27 | } 28 | 29 | @Test 30 | public void simpleCreationWithGasLimit() { 31 | long number = 1; 32 | long timestamp = 2; 33 | Address coinbase = FactoryHelper.createRandomAddress(); 34 | Difficulty difficulty = Difficulty.ONE; 35 | 36 | BlockData blockData = new BlockData(number, timestamp, coinbase, difficulty, 12_000_000L, 0); 37 | 38 | Assert.assertEquals(number, blockData.getNumber()); 39 | Assert.assertEquals(timestamp, blockData.getTimestamp()); 40 | Assert.assertEquals(coinbase, blockData.getCoinbase()); 41 | Assert.assertEquals(difficulty, blockData.getDifficulty()); 42 | Assert.assertEquals(12_000_000L, blockData.getGasLimit()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonBuilder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | /** 4 | * Created by ajlopez on 02/11/2018. 5 | */ 6 | public class JsonBuilder { 7 | private JsonValue value; 8 | 9 | public JsonBuilder value(Object obj) { 10 | if (obj == null) { 11 | this.value = JsonNullValue.getInstance(); 12 | return this; 13 | } 14 | else 15 | return this.value(obj.toString()); 16 | } 17 | 18 | public JsonBuilder value(String value) { 19 | if (value == null) 20 | this.value = JsonNullValue.getInstance(); 21 | else 22 | this.value = new JsonStringValue(value); 23 | 24 | return this; 25 | } 26 | 27 | public JsonBuilder value(int value) { 28 | this.value = new JsonNumericValue(Integer.toString(value)); 29 | 30 | return this; 31 | } 32 | 33 | public JsonBuilder value(long value) { 34 | this.value = new JsonNumericValue(Long.toString(value)); 35 | 36 | return this; 37 | } 38 | 39 | public JsonBuilder value(boolean value) { 40 | this.value = new JsonBooleanValue(value); 41 | 42 | return this; 43 | } 44 | 45 | public JsonBuilder value(JsonValue value) { 46 | this.value = value; 47 | 48 | return this; 49 | } 50 | 51 | public JsonBuilder name(String name) { 52 | throw new UnsupportedOperationException(); 53 | } 54 | 55 | public JsonBuilder array() { 56 | return new JsonArrayBuilder(this); 57 | } 58 | 59 | public JsonBuilder object() { 60 | return new JsonObjectBuilder(this); 61 | } 62 | 63 | public JsonBuilder end() { 64 | return this; 65 | } 66 | 67 | public JsonValue build() { 68 | return this.value; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/net/peers/MessageInputStreamTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.net.peers; 2 | 3 | import com.ajlopez.blockchain.net.messages.GetBlockByHashMessage; 4 | import com.ajlopez.blockchain.net.messages.Message; 5 | import com.ajlopez.blockchain.net.messages.MessageEncoder; 6 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 7 | import com.ajlopez.blockchain.utils.HashUtilsTest; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | import java.io.*; 12 | 13 | /** 14 | * Created by ajlopez on 19/11/2018. 15 | */ 16 | public class MessageInputStreamTest { 17 | @Test 18 | public void readMessage() throws IOException { 19 | Message message = new GetBlockByHashMessage(FactoryHelper.createRandomBlockHash()); 20 | byte[] bytes = MessageEncoder.encode(message); 21 | ByteArrayOutputStream bytesOutputStream = new ByteArrayOutputStream(); 22 | DataOutputStream dataOutputStream = new DataOutputStream(bytesOutputStream); 23 | 24 | dataOutputStream.writeInt(0x01020304); 25 | dataOutputStream.writeShort(Protocols.BLOCKCHAIN); 26 | dataOutputStream.writeShort(1); 27 | dataOutputStream.writeInt(bytes.length); 28 | dataOutputStream.write(bytes); 29 | 30 | dataOutputStream.close(); 31 | 32 | byte[] packet = bytesOutputStream.toByteArray(); 33 | 34 | InputStream inputStream = new ByteArrayInputStream(packet); 35 | PacketInputStream packetInputStream = new PacketInputStream(inputStream); 36 | MessageInputStream messageInputStream = new MessageInputStream((short)1, packetInputStream); 37 | 38 | Message result = messageInputStream.readMessage(); 39 | 40 | Assert.assertNotNull(result); 41 | Assert.assertArrayEquals(bytes, MessageEncoder.encode(result)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/processors/TransactionProcessorTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.processors; 2 | 3 | import com.ajlopez.blockchain.core.Transaction; 4 | import com.ajlopez.blockchain.core.types.Address; 5 | import com.ajlopez.blockchain.core.types.Coin; 6 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Created by ajlopez on 27/01/2018. 14 | */ 15 | public class TransactionProcessorTest { 16 | @Test 17 | public void processTransaction() { 18 | TransactionPool pool = new TransactionPool(); 19 | TransactionProcessor processor = new TransactionProcessor(pool); 20 | 21 | Transaction transaction = FactoryHelper.createTransaction(100); 22 | 23 | processor.processTransaction(transaction); 24 | 25 | Assert.assertTrue(pool.getTransactions().contains(transaction)); 26 | } 27 | 28 | @Test 29 | public void rejectInvalidTransaction() { 30 | Address sender = FactoryHelper.createRandomAddress(); 31 | Address receiver = Address.ADDRESS_RICH_TRANSACTION; 32 | Coin value = Coin.ONE; 33 | byte []data = FactoryHelper.createRandomBytes(42); 34 | 35 | Transaction transaction = new Transaction(sender, receiver, value, 42, data, 6000000, Coin.ZERO); 36 | 37 | TransactionPool pool = new TransactionPool(); 38 | TransactionProcessor processor = new TransactionProcessor(pool); 39 | 40 | List processed = processor.processTransaction(transaction); 41 | 42 | Assert.assertNotNull(processed); 43 | Assert.assertTrue(processed.isEmpty()); 44 | 45 | Assert.assertFalse(pool.getTransactions().contains(transaction)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/bc/WalletTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.bc; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by ajlopez on 24/03/2021. 12 | */ 13 | public class WalletTest { 14 | @Test 15 | public void emptyWallet() { 16 | Wallet wallet = new Wallet(); 17 | 18 | List
addresses = wallet.getAddresses(); 19 | 20 | Assert.assertNotNull(addresses); 21 | Assert.assertTrue(addresses.isEmpty()); 22 | } 23 | 24 | @Test 25 | public void addAddress() { 26 | Wallet wallet = new Wallet(); 27 | Address address = FactoryHelper.createRandomAddress(); 28 | 29 | wallet.addAddress(address); 30 | 31 | List
addresses = wallet.getAddresses(); 32 | 33 | Assert.assertNotNull(addresses); 34 | Assert.assertFalse(addresses.isEmpty()); 35 | Assert.assertEquals(1, addresses.size()); 36 | Assert.assertTrue(addresses.contains(address)); 37 | } 38 | 39 | @Test 40 | public void addTwoAddresses() { 41 | Wallet wallet = new Wallet(); 42 | Address address1 = FactoryHelper.createRandomAddress(); 43 | Address address2 = FactoryHelper.createRandomAddress(); 44 | 45 | wallet.addAddress(address1); 46 | wallet.addAddress(address2); 47 | 48 | List
addresses = wallet.getAddresses(); 49 | 50 | Assert.assertNotNull(addresses); 51 | Assert.assertFalse(addresses.isEmpty()); 52 | Assert.assertEquals(2, addresses.size()); 53 | Assert.assertTrue(addresses.contains(address1)); 54 | Assert.assertTrue(addresses.contains(address2)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/state/TrieNodeStatsVisitorTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.state; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by ajlopez on 22/10/2020. 10 | */ 11 | public class TrieNodeStatsVisitorTest { 12 | @Test 13 | public void processEmptyTrie() throws IOException { 14 | Trie trie = new Trie(); 15 | 16 | TrieNodeStatsVisitor visitor = new TrieNodeStatsVisitor(); 17 | 18 | visitor.process(trie); 19 | 20 | Assert.assertEquals(1, visitor.getNodeCounter()); 21 | Assert.assertEquals(0, visitor.getValueCounter()); 22 | Assert.assertEquals(trie.getEncoded().length, visitor.getEncodedSize()); 23 | } 24 | 25 | @Test 26 | public void processTrieWithOneValue() throws IOException { 27 | Trie trie = new Trie().put(new byte[] { 0x01 }, new byte[] { 0x02 }); 28 | 29 | TrieNodeStatsVisitor visitor = new TrieNodeStatsVisitor(); 30 | 31 | visitor.process(trie); 32 | 33 | Assert.assertEquals(1, visitor.getNodeCounter()); 34 | Assert.assertEquals(1, visitor.getValueCounter()); 35 | Assert.assertEquals(trie.getEncoded().length, visitor.getEncodedSize()); 36 | } 37 | 38 | @Test 39 | public void processTrieWithTwoValues() throws IOException { 40 | Trie trie = new Trie() 41 | .put(new byte[] { 0x01 }, new byte[] { 0x02 }) 42 | .put(new byte[] { 0x02 }, new byte[] { 0x03 }); 43 | 44 | TrieNodeStatsVisitor visitor = new TrieNodeStatsVisitor(); 45 | 46 | visitor.process(trie); 47 | 48 | Assert.assertEquals(3, visitor.getNodeCounter()); 49 | Assert.assertEquals(2, visitor.getValueCounter()); 50 | Assert.assertTrue(trie.getEncoded().length < visitor.getEncodedSize()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/execution/BlockExecutionResult.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.execution; 2 | 3 | import com.ajlopez.blockchain.core.TransactionReceipt; 4 | import com.ajlopez.blockchain.core.types.Hash; 5 | import com.ajlopez.blockchain.merkle.MerkleTreeBuilder; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by ajlopez on 09/06/2020. 11 | */ 12 | public class BlockExecutionResult { 13 | private final Hash stateRootHash; 14 | private final List transactionReceipts; 15 | 16 | private Hash transactionReceiptsHash; 17 | 18 | public BlockExecutionResult(Hash stateRootHash, List transactionReceipts) { 19 | this.stateRootHash = stateRootHash; 20 | this.transactionReceipts = transactionReceipts; 21 | } 22 | 23 | public Hash getStateRootHash() { return this.stateRootHash; } 24 | 25 | public List getTransactionReceipts() { 26 | return this.transactionReceipts; 27 | } 28 | 29 | public Hash getTransactionReceiptsHash() { 30 | if (this.transactionReceiptsHash != null) 31 | return this.transactionReceiptsHash; 32 | 33 | this.transactionReceiptsHash = calculateTransactionReceiptsHash(this.transactionReceipts); 34 | 35 | return this.transactionReceiptsHash; 36 | } 37 | 38 | public static Hash calculateTransactionReceiptsHash(List transactionReceipts) { 39 | MerkleTreeBuilder merkleTreeBuilder = new MerkleTreeBuilder(); 40 | 41 | if (transactionReceipts == null) 42 | return merkleTreeBuilder.build().getHash(); 43 | 44 | for (TransactionReceipt transactionReceipt : transactionReceipts) 45 | merkleTreeBuilder.add(transactionReceipt.getHash()); 46 | 47 | return merkleTreeBuilder.build().getHash(); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/encoding/BlockInformationEncoderTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.encoding; 2 | 3 | import com.ajlopez.blockchain.bc.BlockInformation; 4 | import com.ajlopez.blockchain.core.types.BlockHash; 5 | import com.ajlopez.blockchain.core.types.Difficulty; 6 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 7 | import org.junit.Assert; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.rules.ExpectedException; 11 | 12 | /** 13 | * Created by ajlopez on 17/03/2020. 14 | */ 15 | public class BlockInformationEncoderTest { 16 | // https://www.infoq.com/news/2009/07/junit-4.7-rules 17 | @Rule 18 | public ExpectedException exception = ExpectedException.none(); 19 | 20 | @Test 21 | public void encodeAndDecode() { 22 | BlockHash blockHash = FactoryHelper.createRandomBlockHash(); 23 | Difficulty totalDifficulty = Difficulty.fromUnsignedLong(42); 24 | 25 | BlockInformation blockInformation = new BlockInformation(blockHash, totalDifficulty); 26 | 27 | byte[] encoded = BlockInformationEncoder.encode(blockInformation); 28 | 29 | Assert.assertNotNull(encoded); 30 | 31 | BlockInformation result = BlockInformationEncoder.decode(encoded); 32 | 33 | Assert.assertNotNull(result); 34 | Assert.assertEquals(blockHash, result.getBlockHash()); 35 | Assert.assertEquals(totalDifficulty, result.getTotalDifficulty()); 36 | } 37 | 38 | @Test 39 | public void decodeInvalidEncodedBlockInformation() { 40 | byte[] bytes = FactoryHelper.createRandomBytes(42); 41 | byte[] encoded = RLP.encodeList(RLP.encode(bytes)); 42 | 43 | exception.expect(IllegalArgumentException.class); 44 | exception.expectMessage("Invalid block information encoding"); 45 | BlockInformationEncoder.decode(encoded); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/encoding/LogEncoder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.encoding; 2 | 3 | import com.ajlopez.blockchain.core.types.Address; 4 | import com.ajlopez.blockchain.core.types.DataWord; 5 | import com.ajlopez.blockchain.vms.eth.Log; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by ajlopez on 06/06/2020. 12 | */ 13 | public class LogEncoder { 14 | private LogEncoder() { } 15 | 16 | public static byte[] encode(Log log) { 17 | byte[] rlpAddress = RLPEncoder.encodeAddress(log.getAddress()); 18 | byte[] rlpData = RLP.encode(log.getData()); 19 | byte[] rlpTopics = RLPEncoder.encodeDataWords(log.getTopics()); 20 | 21 | return RLP.encodeList(rlpAddress, rlpData, rlpTopics); 22 | } 23 | 24 | public static Log decode(byte[] encoded) { 25 | byte[][] bytes = RLP.decodeList(encoded); 26 | 27 | if (bytes.length != 3) 28 | throw new IllegalArgumentException("Invalid log encoding"); 29 | 30 | Address address = RLPEncoder.decodeAddress(bytes[0]); 31 | byte[] data = RLP.decode(bytes[1]); 32 | List topics = RLPEncoder.decodeDataWords(bytes[2]); 33 | 34 | return new Log(address, data, topics); 35 | } 36 | 37 | public static byte[] encodeList(List logs) { 38 | byte[][] bytes = new byte[logs.size()][]; 39 | 40 | for (int k = 0; k < logs.size(); k++) 41 | bytes[k] = encode(logs.get(k)); 42 | 43 | return RLP.encodeList(bytes); 44 | } 45 | 46 | public static List decodeList(byte[] encoded) { 47 | byte[][] bytes = RLP.decodeList(encoded); 48 | List logs = new ArrayList<>(bytes.length); 49 | 50 | for (int k = 0; k < bytes.length; k++) 51 | logs.add(decode(bytes[k])); 52 | 53 | return logs; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/ajlopez/blockchain/processors/PeerProcessorTest.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.processors; 2 | 3 | import com.ajlopez.blockchain.core.types.Difficulty; 4 | import com.ajlopez.blockchain.net.PeerId; 5 | import com.ajlopez.blockchain.net.Status; 6 | import com.ajlopez.blockchain.test.utils.FactoryHelper; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | /** 11 | * Created by ajlopez on 10/02/2018. 12 | */ 13 | public class PeerProcessorTest { 14 | @Test 15 | public void noBestBlockNumberInNewPeer() { 16 | PeerProcessor processor = new PeerProcessor(1); 17 | PeerId peerId = FactoryHelper.createRandomPeerId(); 18 | 19 | Assert.assertNull(processor.getStatus(peerId)); 20 | } 21 | 22 | @Test 23 | public void registerPeerBestBlockNumber() { 24 | PeerProcessor processor = new PeerProcessor(1); 25 | long bestBlockNumber = 100; 26 | PeerId peerId = FactoryHelper.createRandomPeerId(); 27 | Status status = new Status(peerId, 1, bestBlockNumber, FactoryHelper.createRandomBlockHash(), Difficulty.ONE); 28 | 29 | processor.registerStatus(status); 30 | 31 | Status result = processor.getStatus(peerId); 32 | 33 | Assert.assertNotNull(result); 34 | Assert.assertSame(status, result); 35 | } 36 | 37 | @Test 38 | public void registerPeerBestBlockNumberFromAnotherNetworkNumber() { 39 | PeerProcessor processor = new PeerProcessor(1); 40 | PeerId peerId = FactoryHelper.createRandomPeerId(); 41 | long bestBlockNumber = 100; 42 | 43 | Status status = new Status(peerId, 2, bestBlockNumber, FactoryHelper.createRandomBlockHash(), Difficulty.ONE); 44 | 45 | processor.registerStatus(status); 46 | 47 | Assert.assertNull(processor.getStatus(peerId)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/ajlopez/blockchain/json/JsonObjectBuilder.java: -------------------------------------------------------------------------------- 1 | package com.ajlopez.blockchain.json; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by ajlopez on 04/11/2018. 8 | */ 9 | public class JsonObjectBuilder extends JsonBuilder { 10 | private JsonBuilder parent; 11 | private String name; 12 | Map properties = new LinkedHashMap<>(); 13 | 14 | public JsonObjectBuilder(JsonBuilder parent) { 15 | this.parent = parent; 16 | } 17 | 18 | @Override 19 | public JsonBuilder value(int value) { 20 | super.value(value); 21 | properties.put(this.name, super.build()); 22 | 23 | return this; 24 | } 25 | 26 | public JsonBuilder value(long value) { 27 | super.value(value); 28 | properties.put(this.name, super.build()); 29 | 30 | return this; 31 | } 32 | 33 | @Override 34 | public JsonBuilder value(boolean value) { 35 | super.value(value); 36 | properties.put(this.name, super.build()); 37 | 38 | return this; 39 | } 40 | 41 | @Override 42 | public JsonBuilder value(String value) { 43 | super.value(value); 44 | properties.put(this.name, super.build()); 45 | 46 | return this; 47 | } 48 | 49 | @Override 50 | public JsonBuilder value(JsonValue value) { 51 | properties.put(this.name, value); 52 | 53 | return this; 54 | } 55 | 56 | @Override 57 | public JsonBuilder name(String name) { 58 | this.name = name; 59 | 60 | return this; 61 | } 62 | 63 | @Override 64 | public JsonValue build() { 65 | return new JsonObjectValue(this.properties); 66 | } 67 | 68 | @Override 69 | public JsonBuilder end() { 70 | parent.value(this.build()); 71 | 72 | return parent; 73 | } 74 | } 75 | --------------------------------------------------------------------------------