Initial changes for poc6 networking

This commit is contained in:
nicksavers 2014-09-21 11:59:04 +02:00
parent b09a73678e
commit 6c1de3e483
23 changed files with 661 additions and 619 deletions

View File

@ -42,7 +42,7 @@ import static org.ethereum.core.Denomination.SZABO;
* <li>Let S_FINAL be S[n], but adding the block reward paid to the miner.</li>
* <li>Check if S_FINAL is the same as the STATE_ROOT. If it is, the block is valid; otherwise, it is not valid.</li>
* </ol>
* See <a href="https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-White-Paper#blockchain-and-mining">Ethereum Whitepaper</a>
* See <a href="https://github.com/ethereum/wiki/wiki/White-Paper#blockchain-and-mining">Ethereum Whitepaper</a>
*
*
* www.ethereumJ.com
@ -104,7 +104,7 @@ public class BlockchainImpl implements Blockchain {
block.getParentHash())) {
return;
}
// if there is some blocks already keep chain continuity
// if there are some blocks already keep chain continuity
if (!blockCache.isEmpty()) {
String hashLast = Hex.toHexString(getLastBlock().getHash());
String blockParentHash = Hex.toHexString(block.getParentHash());

View File

@ -12,84 +12,85 @@ import java.util.concurrent.ConcurrentLinkedQueue;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* @author: Roman Mandeleil
* Created on: 27/07/2014 11:28
*/
public class BlockQueue {
private static Logger logger = LoggerFactory.getLogger("blockchain");
private static Logger logger = LoggerFactory.getLogger("blockchain");
private Queue<Block> blockQueue = new ConcurrentLinkedQueue<>();
private Block lastBlock;
private Queue<Block> blockQueue = new ConcurrentLinkedQueue<>();
private Block lastBlock;
private Timer timer = new Timer("BlockQueueTimer");
private Timer timer = new Timer("BlockQueueTimer");
public BlockQueue() {
public BlockQueue() {
timer.scheduleAtFixedRate (new TimerTask() {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
nudgeQueue();
}
}, 10, 10);
}
public void run() {
nudgeQueue();
}
}, 10, 10);
}
private void nudgeQueue() {
if (blockQueue.isEmpty()) return;
private void nudgeQueue() {
if (blockQueue.isEmpty())
return;
Block block = blockQueue.poll();
WorldManager.getInstance().getBlockchain().add(block);
}
Block block = blockQueue.poll();
public void addBlocks(List<Block> blockList) {
WorldManager.getInstance().getBlockchain().add(block);
}
Block lastReceivedBlock = blockList.get(blockList.size() - 1);
if (lastReceivedBlock.getNumber() != getLast().getNumber() + 1)
return;
public void addBlocks(List<Block> blockList) {
for (int i = blockList.size() - 1; i >= 0; --i) {
Block lastReceivedBlock = blockList.get(blockList.size() - 1);
if (lastReceivedBlock.getNumber() != getLast().getNumber() + 1) return;
if (blockQueue.size() > SystemProperties.CONFIG.maxBlocksQueued())
return;
for (int i = blockList.size() - 1; i >= 0; --i) {
this.lastBlock = blockList.get(i);
logger.trace("Last block now index: [ {} ]", lastBlock.getNumber());
blockQueue.add(lastBlock);
}
logger.trace("Blocks waiting to be proceed in the queue: [ {} ]",
blockQueue.size());
}
if (blockQueue.size() >
SystemProperties.CONFIG.maxBlocksQueued()) return;
public Block getLast() {
if (blockQueue.isEmpty())
return WorldManager.getInstance().getBlockchain().getLastBlock();
return lastBlock;
}
this.lastBlock = blockList.get(i);
logger.trace("Last block now index: [ {} ]", lastBlock.getNumber());
blockQueue.add(lastBlock);
}
private class BlockByIndexComparator implements Comparator<Block> {
logger.trace("Blocks waiting to be proceed in the queue: [ {} ]", blockQueue.size());
}
@Override
public int compare(Block o1, Block o2) {
public Block getLast() {
if (o1 == null || o2 == null)
throw new NullPointerException();
if (blockQueue.isEmpty())
return WorldManager.getInstance().getBlockchain().getLastBlock();
if (o1.getNumber() > o2.getNumber())
return 1;
if (o1.getNumber() < o2.getNumber())
return -1;
return lastBlock;
}
return 0;
}
}
private class BlockByIndexComparator implements Comparator<Block> {
@Override
public int compare(Block o1, Block o2) {
if (o1 == null || o2 == null ) throw new NullPointerException();
if (o1.getNumber() > o2.getNumber()) return 1;
if (o1.getNumber() < o2.getNumber()) return -1;
return 0;
}
}
public int size() {
return blockQueue.size();
}
public void close(){
timer.cancel();
timer.purge();
}
public int size() {
return blockQueue.size();
}
public void close() {
timer.cancel();
timer.purge();
}
}

View File

@ -3,19 +3,89 @@ package org.ethereum.net;
import java.util.HashMap;
import java.util.Map;
/**
* A list of commands for the Ethereum network protocol.
* <br/>
* The codes for these commands are the first byte in every packet.
*
* @see <a href="https://github.com/ethereum/wiki/wiki/Wire-Protocol">
* https://github.com/ethereum/wiki/wiki/Wire-Protocol</a><br/>
* <a href="https://github.com/ethereum/cpp-ethereum/wiki/%C3%90%CE%9EVP2P-Networking">
* https://github.com/ethereum/cpp-ethereum/wiki/ÐΞVP2P-Networking</a><br/>
* <a href="https://github.com/ethereum/cpp-ethereum/wiki/PoC-6-Network-Protocol">
* https://github.com/ethereum/cpp-ethereum/wiki/PoC-6-Network-Protocol</a>
*/
public enum Command {
/* P2P */
/** [0x00, P2P_VERSION, CLIEND_ID, CAPS, LISTEN_PORT, CLIENT_ID] <br/>
* First packet sent over the connection, and sent once by both sides.
* No other messages may be sent until a Hello is received. */
HELLO(0x00),
/** [0x01, REASON] <br/>Inform the peer that a disconnection is imminent;
* if received, a peer should disconnect immediately. When sending,
* well-behaved hosts give their peers a fighting chance (read: wait 2 seconds)
* to disconnect to before disconnecting themselves. */
DISCONNECT(0x01),
/** [0x02] <br/>Requests an immediate reply of Pong from the peer. */
PING(0x02),
/** [0x03] <br/>Reply to peer's Ping packet. */
PONG(0x03),
GET_PEERS(0x10),
PEERS(0x11),
/** [0x04] <br/>Request the peer to enumerate some known peers
* for us to connect to. This should include the peer itself. */
GET_PEERS(0x04),
/** [0x05, [IP1, Port1, Id1], [IP2, Port2, Id2], ... ] <br/>
* Specifies a number of known peers. IP is a 4-byte array 'ABCD'
* that should be interpreted as the IP address A.B.C.D.
* Port is a 2-byte array that should be interpreted as a
* 16-bit big-endian integer. Id is the 512-bit hash that acts
* as the unique identifier of the node. */
PEERS(0x05),
/** [0x10, [PROTOCOL_VERSION, NETWORK_ID, TD, BEST_HASH, GENESIS_HASH] <br/>
* Inform a peer of it's current ethereum state. This message should be
* send after the initial handshake and prior to any ethereum related messages. */
STATUS(0x10),
/* Ethereum */
/** [0x12, [nonce, receiving_address, value, ... ], ... ] <br/>
* Specify (a) transaction(s) that the peer should make sure is included
* on its transaction queue. The items in the list (following the first item 0x12)
* are transactions in the format described in the main Ethereum specification. */
TRANSACTIONS(0x12),
/** [0x13, [block_header, transaction_list, uncle_list], ... ] <br/>
* Specify (a) block(s) that the peer should know about.
* The items in the list (following the first item, 0x13)
* are blocks in the format described in the main Ethereum specification. */
BLOCKS(0x13),
GET_CHAIN(0x14),
NOT_IN_CHAIN(0x15),
GET_TRANSACTIONS(0x16),
/** [0x17, [ hash : B_32, maxBlocks: P ]: <br/>
* Requests a BlockHashes message of at most maxBlocks entries,
* of block hashes from the blockchain, starting at the parent of block hash.
* Does not require the peer to give maxBlocks hashes -
* they could give somewhat fewer. */
GET_BLOCK_HASHES(0x17),
/** [0x18, [ hash_0: B_32, hash_1: B_32, .... ]: <br/>Gives a series of hashes
* of blocks (each the child of the next). This implies that the blocks
* are ordered from youngest to oldest. */
BLOCK_HASHES(0x18),
/** [0x19, [ hash_0: B_32, hash_1: B_32, .... ]: <br/>Requests a Blocks message
* detailing a number of blocks to be sent, each referred to by a hash. <br/>
* <b>Note:</b> Don't expect that the peer necessarily give you all these blocks
* in a single message - you might have to re-request them. */
GET_BLOCKS(0x19),
UNKNOWN(0xFF);
private int cmd;

View File

@ -12,63 +12,60 @@ import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* www.ethereumJ.com
* This class contains the logic for sending messages in a queue
*
* @author: Roman Mandeleil
* Created on: 09/07/2014 10:29
* Messages open by send and answered by receive of appropriate message
* GET_BLOCK_HASHES by BLOCK_HASHES
* GET_BLOCKS by BLOCKS
* PING by PONG
* GET_PEERS by PEERS
* GET_TRANSACTIONS by TRANSACTIONS
*
* The following messages will not be answered:
* PONG, PEERS, BLOCKS, TRANSACTIONS
*
* @author Roman Mandeleil
*/
public class MessageQueue {
// 1) queue for messages
// 2) process for sending messages
// 3) messages open by send and answered by receive of appropriate message
// GET_CHAIN by BLOCKS
// PING by PONG
// GET_PEERS by PEERS
// GET_TRANSACTIONS by TRANSACTIONS
// messages will not be answered: TRANSACTIONS, PONG, PEERS, TRANSACTIONS
private Logger logger = LoggerFactory.getLogger("wire");
Queue<MessageRoundtrip> messageQueue = new ConcurrentLinkedQueue<>();
ChannelHandlerContext ctx = null;
Timer timer = new Timer();
private Queue<MessageRoundtrip> messageQueue = new ConcurrentLinkedQueue<>();
private ChannelHandlerContext ctx = null;
private Timer timer = new Timer();
public MessageQueue(ChannelHandlerContext ctx) {
this.ctx = ctx;
public MessageQueue(ChannelHandlerContext ctx) {
this.ctx = ctx;
timer.scheduleAtFixedRate (new TimerTask() {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
nudgeQueue();
}
}, 10, 10);
}
public void run() {
nudgeQueue();
}
}, 10, 10);
}
public void sendMessage(Message msg) {
public void sendMessage(Message msg){
if (msg instanceof GetChainMessage && containsGetChain())
if (msg instanceof GetBlockHashesMessage && containsGetBlockHashes())
return;
messageQueue.add(new MessageRoundtrip(msg));
}
public void receivedMessage(Message msg){
public void receivedMessage(Message msg) {
if (logger.isDebugEnabled())
logger.debug("Recv: [ {} ] - [ {} ]",
msg.getMessageName(),
Hex.toHexString(msg.getPayload()));
if (null != messageQueue.peek()) {
MessageRoundtrip messageRoundtrip = messageQueue.peek();
Message waitingMessage = messageRoundtrip.getMsg();
if (waitingMessage.getAnswerMessage() == null) return;
if (waitingMessage.getAnswerMessage() == null)
return;
if (msg.getClass() == waitingMessage.getAnswerMessage()){
messageRoundtrip.answer();
@ -77,7 +74,7 @@ public class MessageQueue {
}
}
private void nudgeQueue(){
private void nudgeQueue() {
// The message was answered, remove from the queue
if (null != messageQueue.peek()) {
@ -89,18 +86,17 @@ public class MessageQueue {
}
// Now send the next message
if (null != messageQueue.peek()){
if (null != messageQueue.peek()) {
MessageRoundtrip messageRoundtrip = messageQueue.peek();
if (messageRoundtrip.getRetryTimes() == 0 ) {// todo: retry logic || messageRoundtrip.hasToRetry()){
if (messageRoundtrip.getRetryTimes() == 0 ) {
// todo: retry logic || messageRoundtrip.hasToRetry()){
Message msg = messageRoundtrip.getMsg();
sendToWire(msg);
if (msg.getAnswerMessage() == null){
if (msg.getAnswerMessage() == null)
messageQueue.remove();
}
else {
messageRoundtrip.incRetryTimes();
messageRoundtrip.saveTime();
@ -109,7 +105,7 @@ public class MessageQueue {
}
}
private void sendToWire(Message msg){
private void sendToWire(Message msg) {
if (logger.isDebugEnabled())
logger.debug("Send: [ {} ] - [ {} ]",
@ -117,19 +113,19 @@ public class MessageQueue {
Hex.toHexString(msg.getPayload()));
ByteBuf buffer = ctx.alloc().buffer(msg.getPayload().length + 8);
buffer.writeBytes(StaticMessages.MAGIC_PACKET);
buffer.writeBytes(StaticMessages.SYNC_TOKEN);
buffer.writeBytes(ByteUtil.calcPacketLength(msg.getPayload()));
buffer.writeBytes(msg.getPayload());
ctx.writeAndFlush(buffer);
}
private boolean containsGetChain(){
private boolean containsGetBlockHashes() {
Iterator<MessageRoundtrip> iterator = messageQueue.iterator();
while(iterator.hasNext()){
MessageRoundtrip msgRoundTrip = iterator.next();
if (msgRoundTrip.getMsg() instanceof GetChainMessage)
if (msgRoundTrip.getMsg() instanceof GetBlockHashesMessage)
return true;
}
return false;

View File

@ -3,15 +3,15 @@ package org.ethereum.net;
import org.ethereum.net.message.Message;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 09/07/2014 13:54
* Utility wraps around a message to keep track of the number of times it has been offered
* This class also contains the last time a message was offered and is updated
* when an answer has been received to it can be removed from the queue.
*
* @author Roman Mandeleil
*/
public class MessageRoundtrip {
Message msg = null;
private Message msg = null;
long lastTimestamp = 0;
long retryTimes = 0;
boolean answered = false;
@ -36,9 +36,12 @@ public class MessageRoundtrip {
public void incRetryTimes(){
++retryTimes;
}
public void saveTime(){lastTimestamp = System.currentTimeMillis();}
public void saveTime() {
lastTimestamp = System.currentTimeMillis();
}
public boolean hasToRetry(){
public boolean hasToRetry() {
return 20000 < System.currentTimeMillis() - lastTimestamp;
}

View File

@ -25,10 +25,6 @@ import java.util.Timer;
import java.util.TimerTask;
import static org.ethereum.net.Command.*;
import static org.ethereum.net.Command.GET_PEERS;
import static org.ethereum.net.Command.GET_TRANSACTIONS;
import static org.ethereum.net.Command.PING;
import static org.ethereum.net.Command.PONG;
import static org.ethereum.net.message.StaticMessages.*;
@ -41,16 +37,13 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
private Logger logger = LoggerFactory.getLogger("wire");
private Timer chainAskTimer = new Timer("ChainAskTimer");
private int secToAskForChain = 1;
private Timer blocksAskTimer = new Timer("ChainAskTimer");
private final Timer timer = new Timer("MiscMessageTimer");
private int secToAskForBlocks = 1;
private boolean tearDown = false;
private PeerListener peerListener;
MessageQueue msgQueue = null;
private MessageQueue msgQueue = null;
public EthereumProtocolHandler() { }
@ -91,12 +84,12 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
}
}, 2000, 10000);
chainAskTimer.scheduleAtFixedRate(new TimerTask() {
blocksAskTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
sendGetChain();
sendGetBlocks();
}
}, 1000, secToAskForChain * 1000);
}, 1000, secToAskForBlocks * 1000);
}
@ -108,11 +101,15 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
EthereumListener listener = WorldManager.getInstance().getListener();
byte command = RLP.getCommandCode(payload);
// got HELLO
if (Command.fromInt(command) == HELLO) {
logger.info("[Recv: HELLO]" );
byte commandCode = RLP.getCommandCode(payload);
Command receivedCommand = Command.fromInt(commandCode);
logger.info("[Recv: {}]", receivedCommand.name());
if (peerListener != null) peerListener.console("[Recv: " + receivedCommand.name() + "]");
switch(receivedCommand) {
case HELLO:
RLPList rlpList = RLP.decode2(payload);
HelloMessage helloMessage = new HelloMessage(rlpList);
@ -123,13 +120,8 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
listener.trace(String.format("Got handshake: [ %s ]", helloMessage.toString()));
listener.onRecvMessage(helloMessage);
}
}
// got DISCONNECT
if (Command.fromInt(command) == DISCONNECT) {
if (peerListener != null) peerListener.console("[Recv: DISCONNECT]");
break;
case DISCONNECT:
DisconnectMessage disconnectMessage = new DisconnectMessage(payload);
msgQueue.receivedMessage(disconnectMessage);
@ -138,45 +130,29 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
if (listener != null)
listener.onRecvMessage(disconnectMessage);
}
// got PING send pong
if (Command.fromInt(command) == PING) {
if (peerListener != null) peerListener.console("[Recv: PING]");
msgQueue.receivedMessage(PING_MESSAGE);
break;
case PING:
msgQueue.receivedMessage(PING_MESSAGE);
sendPong();
if (listener != null)
listener.onRecvMessage(PING_MESSAGE);
}
// got PONG mark it
if (Command.fromInt(command) == PONG) {
if (peerListener != null) peerListener.console("[Recv: PONG]");
break;
case PONG:
msgQueue.receivedMessage(PONG_MESSAGE);
if (listener != null)
listener.onRecvMessage(PONG_MESSAGE);
}
// got GETPEERS send peers
if (Command.fromInt(command) == GET_PEERS) {
if (peerListener != null) peerListener.console("[Recv: GETPEERS]");
msgQueue.receivedMessage(GET_PEERS_MESSAGE);
break;
case GET_PEERS:
msgQueue.receivedMessage(GET_PEERS_MESSAGE);
// TODO: send peer list
if (listener != null)
listener.onRecvMessage(GET_PEERS_MESSAGE);
}
// got PEERS
if (Command.fromInt(command) == PEERS) {
if (peerListener != null) peerListener.console("[Recv: PEERS]");
break;
case PEERS:
PeersMessage peersMessage = new PeersMessage(payload);
msgQueue.receivedMessage(peersMessage);
@ -187,13 +163,9 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
if (listener != null)
listener.onRecvMessage(peersMessage);
}
// got TRANSACTIONS
if (Command.fromInt(command) == TRANSACTIONS) {
if (peerListener != null) peerListener.console("Recv: TRANSACTIONS]");
TransactionsMessage transactionsMessage = new TransactionsMessage(payload);
break;
case TRANSACTIONS:
TransactionsMessage transactionsMessage = new TransactionsMessage(payload);
msgQueue.receivedMessage(transactionsMessage);
List<Transaction> txList = transactionsMessage.getTransactions();
@ -207,51 +179,44 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
if (listener != null)
listener.onRecvMessage(transactionsMessage);
}
// got BLOCKS
if (Command.fromInt(command) == BLOCKS) {
if (peerListener != null) peerListener.console("[Recv: BLOCKS]");
break;
case BLOCKS:
BlocksMessage blocksMessage = new BlocksMessage(payload);
List<Block> blockList = blocksMessage.getBlockDataList();
msgQueue.receivedMessage(blocksMessage);
// If we get one block from a peer we ask less greedy
if (blockList.size() <= 1 && secToAskForBlocks != 10) {
// If we get one block from a peer
// we ask less greedy
if (blockList.size() <= 1 && secToAskForChain != 10) {
logger.info("Now we ask for blocks each 10 seconds");
secToAskForBlocks = 10;
logger.info("Now we ask for a chain each 10 seconds");
secToAskForChain = 10;
chainAskTimer.cancel();
chainAskTimer.purge();
chainAskTimer = new Timer();
chainAskTimer.scheduleAtFixedRate(new TimerTask() {
blocksAskTimer.cancel();
blocksAskTimer.purge();
blocksAskTimer = new Timer();
blocksAskTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
sendGetChain();
sendGetBlocks();
}
}, 3000, secToAskForChain * 1000);
}, 3000, secToAskForBlocks * 1000);
}
// If we get more blocks from a peer
// we ask more greedy
if (blockList.size() > 2 && secToAskForChain != 1) {
// If we get more blocks from a peer we ask more greedy
if (blockList.size() > 2 && secToAskForBlocks != 1) {
logger.info("Now we ask for a chain each 1 seconds");
secToAskForChain = 1;
secToAskForBlocks = 1;
chainAskTimer.cancel();
chainAskTimer.purge();
chainAskTimer = new Timer();
chainAskTimer.scheduleAtFixedRate(new TimerTask() {
blocksAskTimer.cancel();
blocksAskTimer.purge();
blocksAskTimer = new Timer();
blocksAskTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
sendGetChain();
sendGetBlocks();
}
}, 3000, secToAskForChain * 1000);
}, 3000, secToAskForBlocks * 1000);
}
if (blockList.isEmpty()) return;
@ -260,57 +225,32 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
if (listener != null)
listener.onRecvMessage(blocksMessage);
}
break;
case GET_TRANSACTIONS:
// got GETCHAIN
if (Command.fromInt(command) == GET_CHAIN) {
logger.info("[Recv: GET_CHAIN]");
if (peerListener != null) peerListener.console("[Recv: GET_CHAIN]");
RLPList rlpList = RLP.decode2(payload);
GetChainMessage getChainMessage = new GetChainMessage(rlpList);
// todo: send blocks
logger.info(getChainMessage.toString());
if (peerListener != null) peerListener.console(getChainMessage.toString());
if (listener != null)
listener.onRecvMessage(getChainMessage);
}
// got NOTINCHAIN
if (Command.fromInt(command) == NOT_IN_CHAIN) {
logger.info("[Recv: NOT_IN_CHAIN]");
if (peerListener != null) peerListener.console("[Recv: NOT_IN_CHAIN]");
RLPList rlpList = RLP.decode2(payload);
NotInChainMessage notInChainMessage = new NotInChainMessage(rlpList);
logger.info(notInChainMessage.toString());
if (peerListener != null) peerListener.console(notInChainMessage.toString());
if (listener != null)
listener.onRecvMessage(notInChainMessage);
}
// got GETTRANSACTIONS
if (Command.fromInt(command) == GET_TRANSACTIONS) {
logger.info("[Recv: GET_TRANSACTIONS]");
if (peerListener != null) peerListener.console("[Recv: GET_TRANSACTIONS]");
// TODO: return it in the future
// Collection<Transaction> pendingTxList =
// MainData.instance.getBlockchain().getPendingTransactionList();
// TransactionsMessage txMsg =
// new TransactionsMessage(new ArrayList(pendingTxList));
// TODO Implement GET_TRANSACTIONS command
// List<Transaction> pendingTxList =
// WorldManager.getInstance().getBlockchain().getPendingTransactionList();
// TransactionsMessage txMsg = new TransactionsMessage(pendingTxList);
// sendMsg(txMsg, ctx);
if (listener != null)
listener.onRecvMessage(GET_TRANSACTIONS_MESSAGE);
break;
case GET_BLOCK_HASHES:
// TODO Implement GET_BLOCK_HASHES command
break;
case BLOCK_HASHES:
// TODO Implement BLOCK_HASHES command
break;
case GET_BLOCKS:
// TODO Implement GET_BLOCKS command
break;
default:
// do nothing and ignore this command
break;
}
}
@ -354,7 +294,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
sendMsg(GET_TRANSACTIONS_MESSAGE);
}
private void sendGetChain() {
private void sendGetBlocks() {
if (WorldManager.getInstance().getBlockchain().getBlockQueue().size() >
SystemProperties.CONFIG.maxBlocksQueued()) return;
@ -363,14 +303,14 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
if (lastBlock == null) return;
byte[] hash = lastBlock.getHash();
GetChainMessage chainMessage =
new GetChainMessage( SystemProperties.CONFIG.maxBlocksAsk(), hash);
sendMsg(chainMessage);
GetBlocksMessage getBlocksMessage = new GetBlocksMessage(
SystemProperties.CONFIG.maxBlocksAsk(), hash);
sendMsg(getBlocksMessage);
}
public void killTimers(){
chainAskTimer.cancel();
chainAskTimer.purge();
blocksAskTimer.cancel();
blocksAskTimer.purge();
timer.cancel();
timer.purge();

View File

@ -0,0 +1,29 @@
package org.ethereum.net.message;
public class BlockHashesMessage extends Message {
@Override
public void parseRLP() {
// TODO Auto-generated method stub
}
@Override
public byte[] getPayload() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getMessageName() {
// TODO Auto-generated method stub
return null;
}
@Override
public Class getAnswerMessage() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -6,7 +6,7 @@ import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import static org.ethereum.net.Command.DISCONNECT;
import static org.ethereum.net.message.ReasonCode.DISCONNECT_REQUESTED;
import static org.ethereum.net.message.ReasonCode.REQUESTED;
/**
* www.ethereumJ.com
@ -22,7 +22,6 @@ public class DisconnectMessage extends Message {
this.payload = payload;
}
public DisconnectMessage(RLPList rawData) {
super(rawData);
}
@ -38,7 +37,7 @@ public class DisconnectMessage extends Message {
byte[] reasonB = ((RLPItem)paramsList.get(1)).getRLPData();
if (reasonB == null) {
this.reason = DISCONNECT_REQUESTED;
this.reason = REQUESTED;
} else {
this.reason = ReasonCode.fromInt(reasonB[0]);
}

View File

@ -0,0 +1,24 @@
package org.ethereum.net.message;
public class GetBlockHashesMessage extends Message {
@Override
public void parseRLP() {
}
@Override
public byte[] getPayload() {
return null;
}
@Override
public String getMessageName() {
return "GetBlockHashes";
}
@Override
public Class<BlockHashesMessage> getAnswerMessage() {
return BlockHashesMessage.class;
}
}

View File

@ -1,106 +1,87 @@
package org.ethereum.net.message;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import static org.ethereum.net.Command.GET_CHAIN;
import org.ethereum.net.Command;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
/**
* www.ethereumJ.com
* @author: Roman Mandeleil
* Created on: 06/04/14 14:56
*/
public class GetChainMessage extends Message {
private List<byte[]> blockHashList = new ArrayList<byte[]>();
private BigInteger blockNum;
public GetChainMessage(RLPList rawData) {
super(rawData);
}
// TODO: it get's byte for now change it to int
public GetChainMessage(byte number , byte[]... blockHashList) {
byte[][] encodedElements = new byte[blockHashList.length + 2][];
encodedElements[0] = new byte[]{0x14};
int i = 1;
for (byte[] hash : blockHashList) {
this.blockHashList.add(hash);
byte[] element = RLP.encodeElement(hash);
encodedElements[i] = element;
++i;
}
encodedElements[i] = RLP.encodeByte(number);
this.payload = RLP.encodeList(encodedElements);
this.parsed = true;
}
@Override
public void parseRLP() {
RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0]) != GET_CHAIN) {
throw new Error("GetChain: parsing for mal data");
}
int size = paramsList.size();
for (int i = 1; i < size - 1; ++i) {
blockHashList.add(((RLPItem) paramsList.get(i)).getRLPData());
}
// the last element is the num of requested blocks
byte[] blockNumB = ((RLPItem)paramsList.get(size - 1)).getRLPData();
this.blockNum = new BigInteger(blockNumB);
this.parsed = true;
// TODO: what to do when mal data ?
}
@Override
public byte[] getPayload() {
return payload;
}
public List<byte[]> getBlockHashList() {
if (!parsed) parseRLP();
return blockHashList;
}
public BigInteger getBlockNum() {
if (!parsed) parseRLP();
return blockNum;
}
@Override
public String getMessageName() {
return "GetChain";
}
@Override
public Class<BlocksMessage> getAnswerMessage() {
return BlocksMessage.class;
}
public String toString() {
if (!parsed) parseRLP();
StringBuffer sb = new StringBuffer();
for (byte[] blockHash : blockHashList) {
sb.append("").append(Hex.toHexString(blockHash)).append(", ");
}
sb.append(" blockNum=").append(blockNum);
return "GetChain Message [" + sb.toString() + " ]";
}
}
package org.ethereum.net.message;
import static org.ethereum.net.Command.GET_BLOCKS;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.ethereum.net.Command;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
public class GetBlocksMessage extends Message {
private List<byte[]> blockHashList = new ArrayList<byte[]>();
private BigInteger blockNum;
public GetBlocksMessage(RLPList rawData) {
super(rawData);
}
// TODO: it get's byte for now change it to int
public GetBlocksMessage(byte number , byte[]... blockHashList) {
byte[][] encodedElements = new byte[blockHashList.length + 2][];
encodedElements[0] = new byte[]{0x14};
int i = 1;
for (byte[] hash : blockHashList) {
this.blockHashList.add(hash);
byte[] element = RLP.encodeElement(hash);
encodedElements[i] = element;
++i;
}
encodedElements[i] = RLP.encodeByte(number);
this.payload = RLP.encodeList(encodedElements);
this.parsed = true;
}
@Override
public void parseRLP() {
RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem) (paramsList).get(0)).getRLPData()[0]) != GET_BLOCKS)
throw new Error("GetBlocks: parsing for mal data");
int size = paramsList.size();
for (int i = 1; i < size - 1; ++i) {
blockHashList.add(((RLPItem) paramsList.get(i)).getRLPData());
}
// the last element is the num of requested blocks
byte[] blockNumB = ((RLPItem) paramsList.get(size - 1)).getRLPData();
this.blockNum = new BigInteger(blockNumB);
this.parsed = true;
// TODO: what to do when mal data ?
}
@Override
public byte[] getPayload() {
return payload;
}
@Override
public String getMessageName() {
return "GetBlocks";
}
@Override
public Class<BlocksMessage> getAnswerMessage() {
return BlocksMessage.class;
}
public String toString() {
if (!parsed) parseRLP();
StringBuffer sb = new StringBuffer();
for (byte[] blockHash : blockHashList)
sb.append("").append(Hex.toHexString(blockHash)).append(", ");
sb.append(" blockNum=").append(blockNum);
return "GetBlocks Message [" + sb.toString() + " ]";
}
}

View File

@ -3,18 +3,20 @@ package org.ethereum.net.message;
import org.spongycastle.util.encoders.Hex;
import static org.ethereum.net.Command.HELLO;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import java.math.BigInteger;
import java.nio.ByteBuffer;
/**
* www.ethereumJ.com
* @author: Roman Mandeleil
* Created on: 06/04/14 14:56
* Wrapper around an Ethereum HelloMessage on the network
*
* @see {@link org.ethereum.net.Command#HELLO}
*
* @author Roman Mandeleil
*/
public class HelloMessage extends Message {
@ -24,18 +26,30 @@ public class HelloMessage extends Message {
private byte capabilities;
private short peerPort;
private byte[] peerId;
/** Total difficulty of the best chain as found in block header. */
private byte[] totalDifficulty;
/** The hash of the best (i.e. highest TD) known block. */
private byte[] bestHash;
/** The hash of the Genesis block */
private byte[] genesisHash;
public HelloMessage(RLPList rawData) {
super(rawData);
}
public HelloMessage(byte protocolVersion, byte networkId, String clientId, byte capabilities, short peerPort, byte[] peerId) {
public HelloMessage(byte protocolVersion, byte networkId, String clientId,
byte capabilities, short peerPort, byte[] peerId, byte[] totalDifficulty,
byte[] bestHash, byte[] genesisHash) {
this.protocolVersion = protocolVersion;
this.networkId = networkId;
this.clientId = clientId;
this.capabilities = capabilities;
this.peerPort = peerPort;
this.peerId = peerId;
this.totalDifficulty = totalDifficulty;
this.bestHash = bestHash;
this.genesisHash = genesisHash;
this.parsed = true;
}
@ -49,35 +63,48 @@ public class HelloMessage extends Message {
if (((RLPItem)paramsList.get(0)).getRLPData() != null) {
throw new Error("HelloMessage: parsing for mal data");
}
this.protocolVersion = ((RLPItem) paramsList.get(1)).getRLPData()[0];
byte[] networkIdBytes = ((RLPItem) paramsList.get(2)).getRLPData();
this.networkId = networkIdBytes == null ? 0 : networkIdBytes[0];
byte[] clientIdBytes = ((RLPItem) paramsList.get(3)).getRLPData();
this.clientId = new String(clientIdBytes != null ? clientIdBytes : EMPTY_BYTE_ARRAY);
this.capabilities = ((RLPItem) paramsList.get(4)).getRLPData()[0];
this.protocolVersion = ((RLPItem) paramsList.get(1)).getRLPData()[0];
byte[] networkIdBytes = ((RLPItem) paramsList.get(2)).getRLPData();
this.networkId = networkIdBytes == null ? 0 : networkIdBytes[0] ;
byte[] idData = ((RLPItem) paramsList.get(3)).getRLPData();
this.clientId = new String(idData != null ? idData : new byte[0]);
this.capabilities = ((RLPItem) paramsList.get(4)).getRLPData()[0];
ByteBuffer bb = ByteBuffer.wrap(((RLPItem) paramsList.get(5)).getRLPData());
this.peerPort = new BigInteger(bb.array()).shortValue();
this.peerId = ((RLPItem) paramsList.get(6)).getRLPData();
byte[] peerPortBytes = ((RLPItem) paramsList.get(5)).getRLPData();
this.peerPort = new BigInteger(peerPortBytes).shortValue();
this.peerId = ((RLPItem) paramsList.get(6)).getRLPData();
this.totalDifficulty = ((RLPItem) paramsList.get(7)).getRLPData();
this.bestHash = ((RLPItem) paramsList.get(8)).getRLPData();
this.genesisHash = ((RLPItem) paramsList.get(9)).getRLPData();
this.parsed = true;
// TODO: what to do when mal data ?
}
public byte[] getPayload() {
byte[] command = RLP.encodeByte(HELLO.asByte());
byte[] protocolVersion = RLP.encodeByte(this.protocolVersion);
byte[] networkId = RLP.encodeByte(this.networkId);
byte[] clientId = RLP.encodeString(this.clientId);
byte[] capabilities = RLP.encodeByte(this.capabilities);
byte[] peerPort = RLP.encodeShort(this.peerPort);
byte[] peerId = RLP.encodeElement(this.peerId);
byte[] command = RLP.encodeByte(HELLO.asByte());
byte[] protocolVersion = RLP.encodeByte(this.protocolVersion);
byte[] networkId = RLP.encodeByte(this.networkId);
byte[] clientId = RLP.encodeString(this.clientId);
byte[] capabilities = RLP.encodeByte(this.capabilities);
byte[] peerPort = RLP.encodeShort(this.peerPort);
byte[] peerId = RLP.encodeElement(this.peerId);
byte[] totalDifficulty = RLP.encodeElement(this.totalDifficulty);
byte[] bestHash = RLP.encodeElement(this.bestHash);
byte[] genesisHash = RLP.encodeElement(this.genesisHash);
byte[] data = RLP.encodeList(command, protocolVersion, networkId,
clientId, capabilities, peerPort, peerId);
byte[] data = RLP.encodeList(command, protocolVersion, networkId,
clientId, capabilities, peerPort, peerId, totalDifficulty,
bestHash, genesisHash);
return data;
}
@ -116,14 +143,29 @@ public class HelloMessage extends Message {
if (!parsed) parseRLP();
return peerId;
}
public byte[] getTotalDifficulty() {
if (!parsed) parseRLP();
return totalDifficulty;
}
@Override
public byte[] getBestHash() {
if (!parsed) parseRLP();
return bestHash;
}
public byte[] getGenesisHash() {
if (!parsed) parseRLP();
return genesisHash;
}
@Override
public String getMessageName() {
return "HelloMessage";
}
@Override
public Class getAnswerMessage() {
public Class<?> getAnswerMessage() {
return null;
}
@ -136,6 +178,9 @@ public class HelloMessage extends Message {
" capabilities=" + this.capabilities + " " +
" peerPort=" + this.peerPort + " " +
" peerId=" + Hex.toHexString(this.peerId) + " " +
" totalDifficulty=" + Hex.toHexString(this.totalDifficulty) + " " +
" bestHash=" + Hex.toHexString(this.bestHash) + " " +
" genesisHash=" + Hex.toHexString(this.genesisHash) + " " +
"]";
}
}

View File

@ -20,12 +20,11 @@ public abstract class Message {
parsed = false;
}
public abstract void parseRLP();
public abstract byte[] getPayload();
public abstract void parseRLP();
public abstract String getMessageName();
public abstract Class getAnswerMessage();
public abstract byte[] getPayload();
public abstract String getMessageName();
public abstract Class<?> getAnswerMessage();
}

View File

@ -1,58 +0,0 @@
package org.ethereum.net.message;
import static org.ethereum.net.Command.NOT_IN_CHAIN;
import org.ethereum.net.Command;
import org.ethereum.net.message.Message;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
/**
* www.ethereumJ.com
* @author: Roman Mandeleil
* Created on: 06/04/14 14:56
*/
public class NotInChainMessage extends Message {
private byte[] hash;
public NotInChainMessage(RLPList rawData) {
super(rawData);
}
@Override
public void parseRLP() {
RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0] & 0xFF) != NOT_IN_CHAIN) {
throw new Error("NotInChain Message: parsing for mal data");
}
hash = ((RLPItem)paramsList.get(1)).getRLPData();
}
@Override
public byte[] getPayload() {
return null;
}
public byte[] getHash() {
return hash;
}
@Override
public String getMessageName() {
return "NotInChain";
}
@Override
public Class getAnswerMessage() {
return null;
}
public String toString() {
if (!parsed)
parseRLP();
return "NotInChain Message [" + Hex.toHexString(hash) + "]";
}
}

View File

@ -3,9 +3,13 @@ package org.ethereum.net.message;
import java.util.HashMap;
import java.util.Map;
/**
* Reason is an optional integer specifying one
* of a number of reasons for disconnect
*/
public enum ReasonCode {
DISCONNECT_REQUESTED(0x00),
REQUESTED(0x00),
TCP_ERROR(0x01),
BAD_PROTOCOL(0x02),
USELESS_PEER(0x03),

View File

@ -1,7 +1,10 @@
package org.ethereum.net.message;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.Block;
import org.ethereum.core.Genesis;
import org.ethereum.crypto.HashUtil;
import org.ethereum.manager.WorldManager;
import org.spongycastle.util.encoders.Hex;
/**
@ -11,21 +14,20 @@ import org.spongycastle.util.encoders.Hex;
*/
public class StaticMessages {
public final static GetTransactionsMessage
GET_TRANSACTIONS_MESSAGE = new GetTransactionsMessage();
public final static GetPeersMessage GET_PEERS_MESSAGE = new GetPeersMessage();
public final static PingMessage PING_MESSAGE = new PingMessage();
public final static PongMessage PONG_MESSAGE = new PongMessage();
public final static GetPeersMessage GET_PEERS_MESSAGE = new GetPeersMessage();
public final static GetTransactionsMessage GET_TRANSACTIONS_MESSAGE = new GetTransactionsMessage();
public static final byte[] PING = Hex.decode("2240089100000002C102");
public static final byte[] PONG = Hex.decode("2240089100000002C103");
public static final byte[] GET_PEERS = Hex.decode("2240089100000002C110");
public static final byte[] PING_PACKET = Hex.decode("2240089100000002C102");
public static final byte[] PONG_PACKET = Hex.decode("2240089100000002C103");
public static final byte[] GET_PEERS_PACKET = Hex.decode("2240089100000002C110");
public static final byte[] GET_TRANSACTIONS = Hex.decode("2240089100000002C116");
public static final byte[] DISCONNECT_08 = Hex.decode("2240089100000003C20108");
public static final byte[] MAGIC_PACKET = Hex.decode("22400891");
public static final byte[] SYNC_TOKEN = Hex.decode("22400891");
public static final byte[] GENESIS_HASH = Genesis.getInstance().getHash();
static {
HELLO_MESSAGE = generateHelloMessage();
@ -45,8 +47,12 @@ public class StaticMessages {
String helloAnnouncement = String.format("Ethereum(J)/v%s/%s/%s/Java", version, phrase, system);
Block lastBlock = WorldManager.getInstance().getBlockchain().getLastBlock();
byte[] totalDifficulty = lastBlock.getDifficulty();
byte[] bestHash = lastBlock.getHash();
return new HelloMessage((byte) 0x21, (byte) 0x00,
helloAnnouncement, Byte.parseByte("00000111", 2),
(short) 30303, peerIdBytes);
(short) 30303, peerIdBytes, totalDifficulty, bestHash, GENESIS_HASH);
}
}

View File

@ -0,0 +1,85 @@
package org.ethereum.net.message;
import static org.ethereum.net.Command.STATUS;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
/**
* Wrapper around an Ethereum StatusMessage on the network
*
* @see {@link org.ethereum.net.Command#STATUS}
*/
public class StatusMessage extends Message {
private byte protocolVersion;
private byte networkId;
/** Total difficulty of the best chain as found in block header. */
private byte[] totalDifficulty;
/** The hash of the best (i.e. highest TD) known block. */
private byte[] bestHash;
/** The hash of the Genesis block */
private byte[] genesisHash;
public StatusMessage(RLPList rawData) {
super(rawData);
}
public StatusMessage(byte protocolVersion, byte networkId,
byte[] totalDifficulty, byte[] bestHash, byte[] genesisHash) {
this.protocolVersion = protocolVersion;
this.networkId = networkId;
this.totalDifficulty = totalDifficulty;
this.bestHash = bestHash;
this.genesisHash = genesisHash;
this.parsed = true;
}
@Override
public void parseRLP() {
RLPList paramsList = (RLPList) rawData.get(0);
/* the message does not distinguish between the 0 and null
* so check command code for null */
// TODO: find out if it can be 00
if (((RLPItem) paramsList.get(0)).getRLPData() != null)
throw new Error("StaticMessage: parsing for mal data");
this.protocolVersion = ((RLPItem) paramsList.get(1)).getRLPData()[0];
byte[] networkIdBytes = ((RLPItem) paramsList.get(2)).getRLPData();
this.networkId = networkIdBytes == null ? 0 : networkIdBytes[0];
this.totalDifficulty = ((RLPItem) paramsList.get(3)).getRLPData();
this.bestHash = ((RLPItem) paramsList.get(4)).getRLPData();
this.genesisHash = ((RLPItem) paramsList.get(5)).getRLPData();
this.parsed = true;
}
@Override
public byte[] getPayload() {
byte[] command = RLP.encodeByte(STATUS.asByte());
byte[] protocolVersion = RLP.encodeByte(this.protocolVersion);
byte[] networkId = RLP.encodeByte(this.networkId);
byte[] totalDifficulty = RLP.encodeElement(this.totalDifficulty);
byte[] bestHash = RLP.encodeElement(this.bestHash);
byte[] genesisHash = RLP.encodeElement(this.genesisHash);
byte[] data = RLP.encodeList(command, protocolVersion, networkId,
totalDifficulty, bestHash, genesisHash);
return data;
}
@Override
public String getMessageName() {
return "StatusMessage";
}
@Override
public Class<?> getAnswerMessage() {
return null;
}
}

View File

@ -36,22 +36,20 @@ public class TransactionsMessage extends Message {
for (Transaction tx : transactionList) {
byte[] txPayload = tx.getEncoded();
try {
baos.write(txPayload);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
try {
baos.write(txPayload);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
byte[][] elements = new byte[transactionList.size() + 1][];
elements[0] = new byte[]{Command.TRANSACTIONS.asByte()};
for (int i = 0; i < transactionList.size(); ++i) {
for (int i = 0; i < transactionList.size(); ++i)
elements[i + 1] = transactionList.get(i).getEncoded();
}
payload = RLP.encodeList(elements);
}
public TransactionsMessage(byte[] payload) {
super(RLP.decode2(payload));
this.payload = payload;
@ -66,9 +64,8 @@ public class TransactionsMessage extends Message {
public void parseRLP() {
RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0] & 0xFF) != TRANSACTIONS) {
throw new Error("TransactionMessage: parsing for mal data");
}
if (Command.fromInt(((RLPItem) (paramsList).get(0)).getRLPData()[0] & 0xFF) != TRANSACTIONS)
throw new Error("TransactionMessage: parsing for mal data");
transactions = new ArrayList<Transaction>();
int size = paramsList.size();
@ -103,9 +100,8 @@ public class TransactionsMessage extends Message {
public String toString() {
if(!parsed) parseRLP();
StringBuffer sb = new StringBuffer();
for (Transaction transaction : transactions) {
for (Transaction transaction : transactions)
sb.append(" ").append(transaction).append("\n");
}
return "Transactions Message [\n" + sb.toString() + " ]";
}
}
}

View File

@ -145,15 +145,15 @@ public class EthereumPeerTasterHandler extends ChannelInboundHandlerAdapter {
}
private void sendPing(ChannelHandlerContext ctx) {
ByteBuf buffer = ctx.alloc().buffer(StaticMessages.PING.length);
buffer.writeBytes(StaticMessages.PING);
ByteBuf buffer = ctx.alloc().buffer(StaticMessages.PING_PACKET.length);
buffer.writeBytes(StaticMessages.PING_PACKET);
ctx.writeAndFlush(buffer);
}
private void sendPong(ChannelHandlerContext ctx) {
logger.info("[Send: PONG]");
ByteBuf buffer = ctx.alloc().buffer(StaticMessages.PONG.length);
buffer.writeBytes(StaticMessages.PONG);
ByteBuf buffer = ctx.alloc().buffer(StaticMessages.PONG_PACKET.length);
buffer.writeBytes(StaticMessages.PONG_PACKET);
ctx.writeAndFlush(buffer);
}
@ -165,8 +165,8 @@ public class EthereumPeerTasterHandler extends ChannelInboundHandlerAdapter {
}
private void sendGetPeers(ChannelHandlerContext ctx) {
ByteBuf buffer = ctx.alloc().buffer(StaticMessages.GET_PEERS.length);
buffer.writeBytes(StaticMessages.GET_PEERS);
ByteBuf buffer = ctx.alloc().buffer(StaticMessages.GET_PEERS_PACKET.length);
buffer.writeBytes(StaticMessages.GET_PEERS_PACKET);
ctx.writeAndFlush(buffer);
}

View File

@ -108,9 +108,8 @@ public class Program {
this.pc = pc.intValue();
if (this.pc == ops.length) {
if (this.pc == ops.length)
stop();
}
if (this.pc > ops.length) {
stop();
@ -154,9 +153,8 @@ public class Program {
}
public DataWord stackPop() {
if (stack.size() == 0) {
if (stack.size() == 0)
throw new EmptyStackException();
}
return stack.pop();
}
@ -174,9 +172,7 @@ public class Program {
}
public int getMemSize() {
int memSize = 0;
if (memory != null) memSize = memory.limit();
return memSize;
return memory != null ? memory.limit() : 0;
}
public void memorySave(DataWord addrB, DataWord value) {
@ -361,7 +357,7 @@ public class Program {
* That method is for internal code invocations
*
* - Normal calls invoke a specified contract which updates itself
* - Stateless calls invoke another contract, within the context of the caller
* - Stateless calls invoke code from another contract, within the context of the caller
*
* @param msg is the message call object
*/
@ -452,11 +448,10 @@ public class Program {
if (buffer != null && allocSize > 0) {
int retSize = buffer.limit();
int offset = msg.getOutDataOffs().intValue();
if (retSize > allocSize) {
if (retSize > allocSize)
this.memorySave(offset, buffer.array());
} else {
else
this.memorySave(offset, allocSize, buffer.array());
}
}
}
@ -609,10 +604,7 @@ public class Program {
byte value = memory.get(i);
// Check if value is ASCII
// (should be until 0x7e - but using 0x7f
// to be compatible with cpp-ethereum)
// See: https://github.com/ethereum/cpp-ethereum/issues/299
String character = ((byte)0x20 <= value && value <= (byte)0x7f) ? new String(new byte[]{value}) : "?";
String character = ((byte) 0x20 <= value && value <= (byte) 0x7e) ? new String(new byte[] { value }) : "?";
firstLine.append(character).append("");
secondLine.append(Utils.oneByteToHexString(value)).append(" ");
@ -706,21 +698,19 @@ public class Program {
globalOutput.append(" -- MEMORY -- ").append(memoryData).append("\n");
globalOutput.append(" -- STORAGE -- ").append(storageData).append("\n");
if (result.getHReturn() != null) {
globalOutput.append("\n HReturn: ").append(Hex.toHexString(result.getHReturn().array()));
}
if (result.getHReturn() != null)
globalOutput.append("\n HReturn: ").append(
Hex.toHexString(result.getHReturn().array()));
// soffisticated assumption that msg.data != codedata
// sophisticated assumption that msg.data != codedata
// means we are calling the contract not creating it
byte[] txData = invokeData.getDataCopy(DataWord.ZERO, getDataSize());
if (!Arrays.equals(txData, ops)) {
globalOutput.append("\n msg.data: ").append(Hex.toHexString( txData ));
}
if (!Arrays.equals(txData, ops))
globalOutput.append("\n msg.data: ").append(Hex.toHexString(txData));
globalOutput.append("\n\n Spent Gas: ").append(result.getGasUsed());
if (listener != null) {
if (listener != null)
listener.output(globalOutput.toString());
}
}
}
@ -731,27 +721,25 @@ public class Program {
OpCode op = OpCode.code(code[index]);
byte[] continuedCode = null;
switch(op){
case PUSH1: case PUSH2: case PUSH3: case PUSH4: case PUSH5: case PUSH6: case PUSH7: case PUSH8:
case PUSH9: case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
case PUSH17: case PUSH18: case PUSH19: case PUSH20: case PUSH21: case PUSH22: case PUSH23: case PUSH24:
case PUSH25: case PUSH26: case PUSH27: case PUSH28: case PUSH29: case PUSH30: case PUSH31: case PUSH32:
result += ' ' + op.name() + ' ';
int nPush = op.val() - OpCode.PUSH1.val() + 1;
byte[] data = Arrays.copyOfRange(code, index+1, index + nPush + 1);
result += new BigInteger(data).toString() + ' ';
continuedCode = Arrays.copyOfRange(code, index + nPush + 1, code.length);
break;
default:
result += ' ' + op.name();
continuedCode = Arrays.copyOfRange(code, index + 1, code.length);
break;
switch(op) {
case PUSH1: case PUSH2: case PUSH3: case PUSH4: case PUSH5: case PUSH6: case PUSH7: case PUSH8:
case PUSH9: case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
case PUSH17: case PUSH18: case PUSH19: case PUSH20: case PUSH21: case PUSH22: case PUSH23: case PUSH24:
case PUSH25: case PUSH26: case PUSH27: case PUSH28: case PUSH29: case PUSH30: case PUSH31: case PUSH32:
result += ' ' + op.name() + ' ';
int nPush = op.val() - OpCode.PUSH1.val() + 1;
byte[] data = Arrays.copyOfRange(code, index+1, index + nPush + 1);
result += new BigInteger(data).toString() + ' ';
continuedCode = Arrays.copyOfRange(code, index + nPush + 1, code.length);
break;
default:
result += ' ' + op.name();
continuedCode = Arrays.copyOfRange(code, index + 1, code.length);
break;
}
return stringify(continuedCode, 0, result);
}

View File

@ -26,6 +26,7 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class MessagesTest {
@ -92,7 +93,7 @@ public class MessagesTest {
DisconnectMessage disconnectMessage = new DisconnectMessage(rlpList);
System.out.println(disconnectMessage);
assertEquals(disconnectMessage.getReason(), ReasonCode.DISCONNECT_REQUESTED);
assertEquals(disconnectMessage.getReason(), ReasonCode.REQUESTED);
}
@Test /* DisconnectMessage 2 */
@ -323,47 +324,12 @@ public class MessagesTest {
System.out.println(blocksMessage);
}
/* GET_CHAIN */
/* GET_BLOCKS */
@Test /* GET_CHAIN message parsing*/
@Test /* GET_BLOCKS message parsing*/
public void test_12() {
String getChainRaw = "F9042414A09F7A910147A6C99AB7EEEAC1ADC7B1B9B0D7D9DD83FD7FC125561016F19B624DA024B519ED5F71207330C1F450B7A4C7445C589B5F9AE73E9BBA09E0A8CB15D845A01EF7B03C7E58E65068D71F68E168C0CC0438808C32FB3E46E402D078B2020E0DA06A0F152620F606E134679D22A86156F9DDEB56FACD62C3106CC66AE030234483A001A3E918F9C2F74BD144505CF70650EF30BE806C533BA5AE3C983F55615EE98EA0E5E441F0877116011CCDECE2501A50B40C40418377037E16D0282B2B5E347138A0C55035BC5BDA25A8C6A2EFF4CCF5386CFFF3E7EE1DC9168B00AABF1074FEB1E2A060AC4D0C12C4CBF552440E659FE4E54C38EF7D05EF98965BA0E0248379CB78DFA0E1620BF60A735DDEA7CB434143E7EF663D043420F017360289FF78046948A77AA0DA8BC25F6571AB0EDE9C3DEF34FFF996575ADFCC1F8009A0388CE0A63AD9FC57A0E153CD998421E4F6BAC6A8329AE90BDA70D5D0F46A7EC02B88BDF5D3CAA8384FA0D3F00977D766A5CAF8F07EF8D15B1DADBBFD57B60BBE6CA54B2F9C2446F614EFA08687EC7AE1A541B13CDA57DCF387DEA3EA35A35AC3D5A9381376EAFBC7BA2CF5A08814AC8660E776A3FCA432D2E9A3533AFFEAEC783ECBB5DAD495B44E04EF1F98A07F2E67F508830BF18E8A432BAB9B957FA2BCDE57BE6C766C13871F833C27B9A5A021EE1E6602530EF711F6C8022799108E1AFB546E7B5FF3E3CBB4DA66C054868DA02940A17DD5C1D1911FFDA7DFAE3E5FAF8E1808CBD11F3248C18B8777B4A5D256A07E5D2F25D9750665ADBFF9C36820AD67CC55850D7D798DAB512343AB0C4E58B3A06674BD13B1051852413031BAA885448E50917D7F1D5EFB7DBA397ED4E5C36D72A06AA7A7D18B07BE0222DA47A8EE6923BAD522DBF5D0650D64CA0F2AD4C606D0CCA07F877735FCC29F804926AACEDBF7AD4896E330DCCE57F257A7D5C81F5CC03188A0FF6B7749526281A5E6A845B8ACC7DC03D2CAAEAFF0CEBF78659FDE1007AD9C05A054D6375CF54B0246FB45787206BA70115DED577354942A9219BC9E761A7E0CBCA0809384946576FB15A8F827B30045D18203A90BE662863D5F205B868DF882472AA05A6EA58D02A2823BA693AC11C21D3C4D66AFFFDB9F1475EA86A461912C2F3187A003AF21F3939C29C231200B1F790F16421A8923254CBF2A90455B9B8F28BE4562A036FDFB3E05936CAA1ABBDCFBBACD9000F86FBCAE2228E77346533CEF17073767A0D3CE8B71E129020F3356A09946F9BC4C18E61D9516E74F6F912461A438F1E006A01D8203A8E23F50D70188ED3099E50645B92959C2216EA7A74719C159B4978BDBA03812A8FC4A5BB6EF0832EBF5058FAF65A46187CEE568C94915AE1850069775F3A0687DDEF8750F606637B3F5F23D286053671081A1AD224B35A624DE7392193951A0E6A8E1D4417292E20F9698A3464CEEABC6476A57521FF79D994DE22C55DADEAD820100";
byte[] payload = Hex.decode(getChainRaw);
RLPList rlpList = RLP.decode2(payload);
GetChainMessage getChainMessage = new GetChainMessage(rlpList);
getChainMessage.parseRLP();
System.out.println(getChainMessage);
assertEquals(32, getChainMessage.getBlockHashList().size());
assertEquals("E5E441F0877116011CCDECE2501A50B40C40418377037E16D0282B2B5E347138",
Hex.toHexString( getChainMessage.getBlockHashList().get(5) ).toUpperCase());
assertEquals("6AA7A7D18B07BE0222DA47A8EE6923BAD522DBF5D0650D64CA0F2AD4C606D0CC",
Hex.toHexString( getChainMessage.getBlockHashList().get(19) ).toUpperCase());
assertEquals("03AF21F3939C29C231200B1F790F16421A8923254CBF2A90455B9B8F28BE4562",
Hex.toHexString( getChainMessage.getBlockHashList().get(25) ).toUpperCase());
}
/* NOT_IN_CHAIN */
@Test /* NotInChainMessage parsing 1 */
public void test_13() {
String getChainRaw = "E015A0E5E441F0877116011CCDECE2501A50B40C40418377037E16D0282B2B5E347138";
byte[] payload = Hex.decode(getChainRaw);
RLPList rlpList = RLP.decode2(payload);
NotInChainMessage notInChainMessage = new NotInChainMessage(rlpList);
System.out.println(notInChainMessage);
assertEquals("E5E441F0877116011CCDECE2501A50B40C40418377037E16D0282B2B5E347138",
Hex.toHexString(notInChainMessage.getHash()).toUpperCase());
fail("Not yet implemented");
}
@Test /* BlocksMessage POC-5 real msg*/
@ -396,23 +362,10 @@ public class MessagesTest {
Block block = list.get(0);
}
@Test /* GetChain encode */
@Test /* GetBlocks encode */
public void test14() {
String expected = "F86514A0B4986B41E57B351FD037775A4D439443B4BDE859D061D772117AFA855BFDE18EA0241A2A72372AD63C40704C93CD380A51FEE174EB59501D731A009F51372E5205A0E963653D860F35E27A5572CFEECE1BB518C42BB97DEAC0978FA788F6DCB45CF764";
String hash1 = "B4986B41E57B351FD037775A4D439443B4BDE859D061D772117AFA855BFDE18E";
String hash2 = "241A2A72372AD63C40704C93CD380A51FEE174EB59501D731A009F51372E5205";
String hash3 = "E963653D860F35E27A5572CFEECE1BB518C42BB97DEAC0978FA788F6DCB45CF7";
GetChainMessage getChainMessage =
new GetChainMessage((byte)100, Hex.decode(hash1), Hex.decode(hash2), Hex.decode(hash3));
assertEquals(expected, Hex.toHexString(getChainMessage.getPayload()).toUpperCase());
byte[] size = ByteUtil.calcPacketLength( getChainMessage.getPayload());
assertEquals("00000067", Hex.toHexString(size));
fail("Not yet implemented");
}
@Test /* Transactions msg encode */
@ -441,9 +394,6 @@ public class MessagesTest {
assertEquals(expected, Hex.toHexString( transactionsMessage.getPayload()) );
}
@Ignore
@Test /* Block msg decode - found bug tool */
public void test17() throws URISyntaxException, IOException {

View File

@ -6,25 +6,26 @@ server.acceptConnections = false
# the search of the online peers
# values: [ip:port, ip:port, ip:port ...]
peer.discovery.ip.list = 185.43.109.23:30303, \
54.211.14.10:30303, \
213.100.248.181:30303, \
54.72.69.180:30303, \
54.201.28.117:30303, \
70.29.74.8:7
207.12.89.101:30303
#peer.discovery.ip.list = 127.0.0.1:30303
# active peer ip and port
# that is the peer through
# we get the chain: [54.201.28.117] port: [30303]
# ZeroGox
peer.active.ip = 54.204.10.41
#peer.active.ip = 54.204.10.41
#peer.active.port = 30303
#peer.active.ip = 185.43.109.23
#peer.active.port = 30303
# PoC-6 testnet
peer.active.ip = 207.12.89.101
peer.active.port = 30303
# RomanJ
#peer.active.ip = 54.211.14.10
#peer.active.port = 50505
# PoC-5 testnet
#peer.active.ip = 54.72.69.180
# Localhost
#peer.active.ip = 127.0.0.1
#peer.active.port = 30303
# specify if the mechanism
@ -119,4 +120,4 @@ project.version = PROJECT.VERSION
# hello phrase will be included in
# the hello message of the peer
hello.phrase = RJ
hello.phrase =

View File

@ -42,7 +42,7 @@ public class ToolBar extends JFrame {
String version = SystemProperties.CONFIG.projectVersion();
introLogger.info("");
introLogger.info("|Ξ| EthereumJ [v" + version + "] by RomanJ");
introLogger.info("|Ξ| EthereumJ [v" + version + "]");
introLogger.info("|Ξ| Code by Roman Mandeleil, (c) 2014.");
introLogger.info("|Ξ| Contribution: Nick Savers ");
introLogger.info("|Ξ| Based on a design by Vitalik Buterin.");

View File

@ -6,11 +6,8 @@ server.acceptConnections = false
# the search of the online peers
# values: [ip:port, ip:port, ip:port ...]
peer.discovery.ip.list = 185.43.109.23:30303, \
54.211.14.10:30303, \
213.100.248.181:30303, \
54.72.69.180:30303, \
54.201.28.117:30303, \
70.29.74.8:7
207.12.89.101:30303
#peer.discovery.ip.list = 127.0.0.1:30303
# active peer ip and port
@ -20,31 +17,17 @@ peer.discovery.ip.list = 185.43.109.23:30303, \
#peer.active.ip = 54.204.10.41
#peer.active.port = 30303
# Some dude in Canada
#peer.active.ip = 131.104.247.135
#peer.active.port = 30303
# RomanJ
peer.active.ip = 54.211.14.10
peer.active.port = 30303
# PoC-5 testnet
#peer.active.ip = 185.43.109.23
#peer.active.port = 30303
# PoC-6 testnet
peer.active.ip = 207.12.89.101
peer.active.port = 30303
# Localhost
#peer.active.ip = 127.0.0.1
#peer.active.port = 30303
#peer.active.ip = 54.72.69.180
#peer.active.port = 30303
#peer.active.ip = 185.43.109.23
#peer.active.port = 30303
#peer.active.ip = 151.64.223.120
#peer.active.port = 30304
# specify if the mechanism
# to discover more and more
# peers and check the already
@ -146,4 +129,4 @@ project.version = PROJECT.VERSION
# hello phrase will be included in
# the hello message of the peer
hello.phrase = RJ
hello.phrase =