Merge pull request #2 from nicksavers/master

Merge RLP classes and tests
This commit is contained in:
romanman 2014-05-14 17:14:17 +03:00
commit b1a965c549
27 changed files with 874 additions and 1220 deletions

View File

@ -1,9 +1,9 @@
package org.ethereum.net.vo; package org.ethereum.core;
import org.ethereum.crypto.HashUtil; import org.ethereum.crypto.HashUtil;
import org.ethereum.net.rlp.RLPElement; import org.ethereum.util.RLPElement;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
import java.math.BigInteger; import java.math.BigInteger;
@ -111,7 +111,7 @@ public class Block {
this.hash = HashUtil.sha3(rawData.getRLPData()); this.hash = HashUtil.sha3(rawData.getRLPData());
List params = ((RLPList) rawData.getElement(0)).getList(); RLPList params = (RLPList) rawData.get(0);
this.parentHash = ((RLPItem) params.get(0)).getData(); this.parentHash = ((RLPItem) params.get(0)).getData();
this.unclesHash = ((RLPItem) params.get(1)).getData(); this.unclesHash = ((RLPItem) params.get(1)).getData();
@ -136,13 +136,13 @@ public class Block {
this.nonce = ((RLPItem) params.get(12)).getData(); this.nonce = ((RLPItem) params.get(12)).getData();
// parse transactions // parse transactions
List<RLPElement> transactions = ((RLPList) rawData.getElement(1)).getList(); RLPList transactions = (RLPList) rawData.get(1);
for (RLPElement rlpTx : transactions){ for (RLPElement rlpTx : transactions){
Transaction tx = new Transaction((RLPList)rlpTx); Transaction tx = new Transaction((RLPList)rlpTx);
this.transactionsList.add(tx); this.transactionsList.add(tx);
} }
// parse uncles // parse uncles
List<RLPElement> uncleBlocks = ((RLPList) rawData.getElement(2)).getList(); RLPList uncleBlocks = (RLPList) rawData.get(2);
for (RLPElement rawUncle : uncleBlocks){ for (RLPElement rawUncle : uncleBlocks){
Block blockData = new Block((RLPList)rawUncle); Block blockData = new Block((RLPList)rawUncle);
this.uncleList.add(blockData); this.uncleList.add(blockData);

View File

@ -1,10 +1,10 @@
package org.ethereum.net.vo; package org.ethereum.core;
import org.ethereum.crypto.ECKey.ECDSASignature; import org.ethereum.crypto.ECKey.ECDSASignature;
import org.ethereum.crypto.ECKey; import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil; import org.ethereum.crypto.HashUtil;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
/** /**
@ -71,25 +71,25 @@ public class Transaction {
public void rlpParse(){ public void rlpParse(){
this.hash = HashUtil.sha3(rawData.getRLPData()); this.hash = HashUtil.sha3(rawData.getRLPData());
this.nonce = ((RLPItem) rawData.getElement(0)).getData(); this.nonce = ((RLPItem) rawData.get(0)).getData();
this.value = ((RLPItem) rawData.getElement(1)).getData(); this.value = ((RLPItem) rawData.get(1)).getData();
this.receiveAddress = ((RLPItem) rawData.getElement(2)).getData(); this.receiveAddress = ((RLPItem) rawData.get(2)).getData();
this.gasPrice = ((RLPItem) rawData.getElement(3)).getData(); this.gasPrice = ((RLPItem) rawData.get(3)).getData();
this.gasLimit = ((RLPItem) rawData.getElement(4)).getData(); this.gasLimit = ((RLPItem) rawData.get(4)).getData();
this.data = ((RLPItem) rawData.getElement(5)).getData(); this.data = ((RLPItem) rawData.get(5)).getData();
if (rawData.size() == 9){ // Simple transaction if (rawData.size() == 9){ // Simple transaction
byte v = ((RLPItem) rawData.getElement(6)).getData()[0]; byte v = ((RLPItem) rawData.get(6)).getData()[0];
byte[] r = ((RLPItem) rawData.getElement(7)).getData(); byte[] r = ((RLPItem) rawData.get(7)).getData();
byte[] s = ((RLPItem) rawData.getElement(8)).getData(); byte[] s = ((RLPItem) rawData.get(8)).getData();
this.signature = ECDSASignature.fromComponents(r, s, v); this.signature = ECDSASignature.fromComponents(r, s, v);
} else if (rawData.size() == 10){ // Contract creation transaction } else if (rawData.size() == 10){ // Contract creation transaction
this.init = ((RLPItem) rawData.getElement(6)).getData(); this.init = ((RLPItem) rawData.get(6)).getData();
byte v = ((RLPItem) rawData.getElement(7)).getData()[0]; byte v = ((RLPItem) rawData.get(7)).getData()[0];
byte[] r = ((RLPItem) rawData.getElement(8)).getData(); byte[] r = ((RLPItem) rawData.get(8)).getData();
byte[] s = ((RLPItem) rawData.getElement(9)).getData(); byte[] s = ((RLPItem) rawData.get(9)).getData();
this.signature = ECDSASignature.fromComponents(r, s, v); this.signature = ECDSASignature.fromComponents(r, s, v);
} else throw new Error("Wrong tx data element list size"); } else throw new RuntimeException("Wrong tx data element list size");
this.parsed = true; this.parsed = true;
} }

View File

@ -1,144 +0,0 @@
package org.ethereum.crypto.vm;
/**
* Instruction set for the Ethereum Virtual Machine
*/
public enum OpCode {
/**
* Stop and Arithmetic Operations
*/
STOP(0x00),
ADD(0x01),
MUL(0x02),
SUB(0x03),
DIV(0x04),
SDIV(0x05),
MOD(0x06),
SMOD(0x07),
EXP(0x08),
NEG(0x09),
LT(0x0a),
GT(0x0b),
EQ(0x0c),
NOT(0x0d),
/**
* Bitwise Logic Operations
*/
AND(0x10),
OR(0x11),
XOR(0x12),
BYTE(0x13),
/**
* SHA3
*/
SHA3(0x20),
/**
* Environmental Information
*/
ADDRESS(0x30),
BALANCE(0x31),
ORIGIN(0x32),
CALLER(0x33),
CALLVALUE(0x34),
CALLDATALOAD(0x35),
CALLDATASIZE(0x36),
CALLDATACOPY(0x37),
CODESIZE(0x38),
CODECOPY(0x39),
GASPRICE(0x3a),
/**
* Block Information
*/
PREVHASH(0x40),
COINBASE(0x41),
TIMESTAMP(0x42),
NUMBER(0x43),
DIFFICULTY(0x44),
GASLIMIT(0x45),
/**
* Memory, Storage and Flow Operations
*/
POP(0x50),
DUP(0x51),
SWAP(0x52),
MLOAD(0x53),
MSTORE(0x54),
MSTORE8(0x55),
SLOAD(0x56),
SSTORE(0x57),
JUMP(0x58),
JUMPI(0x59),
PC(0x5a),
MSIZE(0x5b),
GAS(0x5c),
/**
* Push Operations
*/
PUSH1(0x60),
PUSH2(0x61),
PUSH3(0x62),
PUSH4(0x63),
PUSH5(0x64),
PUSH6(0x65),
PUSH7(0x66),
PUSH8(0x67),
PUSH9(0x68),
PUSH10(0x69),
PUSH11(0x6a),
PUSH12(0x6b),
PUSH13(0x6c),
PUSH14(0x6d),
PUSH15(0x6e),
PUSH16(0x6f),
PUSH17(0x70),
PUSH18(0x71),
PUSH19(0x72),
PUSH20(0x73),
PUSH21(0x74),
PUSH22(0x75),
PUSH23(0x76),
PUSH24(0x77),
PUSH25(0x78),
PUSH26(0x79),
PUSH27(0x7a),
PUSH28(0x7b),
PUSH29(0x7c),
PUSH30(0x7d),
PUSH31(0x7e),
PUSH32(0x7f),
/**
* System operations
*/
CREATE(0xf0),
CALL(0xf1),
RETURN(0xf2),
SUICIDE(0xff);
private int opcode;
private OpCode(int opcode) {
this.opcode = opcode;
}
public int getOpCode() {
return this.opcode;
}
}

View File

@ -2,10 +2,10 @@ package org.ethereum.manager;
import com.maxmind.geoip.Location; import com.maxmind.geoip.Location;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.geodb.IpGeoDB; import org.ethereum.geodb.IpGeoDB;
import org.ethereum.net.vo.Block; import org.ethereum.net.client.PeerData;
import org.ethereum.net.vo.PeerData;
import org.ethereum.net.vo.Transaction;
import java.util.*; import java.util.*;

View File

@ -1,16 +1,6 @@
package org.ethereum.net.client; package org.ethereum.net.client;
import static org.ethereum.net.Command.*; import static org.ethereum.net.Command.*;
import static org.ethereum.net.Command.DISCONNECT;
import static org.ethereum.net.Command.GET_CHAIN;
import static org.ethereum.net.Command.GET_PEERS;
import static org.ethereum.net.Command.GET_TRANSACTIONS;
import static org.ethereum.net.Command.HELLO;
import static org.ethereum.net.Command.NOT_IN_CHAIN;
import static org.ethereum.net.Command.PEERS;
import static org.ethereum.net.Command.PING;
import static org.ethereum.net.Command.PONG;
import static org.ethereum.net.Command.TRANSACTIONS;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
@ -21,6 +11,7 @@ import java.util.List;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import org.ethereum.core.Block;
import org.ethereum.gui.PeerListener; import org.ethereum.gui.PeerListener;
import org.ethereum.manager.MainData; import org.ethereum.manager.MainData;
import org.ethereum.net.Command; import org.ethereum.net.Command;
@ -33,9 +24,9 @@ import org.ethereum.net.message.NotInChainMessage;
import org.ethereum.net.message.PeersMessage; import org.ethereum.net.message.PeersMessage;
import org.ethereum.net.message.StaticMessages; import org.ethereum.net.message.StaticMessages;
import org.ethereum.net.message.TransactionsMessage; import org.ethereum.net.message.TransactionsMessage;
import org.ethereum.net.rlp.RLP; import org.ethereum.util.ByteUtil;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLP;
import org.ethereum.net.vo.Block; import org.ethereum.util.RLPList;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
@ -50,7 +41,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
private final static byte[] MAGIC_PREFIX = {(byte)0x22, (byte)0x40, (byte)0x08, (byte)0x91}; private final static byte[] MAGIC_PREFIX = {(byte)0x22, (byte)0x40, (byte)0x08, (byte)0x91};
private final static byte[] HELLO_MESSAGE = StaticMessages.HELLO_MESSAGE.getPayload(); private final static byte[] HELLO_MESSAGE = StaticMessages.HELLO_MESSAGE.getPayload();
private final static byte[] HELLO_MESSAGE_LEN = calcPacketLength(HELLO_MESSAGE); private final static byte[] HELLO_MESSAGE_LEN = ByteUtil.calcPacketLength(HELLO_MESSAGE);
private long lastPongTime = 0; private long lastPongTime = 0;
private boolean tearDown = false; private boolean tearDown = false;
@ -140,8 +131,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
// got HELLO // got HELLO
if (Command.fromInt(command) == HELLO) { if (Command.fromInt(command) == HELLO) {
System.out.println("[Recv: HELLO]" ); System.out.println("[Recv: HELLO]" );
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
HelloMessage helloMessage = new HelloMessage(rlpList); HelloMessage helloMessage = new HelloMessage(rlpList);
System.out.println(helloMessage.toString()); System.out.println(helloMessage.toString());
@ -152,8 +142,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
System.out.println("[Recv: DISCONNECT]"); System.out.println("[Recv: DISCONNECT]");
if (peerListener != null) peerListener.console("[Recv: DISCONNECT]"); if (peerListener != null) peerListener.console("[Recv: DISCONNECT]");
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
DisconnectMessage disconnectMessage = new DisconnectMessage(rlpList); DisconnectMessage disconnectMessage = new DisconnectMessage(rlpList);
System.out.println(disconnectMessage); System.out.println(disconnectMessage);
@ -198,8 +187,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
System.out.println("[Recv: PEERS]"); System.out.println("[Recv: PEERS]");
if (peerListener != null) peerListener.console("[Recv: PEERS]"); if (peerListener != null) peerListener.console("[Recv: PEERS]");
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
PeersMessage peersMessage = new PeersMessage(rlpList); PeersMessage peersMessage = new PeersMessage(rlpList);
MainData.instance.addPeers(peersMessage.getPeers()); MainData.instance.addPeers(peersMessage.getPeers());
@ -212,8 +200,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
System.out.println("Recv: TRANSACTIONS]"); System.out.println("Recv: TRANSACTIONS]");
if (peerListener != null) peerListener.console("Recv: TRANSACTIONS]"); if (peerListener != null) peerListener.console("Recv: TRANSACTIONS]");
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
TransactionsMessage transactionsMessage = new TransactionsMessage(rlpList); TransactionsMessage transactionsMessage = new TransactionsMessage(rlpList);
MainData.instance.addTransactions(transactionsMessage.getTransactions()); MainData.instance.addTransactions(transactionsMessage.getTransactions());
@ -226,8 +213,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
System.out.println("[Recv: BLOCKS]"); System.out.println("[Recv: BLOCKS]");
if (peerListener != null) peerListener.console("[Recv: BLOCKS]"); if (peerListener != null) peerListener.console("[Recv: BLOCKS]");
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
BlocksMessage blocksMessage = new BlocksMessage(rlpList); BlocksMessage blocksMessage = new BlocksMessage(rlpList);
List<Block> blockList = blocksMessage.getBlockDataList(); List<Block> blockList = blocksMessage.getBlockDataList();
@ -241,8 +227,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
System.out.println("[Recv: GET_CHAIN]"); System.out.println("[Recv: GET_CHAIN]");
if (peerListener != null) peerListener.console("[Recv: GET_CHAIN]"); if (peerListener != null) peerListener.console("[Recv: GET_CHAIN]");
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
GetChainMessage getChainMessage = new GetChainMessage(rlpList); GetChainMessage getChainMessage = new GetChainMessage(rlpList);
System.out.println(getChainMessage); System.out.println(getChainMessage);
@ -253,8 +238,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
System.out.println("[Recv: NOT_IN_CHAIN]"); System.out.println("[Recv: NOT_IN_CHAIN]");
if (peerListener != null) peerListener.console("[Recv: NOT_IN_CHAIN]"); if (peerListener != null) peerListener.console("[Recv: NOT_IN_CHAIN]");
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
NotInChainMessage notInChainMessage = new NotInChainMessage(rlpList); NotInChainMessage notInChainMessage = new NotInChainMessage(rlpList);
System.out.println(notInChainMessage); System.out.println(notInChainMessage);
@ -285,11 +269,9 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
} }
private void sendMsg(Message msg, ChannelHandlerContext ctx){ private void sendMsg(Message msg, ChannelHandlerContext ctx){
byte[] data = msg.getPayload(); byte[] data = msg.getPayload();
final ByteBuf buffer = ctx.alloc().buffer(data.length + 8); final ByteBuf buffer = ctx.alloc().buffer(data.length + 8);
byte[] packetLen = calcPacketLength(data); byte[] packetLen = ByteUtil.calcPacketLength(data);
buffer.writeBytes(MAGIC_PREFIX); buffer.writeBytes(MAGIC_PREFIX);
buffer.writeBytes(packetLen); buffer.writeBytes(packetLen);
@ -330,22 +312,8 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
private void sendTx(ChannelHandlerContext ctx){ private void sendTx(ChannelHandlerContext ctx){
byte[] TX_MSG = byte[] TX_MSG =
Hex.decode("2240089100000070F86E12F86B80881BC16D674EC8000094CD2A3D9F938E13CD947EC05ABC7FE734DF8DD8268609184E72A00064801BA0C52C114D4F5A3BA904A9B3036E5E118FE0DBB987FE3955DA20F2CD8F6C21AB9CA06BA4C2874299A55AD947DBC98A25EE895AABF6B625C26C435E84BFD70EDF2F69"); Hex.decode("2240089100000070F86E12F86B80881BC16D674EC8000094CD2A3D9F938E13CD947EC05ABC7FE734DF8DD8268609184E72A00064801BA0C52C114D4F5A3BA904A9B3036E5E118FE0DBB987FE3955DA20F2CD8F6C21AB9CA06BA4C2874299A55AD947DBC98A25EE895AABF6B625C26C435E84BFD70EDF2F69");
ByteBuf buffer = ctx.alloc().buffer(TX_MSG.length); ByteBuf buffer = ctx.alloc().buffer(TX_MSG.length);
buffer.writeBytes(TX_MSG); buffer.writeBytes(TX_MSG);
ctx.writeAndFlush(buffer); ctx.writeAndFlush(buffer);
} }
private static byte[] calcPacketLength(byte[] msg){
int msgLen = msg.length;
byte[] len = {
(byte)((msgLen >> 24) & 0xFF),
(byte)((msgLen >> 16) & 0xFF),
(byte)((msgLen >> 8) & 0xFF),
(byte)((msgLen ) & 0xFF)};
return len;
}
} }

View File

@ -1,4 +1,4 @@
package org.ethereum.net.vo; package org.ethereum.net.client;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;

View File

@ -5,11 +5,11 @@ import java.util.List;
import static org.ethereum.net.Command.BLOCKS; import static org.ethereum.net.Command.BLOCKS;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.net.Command; import org.ethereum.net.Command;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.net.vo.Block;
import org.ethereum.net.vo.Transaction;
/** /**
* www.ethereumJ.com * www.ethereumJ.com
@ -26,14 +26,14 @@ public class BlocksMessage extends Message {
public void parseRLP() { public void parseRLP() {
RLPList paramsList = (RLPList) rawData.getElement(0); RLPList paramsList = (RLPList) rawData.get(0);
if ( Command.fromInt(((RLPItem)(paramsList).getElement(0)).getData()[0]) != BLOCKS){ if (Command.fromInt(((RLPItem) (paramsList).get(0)).getData()[0]) != BLOCKS) {
throw new Error("BlocksMessage: parsing for mal data"); throw new Error("BlocksMessage: parsing for mal data");
} }
for (int i = 1; i < paramsList.size(); ++i) { for (int i = 1; i < paramsList.size(); ++i) {
RLPList rlpData = ((RLPList)paramsList.getElement(i)); RLPList rlpData = ((RLPList) paramsList.get(i));
Block blockData = new Block(rlpData); Block blockData = new Block(rlpData);
this.blockDataList.add(blockData); this.blockDataList.add(blockData);
} }
@ -61,8 +61,6 @@ public class BlocksMessage extends Message {
sb.append("[").append(transactionData).append("]\n"); sb.append("[").append(transactionData).append("]\n");
} }
} }
return "Blocks Message [\n" + return "Blocks Message [\n" + sb.toString() + " ]";
sb.toString()
+ " ]";
} }
} }

View File

@ -1,8 +1,9 @@
package org.ethereum.net.message; package org.ethereum.net.message;
import org.ethereum.net.rlp.RLPItem;
import org.ethereum.net.rlp.RLPList;
import org.ethereum.net.Command; import org.ethereum.net.Command;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import static org.ethereum.net.Command.DISCONNECT; import static org.ethereum.net.Command.DISCONNECT;
import static org.ethereum.net.message.ReasonCode.DISCONNECT_REQUESTED; import static org.ethereum.net.message.ReasonCode.DISCONNECT_REQUESTED;
@ -22,13 +23,13 @@ public class DisconnectMessage extends Message {
@Override @Override
public void parseRLP() { public void parseRLP() {
RLPList paramsList = (RLPList) rawData.getElement(0); RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).getElement(0)).getData()[0]) != DISCONNECT){ if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0]) != DISCONNECT){
throw new Error("Disconnect: parsing for mal data"); throw new Error("Disconnect: parsing for mal data");
} }
byte[] reasonB = ((RLPItem)paramsList.getElement(1)).getData(); byte[] reasonB = ((RLPItem)paramsList.get(1)).getData();
if (reasonB == null){ if (reasonB == null){
this.reason = DISCONNECT_REQUESTED; this.reason = DISCONNECT_REQUESTED;
} else { } else {

View File

@ -5,9 +5,10 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.ethereum.net.Command.GET_CHAIN; import static org.ethereum.net.Command.GET_CHAIN;
import org.ethereum.net.Command; import org.ethereum.net.Command;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
/** /**
@ -27,19 +28,19 @@ public class GetChainMessage extends Message {
@Override @Override
public void parseRLP() { public void parseRLP() {
RLPList paramsList = (RLPList) rawData.getElement(0); RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).getElement(0)).getData()[0]) != GET_CHAIN){ if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0]) != GET_CHAIN){
throw new Error("GetChain: parsing for mal data"); throw new Error("GetChain: parsing for mal data");
} }
int size = paramsList.size(); int size = paramsList.size();
for (int i = 1; i < size - 1; ++i){ for (int i = 1; i < size - 1; ++i){
blockHashList.add(((RLPItem) paramsList.getElement(i)).getData()); blockHashList.add(((RLPItem) paramsList.get(i)).getData());
} }
// the last element is the num of requested blocks // the last element is the num of requested blocks
byte[] blockNumB = ((RLPItem)paramsList.getElement(size - 1)).getData(); byte[] blockNumB = ((RLPItem)paramsList.get(size - 1)).getData();
this.blockNum = new BigInteger(blockNumB); this.blockNum = new BigInteger(blockNumB);
this.parsed = true; this.parsed = true;

View File

@ -4,9 +4,9 @@ import org.spongycastle.util.encoders.Hex;
import static org.ethereum.net.Command.HELLO; import static org.ethereum.net.Command.HELLO;
import org.ethereum.net.rlp.RLP; import org.ethereum.util.RLP;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -40,25 +40,25 @@ public class HelloMessage extends Message {
@Override @Override
public void parseRLP() { public void parseRLP() {
RLPList paramsList = (RLPList) rawData.getElement(0); RLPList paramsList = (RLPList) rawData.get(0);
// the message does no distinguish between the 0 and null so here I check command code for null // the message does no distinguish between the 0 and null so here I check command code for null
// todo: find out if it can be 00 // todo: find out if it can be 00
if (((RLPItem)(paramsList).getElement(0)).getData() != null){ if (((RLPItem)(paramsList).get(0)).getData() != null){
throw new Error("HelloMessage: parsing for mal data"); throw new Error("HelloMessage: parsing for mal data");
} }
this.protocolVersion = ((RLPItem) paramsList.getElement(1)).getData()[0]; this.protocolVersion = ((RLPItem) paramsList.get(1)).getData()[0];
byte[] networkIdBytes = ((RLPItem) paramsList.getElement(2)).getData(); byte[] networkIdBytes = ((RLPItem) paramsList.get(2)).getData();
this.networkId = networkIdBytes == null ? 0 : networkIdBytes[0] ; this.networkId = networkIdBytes == null ? 0 : networkIdBytes[0] ;
this.clientId = new String(((RLPItem) paramsList.getElement(3)).getData()); this.clientId = new String(((RLPItem) paramsList.get(3)).getData());
this.capabilities = ((RLPItem) paramsList.getElement(4)).getData()[0]; this.capabilities = ((RLPItem) paramsList.get(4)).getData()[0];
ByteBuffer bb = ByteBuffer.wrap(((RLPItem) paramsList.getElement(5)).getData()); ByteBuffer bb = ByteBuffer.wrap(((RLPItem) paramsList.get(5)).getData());
this.peerPort = bb.getShort(); this.peerPort = bb.getShort();
this.peerId = ((RLPItem) paramsList.getElement(6)).getData(); this.peerId = ((RLPItem) paramsList.get(6)).getData();
this.parsed = true; this.parsed = true;
// todo: what to do when mal data ? // todo: what to do when mal data ?
} }

View File

@ -1,6 +1,6 @@
package org.ethereum.net.message; package org.ethereum.net.message;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
/** /**
* www.ethereumJ.com * www.ethereumJ.com

View File

@ -4,8 +4,8 @@ import static org.ethereum.net.Command.NOT_IN_CHAIN;
import org.ethereum.net.Command; import org.ethereum.net.Command;
import org.ethereum.net.message.Message; import org.ethereum.net.message.Message;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
/** /**
@ -23,12 +23,12 @@ public class NotInChainMessage extends Message {
@Override @Override
public void parseRLP() { public void parseRLP() {
RLPList paramsList = (RLPList) rawData.getElement(0); RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).getElement(0)).getData()[0] & 0xFF) != NOT_IN_CHAIN){ if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0] & 0xFF) != NOT_IN_CHAIN){
throw new Error("NotInChain Message: parsing for mal data"); throw new Error("NotInChain Message: parsing for mal data");
} }
hash = ((RLPItem)paramsList.getElement(1)).getData(); hash = ((RLPItem)paramsList.get(1)).getData();
} }
@Override @Override

View File

@ -5,10 +5,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.ethereum.net.Command.PEERS; import static org.ethereum.net.Command.PEERS;
import org.ethereum.net.Command; import org.ethereum.net.Command;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.net.client.PeerData;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPItem;
import org.ethereum.net.vo.PeerData; import org.ethereum.util.RLPList;
/** /**
* www.ethereumJ.com * www.ethereumJ.com
@ -30,17 +31,17 @@ public class PeersMessage extends Message {
@Override @Override
public void parseRLP() { public void parseRLP() {
RLPList paramsList = (RLPList) rawData.getElement(0); RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).getElement(0)).getData()[0] & 0xFF) != PEERS){ if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0] & 0xFF) != PEERS){
throw new Error("PeersMessage: parsing for mal data"); throw new Error("PeersMessage: parsing for mal data");
} }
for (int i = 1; i < paramsList.size(); ++i){ for (int i = 1; i < paramsList.size(); ++i){
RLPList peerParams = (RLPList)paramsList.getElement(i); RLPList peerParams = (RLPList)paramsList.get(i);
byte[] ip = ((RLPItem) peerParams.getElement(0)).getData(); byte[] ip = ((RLPItem) peerParams.get(0)).getData();
byte[] shortData = ((RLPItem) peerParams.getElement(1)).getData(); byte[] shortData = ((RLPItem) peerParams.get(1)).getData();
short peerPort = 0; short peerPort = 0;
if (shortData.length == 1) if (shortData.length == 1)
peerPort = shortData[0]; peerPort = shortData[0];
@ -48,7 +49,7 @@ public class PeersMessage extends Message {
ByteBuffer bb = ByteBuffer.wrap(shortData, 0, shortData.length); ByteBuffer bb = ByteBuffer.wrap(shortData, 0, shortData.length);
peerPort = bb.getShort(); peerPort = bb.getShort();
} }
byte[] peerId = ((RLPItem) peerParams.getElement(2)).getData(); byte[] peerId = ((RLPItem) peerParams.get(2)).getData();
PeerData peer = new PeerData(ip, peerPort, peerId); PeerData peer = new PeerData(ip, peerPort, peerId);
peers.add(peer); peers.add(peer);
} }

View File

@ -1,10 +1,11 @@
package org.ethereum.net.message; package org.ethereum.net.message;
import static org.ethereum.net.Command.TRANSACTIONS; import static org.ethereum.net.Command.TRANSACTIONS;
import org.ethereum.core.Transaction;
import org.ethereum.net.Command; import org.ethereum.net.Command;
import org.ethereum.net.rlp.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.net.vo.Transaction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -27,16 +28,16 @@ public class TransactionsMessage extends Message {
@Override @Override
public void parseRLP() { public void parseRLP() {
RLPList paramsList = (RLPList) rawData.getElement(0); RLPList paramsList = (RLPList) rawData.get(0);
if (Command.fromInt(((RLPItem)(paramsList).getElement(0)).getData()[0] & 0xFF) != TRANSACTIONS) { if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0] & 0xFF) != TRANSACTIONS) {
throw new Error("TransactionMessage: parsing for mal data"); throw new Error("TransactionMessage: parsing for mal data");
} }
transactions = new ArrayList<Transaction>(); transactions = new ArrayList<Transaction>();
int size = paramsList.getList().size(); int size = paramsList.size();
for (int i = 1; i < size; ++i){ for (int i = 1; i < size; ++i){
RLPList rlpTxData = (RLPList) paramsList.getElement(i); RLPList rlpTxData = (RLPList) paramsList.get(i);
Transaction tx = new Transaction(rlpTxData); Transaction tx = new Transaction(rlpTxData);
transactions.add(tx); transactions.add(tx);
} }

View File

@ -1,10 +0,0 @@
package org.ethereum.net.rlp;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 21/04/14 16:28
*/
public interface RLPElement {
}

View File

@ -1,7 +1,5 @@
package org.ethereum.util; package org.ethereum.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
@ -40,17 +38,32 @@ public class ByteUtil {
} }
/** /**
* <p>Given a textual message, returns a byte buffer formatted as follows:</p> * Calculate packet length
* @param msg
* @return byte-array with 4 elements
*/ */
public static byte[] formatForSigning(byte[] message) { public static byte[] calcPacketLength(byte[] msg){
try { int msgLen = msg.length;
ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] len = {
bos.write(encodeInt(message.length)); (byte)((msgLen >> 24) & 0xFF),
bos.write(message); (byte)((msgLen >> 16) & 0xFF),
return bos.toByteArray(); (byte)((msgLen >> 8) & 0xFF),
} catch (IOException e) { (byte)((msgLen ) & 0xFF)};
throw new RuntimeException(e); // Cannot happen. return len;
} }
/**
* Cast hex encoded value from byte[] to int
*
* Limited to Integer.MAX_VALUE: 2^32-1 (4 bytes)
*
* @param b array contains the values
* @return unsigned positive int value.
*/
public static int byteArrayToInt(byte[] b) {
if (b == null || b.length == 0)
return 0;
return new BigInteger(1, b).intValue();
} }
public static byte[] encodeInt(int value) { public static byte[] encodeInt(int value) {

View File

@ -1,6 +1,8 @@
package org.ethereum.util; package org.ethereum.util;
public class DecodeResult { import java.io.Serializable;
public class DecodeResult implements Serializable {
private int pos; private int pos;
private Object decoded; private Object decoded;

View File

@ -1,11 +1,21 @@
package org.ethereum.net.rlp; package org.ethereum.util;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Queue; import java.util.Queue;
import static java.util.Arrays.copyOfRange;
import static org.ethereum.util.ByteUtil.byteArrayToInt;
import static org.spongycastle.util.Arrays.concatenate;
import java.util.List;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
/** /**
* Recursive Length Prefix (RLP) encoding. * Recursive Length Prefix (RLP) encoding.
* *
@ -34,6 +44,9 @@ import java.util.Queue;
*/ */
public class RLP { public class RLP {
/** Allow for content up to size of 2^64 bytes **/
private static double MAX_ITEM_LENGTH = Math.pow(256, 8);
/** /**
* Reason for threshold according to Vitalik Buterin: * Reason for threshold according to Vitalik Buterin:
* - 56 bytes maximizes the benefit of both options * - 56 bytes maximizes the benefit of both options
@ -45,6 +58,13 @@ public class RLP {
**/ **/
private static int SIZE_THRESHOLD = 56; private static int SIZE_THRESHOLD = 56;
/** RLP encoding rules are defined as follows: */
/*
* For a single byte whose value is in the [0x00, 0x7f] range, that byte is
* its own RLP encoding.
*/
/* /*
* If a string is 0-55 bytes long, the RLP encoding consists of a single * If a string is 0-55 bytes long, the RLP encoding consists of a single
* byte with value 0x80 plus the length of the string followed by the * byte with value 0x80 plus the length of the string followed by the
@ -52,6 +72,16 @@ public class RLP {
*/ */
private static int OFFSET_SHORT_ITEM = 0x80; private static int OFFSET_SHORT_ITEM = 0x80;
/*
* If a string is more than 55 bytes long, the RLP encoding consists of a
* single byte with value 0xb7 plus the length of the length of the string
* in binary form, followed by the length of the string, followed by the
* string. For example, a length-1024 string would be encoded as
* \xb9\x04\x00 followed by the string. The range of the first byte is thus
* [0xb8, 0xbf].
*/
private static int OFFSET_LONG_ITEM = 0xb8;
/* /*
* If the total payload of a list (i.e. the combined length of all its * If the total payload of a list (i.e. the combined length of all its
* items) is 0-55 bytes long, the RLP encoding consists of a single byte * items) is 0-55 bytes long, the RLP encoding consists of a single byte
@ -61,8 +91,21 @@ public class RLP {
*/ */
private static int OFFSET_SHORT_LIST = 0xc0; private static int OFFSET_SHORT_LIST = 0xc0;
private static byte decodeOneByteItem(byte[] data, int index) { /*
* If the total payload of a list is more than 55 bytes long, the RLP
* encoding consists of a single byte with value 0xf7 plus the length of the
* length of the list in binary form, followed by the length of the list,
* followed by the concatenation of the RLP encodings of the items. The
* range of the first byte is thus [0xf8, 0xff].
*/
private static int OFFSET_LONG_LIST = 0xf8;
/* ******************************************************
* DECODING *
* ******************************************************/
private static byte decodeOneByteItem(byte[] data, int index) {
// null item // null item
if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM) { if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM) {
return (byte) (data[index] - OFFSET_SHORT_ITEM); return (byte) (data[index] - OFFSET_SHORT_ITEM);
@ -91,7 +134,7 @@ public class RLP {
pow--; pow--;
} }
} else { } else {
throw new Error("wrong decode attempt"); throw new RuntimeException("wrong decode attempt");
} }
return value; return value;
} }
@ -122,7 +165,7 @@ public class RLP {
pow--; pow--;
} }
} else { } else {
throw new Error("wrong decode attempt"); throw new RuntimeException("wrong decode attempt");
} }
return value; return value;
} }
@ -143,7 +186,7 @@ public class RLP {
value = new String(data, index + 1, length); value = new String(data, index + 1, length);
} else { } else {
throw new Error("wrong decode attempt"); throw new RuntimeException("wrong decode attempt");
} }
return value; return value;
} }
@ -163,7 +206,7 @@ public class RLP {
length = (byte) (data[index] - OFFSET_SHORT_ITEM); length = (byte) (data[index] - OFFSET_SHORT_ITEM);
} else { } else {
throw new Error("wrong decode attempt"); throw new RuntimeException("wrong decode attempt");
} }
byte[] valueBytes = new byte[length]; byte[] valueBytes = new byte[length];
System.arraycopy(data, index, valueBytes, 0, length); System.arraycopy(data, index, valueBytes, 0, length);
@ -186,7 +229,7 @@ public class RLP {
length = (byte) (data[index] - OFFSET_SHORT_ITEM); length = (byte) (data[index] - OFFSET_SHORT_ITEM);
} else { } else {
throw new Error("wrong decode attempt"); throw new RuntimeException("wrong decode attempt");
} }
byte[] valueBytes = new byte[length]; byte[] valueBytes = new byte[length];
System.arraycopy(data, index, valueBytes, 0, length); System.arraycopy(data, index, valueBytes, 0, length);
@ -209,7 +252,7 @@ public class RLP {
length = (byte) (data[index] - OFFSET_SHORT_ITEM); length = (byte) (data[index] - OFFSET_SHORT_ITEM);
} else { } else {
throw new Error("wrong decode attempt"); throw new RuntimeException("wrong decode attempt");
} }
byte[] valueBytes = new byte[length]; byte[] valueBytes = new byte[length];
System.arraycopy(data, index, valueBytes, 0, length); System.arraycopy(data, index, valueBytes, 0, length);
@ -278,13 +321,8 @@ public class RLP {
offset = offset + 1; offset = offset + 1;
byte dByte = decodeOneByteItem(data, index + offset); byte dByte = decodeOneByteItem(data, index + offset);
byte[] ip = new byte[4]; // return IP address
ip[0] = aByte; return new byte[] { aByte, bByte, cByte, dByte } ;
ip[1] = bByte;
ip[2] = cByte;
ip[3] = dByte;
return ip;
} }
public static int getFirstListElement(byte[] payload, int pos) { public static int getFirstListElement(byte[] payload, int pos) {
@ -293,22 +331,14 @@ public class RLP {
return -1; return -1;
if ((payload[pos] & 0xFF) >= 0xF7) { if ((payload[pos] & 0xFF) >= 0xF7) {
byte lengthOfLength = (byte) (payload[pos] - 0xF7); byte lengthOfLength = (byte) (payload[pos] - 0xF7);
return pos + lengthOfLength + 1; return pos + lengthOfLength + 1;
} }
if ((payload[pos] & 0xFF) >= OFFSET_SHORT_LIST && (payload[pos] & 0xFF) < 0xF7) { if ((payload[pos] & 0xFF) >= OFFSET_SHORT_LIST && (payload[pos] & 0xFF) < 0xF7) {
byte length = (byte) ((payload[pos] & 0xFF) - OFFSET_SHORT_LIST);
return pos + 1; return pos + 1;
} }
if ((payload[pos] & 0xFF) >= 0xB7 && (payload[pos] & 0xFF) < OFFSET_SHORT_LIST) { if ((payload[pos] & 0xFF) >= 0xB7 && (payload[pos] & 0xFF) < OFFSET_SHORT_LIST) {
byte lengthOfLength = (byte) (payload[pos] - 0xB7); byte lengthOfLength = (byte) (payload[pos] - 0xB7);
int length = calcLength(lengthOfLength, payload, pos);
return pos + lengthOfLength + 1; return pos + lengthOfLength + 1;
} }
@ -355,6 +385,7 @@ public class RLP {
*/ */
public static void fullTraverse(byte[] msgData, int level, int startPos, public static void fullTraverse(byte[] msgData, int level, int startPos,
int endPos, int levelToIndex, Queue<Integer> index) { int endPos, int levelToIndex, Queue<Integer> index) {
try { try {
if (msgData == null || msgData.length == 0) if (msgData == null || msgData.length == 0)
@ -443,7 +474,7 @@ public class RLP {
} }
} }
} catch (Throwable th) { } catch (Throwable th) {
throw new Error("wire packet not parsed correctly", throw new RuntimeException("wire packet not parsed correctly",
th.fillInStackTrace()); th.fillInStackTrace());
} }
} }
@ -468,6 +499,14 @@ public class RLP {
return length; return length;
} }
public static byte getCommandCode(byte[] data) {
byte command = 0;
int index = getFirstListElement(data, 0);
command = data[index];
command = ((int) (command & 0xFF) == OFFSET_SHORT_ITEM) ? 0 : command;
return command;
}
/** /**
* Parse wire byte[] message into RLP elements * Parse wire byte[] message into RLP elements
* *
@ -476,8 +515,10 @@ public class RLP {
* @param rlpList * @param rlpList
* - outcome of recursive RLP structure * - outcome of recursive RLP structure
*/ */
public static void parseObjects(byte[] msgData, RLPList rlpList) { public static RLPList decode2(byte[] msgData) {
RLP.fullTraverse(msgData, 0, 0, msgData.length, 1, rlpList); RLPList rlpList = new RLPList();
fullTraverse(msgData, 0, 0, msgData.length, 1, rlpList);
return rlpList;
} }
/** /**
@ -516,7 +557,7 @@ public class RLP {
fullTraverse(msgData, level + 1, pos + lengthOfLength + 1, fullTraverse(msgData, level + 1, pos + lengthOfLength + 1,
pos + lengthOfLength + length + 1, levelToIndex, pos + lengthOfLength + length + 1, levelToIndex,
newLevelList); newLevelList);
rlpList.addItem(newLevelList); rlpList.add(newLevelList);
pos += lengthOfLength + length + 1; pos += lengthOfLength + length + 1;
continue; continue;
@ -536,7 +577,7 @@ public class RLP {
if (length > 0) if (length > 0)
fullTraverse(msgData, level + 1, pos + 1, pos + length fullTraverse(msgData, level + 1, pos + 1, pos + length
+ 1, levelToIndex, newLevelList); + 1, levelToIndex, newLevelList);
rlpList.addItem(newLevelList); rlpList.add(newLevelList);
pos += 1 + length; pos += 1 + length;
continue; continue;
@ -560,7 +601,7 @@ public class RLP {
lengthOfLength + 1); lengthOfLength + 1);
RLPItem rlpItem = new RLPItem(item); RLPItem rlpItem = new RLPItem(item);
rlpList.addItem(rlpItem); rlpList.add(rlpItem);
pos += lengthOfLength + length + 1; pos += lengthOfLength + length + 1;
continue; continue;
@ -579,7 +620,7 @@ public class RLP {
System.arraycopy(msgData, pos, rlpPrefix, 0, 2); System.arraycopy(msgData, pos, rlpPrefix, 0, 2);
RLPItem rlpItem = new RLPItem(item); RLPItem rlpItem = new RLPItem(item);
rlpList.addItem(rlpItem); rlpList.add(rlpItem);
pos += 1 + length; pos += 1 + length;
continue; continue;
@ -588,7 +629,7 @@ public class RLP {
if ((msgData[pos] & 0xFF) == OFFSET_SHORT_ITEM) { if ((msgData[pos] & 0xFF) == OFFSET_SHORT_ITEM) {
byte[] item = new byte[0]; byte[] item = new byte[0];
RLPItem rlpItem = new RLPItem(item); RLPItem rlpItem = new RLPItem(item);
rlpList.addItem(rlpItem); rlpList.add(rlpItem);
pos += 1; pos += 1;
continue; continue;
} }
@ -598,124 +639,120 @@ public class RLP {
byte[] item = { (byte) (msgData[pos] & 0xFF) }; byte[] item = { (byte) (msgData[pos] & 0xFF) };
RLPItem rlpItem = new RLPItem(item); RLPItem rlpItem = new RLPItem(item);
rlpList.addItem(rlpItem); rlpList.add(rlpItem);
pos += 1; pos += 1;
continue; continue;
} }
} }
} catch (Throwable th) { } catch (Throwable th) {
throw new Error("wire packet not parsed correctly", throw new RuntimeException("wire packet not parsed correctly",
th.fillInStackTrace()); th.fillInStackTrace());
} }
} }
private static String rlpEncode(Object item) { /**
if (item instanceof String) { * Reads any RLP encoded byte-array and returns all objects as byte-array or list of byte-arrays
String str = ((String) item); *
int length = str.length(); * @param data RLP encoded byte-array
if (length == 1 && str.charAt(0) < OFFSET_SHORT_ITEM) * @param pos position in the array to start reading
return str; * @return DecodeResult encapsulates the decoded items as a single Object and the final read position
else */
return encodeLength(str.length(), OFFSET_SHORT_ITEM) + str; public static DecodeResult decode(byte[] data, int pos) {
} else if (item instanceof List) { if (data == null || data.length < 1) {
List itemList = (List) item;
StringBuilder output = new StringBuilder();
for (Object oneItem : itemList)
output.append(rlpEncode(oneItem));
return encodeLength(output.toString().length(), OFFSET_SHORT_LIST)
+ output.toString();
}
throw new Error("unsupported type" + item.getClass());
}
/** Integer limitation goes up to 2^31-1 so length can never be bigger */
private static String encodeLength(int L, int offset) {
if (L < SIZE_THRESHOLD)
return "" + (char) (L + offset);
else if (L < Math.pow(256, 8)) {
String BL = toBinary(L);
return "" + (char) (BL.length() + offset + SIZE_THRESHOLD - 1) + BL;
} else
throw new Error("input too long");
}
private static String toBinary(int x) {
if (x == 0)
return "";
else
return toBinary(x >> 8) + ((char) (x & 0x00FF));
}
public static byte getCommandCode(byte[] data) {
byte command = 0;
int index = getFirstListElement(data, 0);
command = data[index];
command = ((int) (command & 0xFF) == OFFSET_SHORT_ITEM) ? 0 : command;
return command;
}
private static Object decode(char[] data) {
if (data == null || data.length == 0)
return null; return null;
}
if (data[0] >= 0xF7) { int prefix = data[pos] & 0xFF;
/* if (prefix == OFFSET_SHORT_ITEM) {
* It's a list with a payload more than 55 bytes return new DecodeResult(pos+1, new byte[0]); // means no length or 0
* data[0] - 0xF7 = how many next bytes allocated for the length of the list } else if (prefix < OFFSET_SHORT_ITEM) {
*/ return new DecodeResult(pos+1, new byte[] { data[pos] }); // byte is its own RLP encoding
byte lengthOfLength = (byte) (data[0] - 0xF7); } else if (prefix < OFFSET_LONG_ITEM){
int len = prefix - OFFSET_SHORT_ITEM; // length of the encoded bytes
return new DecodeResult(pos+1+len, copyOfRange(data, pos+1, pos+1+len));
} else if (prefix < OFFSET_SHORT_LIST) {
int lenlen = prefix - OFFSET_LONG_ITEM + 1; // length of length the encoded bytes
int lenbytes = byteArrayToInt(copyOfRange(data, pos+1, pos+1+lenlen)); // length of encoded bytes
return new DecodeResult(pos+1+lenlen+lenbytes, copyOfRange(data, pos+1+lenlen, pos+1+lenlen+lenbytes));
} else if (prefix < OFFSET_LONG_LIST) {
int len = prefix - OFFSET_SHORT_LIST; // length of the encoded list
int prevPos = pos; pos++;
return decodeList(data, pos, prevPos, len);
} else if (prefix < 0xFF) {
int lenlen = prefix - OFFSET_LONG_LIST + 1; // length of length the encoded list
int lenlist = byteArrayToInt(copyOfRange(data, pos+1, pos+1+lenlen)); // length of encoded bytes
pos = pos + lenlen + 1;
int prevPos = lenlist;
return decodeList(data, pos, prevPos, lenlist);
} else {
throw new RuntimeException("Only byte values between 0x00 and 0xFF are supported, but got: " + prefix);
}
}
byte pow = (byte) (lengthOfLength - 1); private static DecodeResult decodeList(byte[] data, int pos, int prevPos, int len) {
long length = 0; List<Object> slice = new ArrayList<Object>();
for (int i = 1; i <= lengthOfLength; ++i) { for (int i = 0; i < len;) {
length += data[i] << (8 * pow); // Get the next item in the data list and append it
pow--; DecodeResult result = decode(data, pos);
slice.add(result.getDecoded());
// Increment pos by the amount bytes in the previous read
prevPos = result.getPos();
i += (prevPos - pos);
pos = prevPos;
} }
System.out.println(length); return new DecodeResult(pos, slice.toArray());
// now we can parse an item for data[1]..data[length]
} }
if (data[0] >= OFFSET_SHORT_LIST && data[0] < 0xF7)
/*
* It's a list with a payload less than 55 bytes
*/
;
if (data[0] >= 0xB7 && data[0] < OFFSET_SHORT_LIST) {
/*
* It's an item with a payload more than 55 bytes
* data[0] - 0xB7 = how much next bytes allocated for the length of the string
*/
;
byte lengthOfLength = (byte) (data[0] - 0xB7);
byte pow = (byte) (lengthOfLength - 1); /* ******************************************************
long length = 0; * ENCODING *
for (int i = 1; i <= lengthOfLength; ++i) { * ******************************************************/
length += data[i] << (8 * pow);
pow--; /**
} * Turn Object into its RLP encoded equivalent of a byte-array
System.out.println(length); * Support for String, Integer, BigInteger and Lists of any of these types.
// now we can parse an item for data[1]..data[length] *
} * @param item as object or List of objects
if (data[0] >= OFFSET_SHORT_ITEM && data[0] < 0xB7) { * @return byte[] RLP encoded
/*
* It's an item less than 55 bytes long,
* data[0] - OFFSET_SHORT_ITEM == length of the item
*/ */
; public static byte[] encode(Object input) {
Value val = new Value(input);
if (val.isList()) {
List<Object> inputArray = val.asList();
if (inputArray.size() == 0) {
return encodeLength(inputArray.size(), OFFSET_SHORT_LIST);
}
byte[] output = new byte[0];
for (Object object : inputArray) {
output = concatenate(output, encode(object));
}
byte[] prefix = encodeLength(output.length, OFFSET_SHORT_LIST);
return concatenate(prefix, output);
} else {
byte[] inputAsBytes = toBytes(input);
if(inputAsBytes.length == 1) {
return inputAsBytes;
} else {
byte[] firstByte = encodeLength(inputAsBytes.length, OFFSET_SHORT_ITEM);
return concatenate(firstByte, inputAsBytes);
}
}
}
/** Integer limitation goes up to 2^31-1 so length can never be bigger than MAX_ITEM_LENGTH */
public static byte[] encodeLength(int length, int offset) {
if (length < SIZE_THRESHOLD) {
byte firstByte = (byte) (length + offset);
return new byte[] { firstByte };
} else if (length < MAX_ITEM_LENGTH) {
byte[] binaryLength = BigInteger.valueOf(length).toByteArray();
byte firstByte = (byte) (binaryLength.length + offset + SIZE_THRESHOLD - 1 );
return concatenate(new byte[] { firstByte }, binaryLength);
} else {
throw new RuntimeException("Input too long");
} }
if (data[0] == OFFSET_SHORT_ITEM)
/* null item */
;
if (data[0] < OFFSET_SHORT_ITEM)
/* single byte item */
;
return null;
} }
public static byte[] encodeByte(byte singleByte) { public static byte[] encodeByte(byte singleByte) {
if ((singleByte & 0xFF) == 0) { if ((singleByte & 0xFF) == 0) {
return new byte[] { (byte) OFFSET_SHORT_ITEM }; return new byte[] { (byte) OFFSET_SHORT_ITEM };
} else if ((singleByte & 0xFF) < 0x7F) { } else if ((singleByte & 0xFF) < 0x7F) {
@ -729,7 +766,8 @@ public class RLP {
if (singleShort <= 0xFF) if (singleShort <= 0xFF)
return encodeByte((byte) singleShort); return encodeByte((byte) singleShort);
else { else {
return new byte[] { (byte) (OFFSET_SHORT_ITEM+2), (byte) (singleShort >> 8 & 0xFF), return new byte[] { (byte) (OFFSET_SHORT_ITEM + 2),
(byte) (singleShort >> 8 & 0xFF),
(byte) (singleShort >> 0 & 0xFF) }; (byte) (singleShort >> 0 & 0xFF) };
} }
} }
@ -816,4 +854,26 @@ public class RLP {
} }
return data; return data;
} }
/*
* Utility function to convert Objects into byte arrays
*/
private static byte[] toBytes(Object input) {
if (input instanceof byte[]) {
return (byte[]) input;
} else if (input instanceof String) {
String inputString = (String) input;
return inputString.getBytes();
} else if(input instanceof Integer) {
Integer inputInt = (Integer) input;
return (inputInt == 0) ? new byte[0] : BigInteger.valueOf(inputInt.longValue()).toByteArray();
} else if(input instanceof BigInteger) {
BigInteger inputBigInt = (BigInteger) input;
return (inputBigInt == BigInteger.ZERO) ? new byte[0] : inputBigInt.toByteArray();
} else if (input instanceof Value) {
Value val = (Value) input;
return toBytes(val.asObj());
}
throw new RuntimeException("Unsupported type: Only accepting String, Integer and BigInteger for now");
}
} }

View File

@ -0,0 +1,8 @@
package org.ethereum.util;
/**
* Wrapper class for decoded elements from an RLP encoded byte array.
*/
public interface RLPElement {
}

View File

@ -1,11 +1,13 @@
package org.ethereum.net.rlp; package org.ethereum.util;
import java.io.Serializable;
/** /**
* www.ethereumJ.com * www.ethereumJ.com
* User: Roman Mandeleil * User: Roman Mandeleil
* Created on: 21/04/14 16:26 * Created on: 21/04/14 16:26
*/ */
public class RLPItem implements RLPElement { public class RLPItem implements RLPElement, Serializable {
byte[] data; byte[] data;

View File

@ -1,39 +1,15 @@
package org.ethereum.net.rlp; package org.ethereum.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import org.ethereum.util.Utils;
/** /**
* www.ethereumJ.com * www.ethereumJ.com
* User: Roman Mandeleil * User: Roman Mandeleil
* Created on: 21/04/14 16:26 * Created on: 21/04/14 16:26
*/ */
public class RLPList implements RLPElement{ public class RLPList extends ArrayList<RLPElement> implements RLPElement {
byte[] rlpData; byte[] rlpData;
List<RLPElement> list;
public RLPList() {
this.list = new ArrayList<RLPElement>();
}
public void addItem(RLPElement element){
list.add(element);
}
public RLPElement getElement(int index){
return list.get(index);
}
public int size(){
return list.size();
}
public List<RLPElement> getList(){
return list;
}
public void setRLPData(byte[] rlpData){ public void setRLPData(byte[] rlpData){
this.rlpData = rlpData; this.rlpData = rlpData;
@ -46,12 +22,12 @@ public class RLPList implements RLPElement{
public static void recursivePrint(RLPElement element) { public static void recursivePrint(RLPElement element) {
if (element == null) if (element == null)
throw new Error("RLPElement object can't be null"); throw new RuntimeException("RLPElement object can't be null");
if (element instanceof RLPList) { if (element instanceof RLPList) {
RLPList rlpList = (RLPList) element; RLPList rlpList = (RLPList) element;
System.out.print("["); System.out.print("[");
for (RLPElement singleElement : rlpList.getList()) { for (RLPElement singleElement : rlpList) {
recursivePrint(singleElement); recursivePrint(singleElement);
} }
System.out.print("]"); System.out.print("]");

View File

@ -1,213 +0,0 @@
package org.ethereum.util;
import java.math.BigInteger;
import java.util.ArrayList;
import static java.util.Arrays.copyOfRange;
import static org.spongycastle.util.Arrays.concatenate;
import java.util.List;
/**
* Recursive Length Prefix (RLP) encoding.
*
* The purpose of RLP is to encode arbitrarily nested arrays of binary data, and
* RLP is the main encoding method used to serialize objects in Ethereum. The
* only purpose of RLP is to encode structure; encoding specific atomic data
* types (eg. strings, ints, floats) is left up to higher-order protocols; in
* Ethereum the standard is that integers are represented in big endian binary
* form. If one wishes to use RLP to encode a dictionary, the two suggested
* canonical forms are to either use [[k1,v1],[k2,v2]...] with keys in
* lexicographic order or to use the higher-level Patricia Tree encoding as
* Ethereum does.
*
* The RLP encoding function takes in an item. An item is defined as follows:
*
* - A string (ie. byte array) is an item - A list of items is an item
*
* For example, an empty string is an item, as is the string containing the word
* "cat", a list containing any number of strings, as well as more complex data
* structures like ["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]. Note
* that in the context of the rest of this article, "string" will be used as a
* synonym for "a certain number of bytes of binary data"; no special encodings
* are used and no knowledge about the content of the strings is implied.
*
* See: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
*/
public class RlpEncoder extends CompactEncoder {
/** Allow for content up to size of 2^64 bytes **/
private static double MAX_ITEM_LENGTH = Math.pow(256, 8);
/**
[5:30:35 PM] Vitalik Buterin: 56 bytes maximizes the benefit of both options
[5:30:41 PM] Vitalik Buterin: if we went with 60
[5:31:03 PM] Vitalik Buterin: then we would have only had 4 slots for long strings so RLP would not have been able to store objects above 4gb
[5:31:08 PM] Vitalik Buterin: if we went with 48
[5:31:18 PM] Vitalik Buterin: then RLP would be fine for 2^128 space, but that's way too much
[5:31:32 PM] Vitalik Buterin: so 56 and 2^64 space seems like the right place to put the cutoff
[5:31:44 PM] Vitalik Buterin: also, that's where Bitcoin's varint does the cutof
**/
private static int SIZE_THRESHOLD = 56;
/** RLP encoding rules are defined as follows: */
/*
* For a single byte whose value is in the [0x00, 0x7f] range, that byte is
* its own RLP encoding.
*/
/*
* If a string is 0-55 bytes long, the RLP encoding consists of a single
* byte with value 0x80 plus the length of the string followed by the
* string. The range of the first byte is thus [0x80, 0xb7].
*/
private static int offsetShortItem = 0x80;
/*
* If a string is more than 55 bytes long, the RLP encoding consists of a
* single byte with value 0xb7 plus the length of the length of the string
* in binary form, followed by the length of the string, followed by the
* string. For example, a length-1024 string would be encoded as
* \xb9\x04\x00 followed by the string. The range of the first byte is thus
* [0xb8, 0xbf].
*/
private static int offsetLongItem = 0xb8;
/*
* If the total payload of a list (i.e. the combined length of all its
* items) is 0-55 bytes long, the RLP encoding consists of a single byte
* with value 0xc0 plus the length of the list followed by the concatenation
* of the RLP encodings of the items. The range of the first byte is thus
* [0xc0, 0xf7].
*/
private static int offsetShortList = 0xc0;
/*
* If the total payload of a list is more than 55 bytes long, the RLP
* encoding consists of a single byte with value 0xf7 plus the length of the
* length of the list in binary form, followed by the length of the list,
* followed by the concatenation of the RLP encodings of the items. The
* range of the first byte is thus [0xf8, 0xff].
*/
private static int offsetLongList = 0xf8;
private static int maxPrefix = 0xff;
public static byte[] encode(Object input) {
Value val = new Value(input);
if (val.isList()) {
List<Object> inputArray = val.asList();
if (inputArray.size() == 0) {
return encodeLength(inputArray.size(), offsetShortList);
}
byte[] output = new byte[0];
for (Object object : inputArray) {
output = concatenate(output, encode(object));
}
byte[] prefix = encodeLength(output.length, offsetShortList);
return concatenate(prefix, output);
} else {
byte[] inputAsHex = asHex(input);
if(inputAsHex.length == 1) {
return inputAsHex;
} else {
byte[] firstByte = encodeLength(inputAsHex.length, offsetShortItem);
return concatenate(firstByte, inputAsHex);
}
}
}
public static DecodeResult decode(byte[] data, int pos) {
if (data == null || data.length < 1) {
return null;
}
int prefix = data[pos] & maxPrefix;
if (prefix == offsetShortItem) {
return new DecodeResult(pos+1, new byte[0]); // means no length or 0
} else if (prefix < offsetShortItem) {
return new DecodeResult(pos+1, new byte[] { data[pos] }); // byte is its own RLP encoding
} else if (prefix < offsetLongItem){
int len = prefix - offsetShortItem; // length of the encoded bytes
return new DecodeResult(pos+1+len, copyOfRange(data, pos+1, pos+1+len));
} else if (prefix < offsetShortList) {
int lenlen = prefix - offsetLongItem + 1; // length of length the encoded bytes
int lenbytes = toInt(copyOfRange(data, pos+1, pos+1+lenlen)); // length of encoded bytes
return new DecodeResult(pos+1+lenlen+lenbytes, copyOfRange(data, pos+1+lenlen, pos+1+lenlen+lenbytes));
} else if (prefix < offsetLongList) {
int len = prefix - offsetShortList; // length of the encoded list
int prevPos = pos; pos++;
return decodeList(data, pos, prevPos, len);
} else if (prefix < maxPrefix) {
int lenlen = prefix - offsetLongList + 1; // length of length the encoded list
int lenlist = toInt(copyOfRange(data, pos+1, pos+1+lenlen)); // length of encoded bytes
pos = pos + lenlen + 1;
int prevPos = lenlist;
return decodeList(data, pos, prevPos, lenlist);
} else {
throw new RuntimeException("Only byte values between 0x00 and 0xFF are supported, but got: " + prefix);
}
}
/** Integer limitation goes up to 2^31-1 so length can never be bigger than MAX_ITEM_LENGTH */
public static byte[] encodeLength(int length, int offset) {
if (length < SIZE_THRESHOLD) {
byte firstByte = (byte) (length + offset);
return new byte[] { firstByte };
} else if (length < MAX_ITEM_LENGTH) {
byte[] binaryLength = BigInteger.valueOf(length).toByteArray();
byte firstByte = (byte) (binaryLength.length + offset + SIZE_THRESHOLD - 1 );
return concatenate(new byte[] { firstByte }, binaryLength);
} else {
throw new RuntimeException("Input too long");
}
}
private static DecodeResult decodeList(byte[] data, int pos, int prevPos, int len) {
List<Object> slice = new ArrayList<Object>();
for (int i = 0; i < len;) {
// Get the next item in the data list and append it
DecodeResult result = decode(data, pos);
slice.add(result.getDecoded());
// Increment pos by the amount bytes in the previous read
prevPos = result.getPos();
i += (prevPos - pos);
pos = prevPos;
}
return new DecodeResult(pos, slice.toArray());
}
public static byte[] asHex(Object input) {
if (input instanceof byte[]) {
return (byte[]) input;
} else if (input instanceof String) {
String inputString = (String) input;
return inputString.getBytes();
} else if(input instanceof Integer) {
Integer inputInt = (Integer) input;
return (inputInt == 0) ? new byte[0] : BigInteger.valueOf(inputInt.longValue()).toByteArray();
} else if(input instanceof BigInteger) {
BigInteger inputBigInt = (BigInteger) input;
return (inputBigInt == BigInteger.ZERO) ? new byte[0] : inputBigInt.toByteArray();
} else if (input instanceof Value) {
Value val = (Value) input;
return asHex(val.asObj());
}
throw new RuntimeException("Unsupported type: Only accepting String, Integer and BigInteger for now");
}
/**
* Cast hex encoded value from byte[] to int
*
* Limited to Integer.MAX_VALUE: 2^32-1
*
* @param b array contains the hex values
* @return int value of all hex values together.
*/
public static int toInt(byte[] b) {
if (b == null || b.length == 0) {
return 0;
}
return new BigInteger(b).intValue();
}
}

View File

@ -15,7 +15,7 @@ public class Value {
public void fromRlpEncoded(byte[] data) { public void fromRlpEncoded(byte[] data) {
if (data.length != 0) { if (data.length != 0) {
this.value = RlpEncoder.decode(data, 0).getDecoded(); this.value = RLP.decode(data, 0).getDecoded();
} }
} }
@ -104,7 +104,7 @@ public class Value {
* *****************/ * *****************/
public byte[] encode() { public byte[] encode() {
return RlpEncoder.encode(value); return RLP.encode(value);
} }
public boolean cmp(Value o) { public boolean cmp(Value o) {

View File

@ -1,11 +1,10 @@
package org.ethereum.block; package org.ethereum.block;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import org.ethereum.core.Block;
import org.ethereum.crypto.HashUtil; import org.ethereum.crypto.HashUtil;
import org.ethereum.net.rlp.RLP; import org.ethereum.util.RLP;
import org.ethereum.net.rlp.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.net.vo.Block;
import org.ethereum.util.RlpEncoder;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -129,7 +128,7 @@ public class BlockTest {
public void testGenesisFromNew() { public void testGenesisFromNew() {
System.out.println(CPP_PoC5_GENESIS_HEX_RLP_ENCODED); System.out.println(CPP_PoC5_GENESIS_HEX_RLP_ENCODED);
Object genesisItems = RlpEncoder.decode(CPP_PoC5_GENESIS_HEX_RLP_ENCODED.getBytes(), 0).getDecoded(); Object genesisItems = RLP.decode(Hex.decode(CPP_PoC5_GENESIS_HEX_RLP_ENCODED), 0).getDecoded();
// TODO: verify genesis items with expected values // TODO: verify genesis items with expected values
/* From: https://ethereum.etherpad.mozilla.org/11 /* From: https://ethereum.etherpad.mozilla.org/11
@ -183,8 +182,7 @@ public class BlockTest {
byte[] payload = Hex.decode(blocksMsg); byte[] payload = Hex.decode(blocksMsg);
RLPList rlpList = new RLPList(); RLPList rlpList = RLP.decode2(payload);
RLP.parseObjects(payload, rlpList);
Block blockData = new Block(rlpList); Block blockData = new Block(rlpList);
RLPList.recursivePrint(rlpList); RLPList.recursivePrint(rlpList);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,425 +0,0 @@
package org.ethereum.util;
import static org.ethereum.util.RlpEncoder.toInt;
import static org.ethereum.util.RlpTestData.expected14;
import static org.ethereum.util.RlpTestData.expected16;
import static org.ethereum.util.RlpTestData.result01;
import static org.ethereum.util.RlpTestData.result02;
import static org.ethereum.util.RlpTestData.result03;
import static org.ethereum.util.RlpTestData.result04;
import static org.ethereum.util.RlpTestData.result05;
import static org.ethereum.util.RlpTestData.result06;
import static org.ethereum.util.RlpTestData.result07;
import static org.ethereum.util.RlpTestData.result08;
import static org.ethereum.util.RlpTestData.result09;
import static org.ethereum.util.RlpTestData.result10;
import static org.ethereum.util.RlpTestData.result11;
import static org.ethereum.util.RlpTestData.result12;
import static org.ethereum.util.RlpTestData.result13;
import static org.ethereum.util.RlpTestData.result14;
import static org.ethereum.util.RlpTestData.result15;
import static org.ethereum.util.RlpTestData.result16;
import static org.ethereum.util.RlpTestData.test01;
import static org.ethereum.util.RlpTestData.test02;
import static org.ethereum.util.RlpTestData.test03;
import static org.ethereum.util.RlpTestData.test04;
import static org.ethereum.util.RlpTestData.test05;
import static org.ethereum.util.RlpTestData.test06;
import static org.ethereum.util.RlpTestData.test07;
import static org.ethereum.util.RlpTestData.test08;
import static org.ethereum.util.RlpTestData.test09;
import static org.ethereum.util.RlpTestData.test10;
import static org.ethereum.util.RlpTestData.test11;
import static org.ethereum.util.RlpTestData.test12;
import static org.ethereum.util.RlpTestData.test13;
import static org.ethereum.util.RlpTestData.test14;
import static org.ethereum.util.RlpTestData.test15;
import static org.ethereum.util.RlpTestData.test16;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.math.BigInteger;
import java.util.Arrays;
import org.junit.Test;
import com.cedarsoftware.util.DeepEquals;
public class RlpEncoderTest {
/************************************
* Test data from: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
*
* Using assertEquals(String, String) instead of assertArrayEquals to see the actual content when the test fails.
*/
@Test(expected = RuntimeException.class)
public void testEncodeNull() {
RlpEncoder.encode(null);
}
@Test
public void testEncodeEmptyString() {
String test = "";
String expected = "80";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals(test, bytesToAscii(decodeResult));
}
@Test
public void testEncodeShortString() {
String test = "dog";
String expected = "83646f67";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals(test, bytesToAscii(decodeResult));
}
@Test
public void testEncodeSingleCharacter() {
String test = "d";
String expected = "64";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals(test, bytesToAscii(decodeResult));
}
@Test
public void testEncodeLongString() {
String test = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; // length = 56
String expected = "b8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c6974";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals(test, bytesToAscii(decodeResult));
}
@Test
public void testEncodeZero() {
Integer test = new Integer(0);
String expected = "80";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
int result = toInt(decodeResult);
assertEquals(test, Integer.valueOf(result));
}
@Test
public void testEncodeSmallInteger() {
Integer test = new Integer(15);
String expected = "0f";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
int result = toInt(decodeResult);
assertEquals(test, Integer.valueOf(result));
}
@Test
public void testEncodeMediumInteger() {
Integer test = new Integer(1000);
String expected = "8203e8";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
int result = toInt(decodeResult);
assertEquals(test, Integer.valueOf(result));
test = new Integer(1024);
expected = "820400";
encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
result = toInt(decodeResult);
assertEquals(test, Integer.valueOf(result));
}
@Test
public void testEncodeBigInteger() {
BigInteger test = new BigInteger("100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 16);
String expected = "a0100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
byte[] decodeResult = (byte[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals(test, new BigInteger(decodeResult));
}
@Test
public void TestEncodeEmptyList() {
String[] test = new String[0];
String expected = "c0";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
Object[] decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertTrue(decodeResult.length == 0);
}
@Test
public void testEncodeShortStringList() {
String[] test = new String[] { "cat", "dog" };
String expected = "c88363617483646f67";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
Object[] decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals("cat", bytesToAscii((byte[]) decodeResult[0]));
assertEquals("dog", bytesToAscii((byte[]) decodeResult[1]));
test = new String[] { "dog", "god", "cat" };
expected = "cc83646f6783676f6483636174";
encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals("dog", bytesToAscii((byte[]) decodeResult[0]));
assertEquals("god", bytesToAscii((byte[]) decodeResult[1]));
assertEquals("cat", bytesToAscii((byte[]) decodeResult[2]));
}
@Test
public void testEncodeLongStringList() {
String element1 = "cat";
String element2 = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
String[] test = new String[] { element1, element2 };
String expected = "f83e83636174b8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c6974";
byte[] encoderesult = (byte[]) RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
Object[] decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals(element1, bytesToAscii((byte[]) decodeResult[0]));
assertEquals(element2, bytesToAscii((byte[]) decodeResult[1]));
}
//multilist:
//in: [ 1, ["cat"], "dog", [ 2 ] ],
//out: "cc01c48363617483646f67c102"
//in: [ [ ["cat"], ["dog"] ], [ [1] [2] ], [] ],
//out: "cdc88363617483646f67c20102c0"
@Test
public void testEncodeMultiList() {
Object[] test = new Object[] { 1, new Object[] { "cat" }, "dog", new Object[] { 2 } };
String expected = "cc01c48363617483646f67c102";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
Object[] decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals(1, toInt( (byte[]) decodeResult[0] ));
assertEquals("cat", bytesToAscii( ((byte[]) ((Object[]) decodeResult[1])[0] )));
assertEquals("dog", bytesToAscii( (byte[]) decodeResult[2]));
assertEquals(2, toInt( ((byte[]) ((Object[]) decodeResult[3])[0] )));
test = new Object[] { new Object[] { "cat", "dog" }, new Object[] { 1, 2 }, new Object[] { } };
expected = "cdc88363617483646f67c20102c0";
encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertEquals("cat", bytesToAscii( ((byte[]) ((Object[]) decodeResult[0])[0] )));
assertEquals("dog", bytesToAscii( ((byte[]) ((Object[]) decodeResult[0])[1] )));
assertEquals(1, toInt( ((byte[]) ((Object[]) decodeResult[1])[0] )));
assertEquals(2, toInt( ((byte[]) ((Object[]) decodeResult[1])[1] )));
assertTrue( ( ((Object[]) decodeResult[2]).length == 0 ));
}
@Test
public void testEncodeEmptyListOfList() {
// list = [ [ [], [] ], [] ],
Object[] test = new Object[] { new Object[] { new Object[] {}, new Object[] {} }, new Object[] {} };
String expected = "c4c2c0c0c0";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
Object[] decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertTrue( decodeResult.length == 2 );
assertTrue( ( (Object[]) (decodeResult[0] ) ).length == 2);
assertTrue( ( (Object[]) (decodeResult[1] ) ).length == 0);
assertTrue( ( (Object[]) ( (Object[]) ( decodeResult[0] ) )[0]).length == 0);
assertTrue( ( (Object[]) ( (Object[]) ( decodeResult[0] ) )[1]).length == 0);
}
//The set theoretical representation of two
@Test
public void testEncodeRepOfTwoListOfList() {
//list: [ [], [[]], [ [], [[]] ] ]
Object[] test = new Object[] { new Object[] { }, new Object[] { new Object[] {} }, new Object[] { new Object[] {}, new Object[] { new Object[] { } } } };
String expected = "c7c0c1c0c3c0c1c0";
byte[] encoderesult = RlpEncoder.encode(test);
assertEquals(expected, asHex(encoderesult));
Object[] decodeResult = (Object[]) RlpEncoder.decode(encoderesult, 0).getDecoded();
assertTrue( decodeResult.length == 3 );
assertTrue( ( (Object[]) (decodeResult[0]) ).length == 0);
assertTrue( ( (Object[]) (decodeResult[1]) ).length == 1);
assertTrue( ( (Object[]) (decodeResult[2]) ).length == 2);
assertTrue( ( (Object[]) ( (Object[]) (decodeResult[1]) )[0]).length == 0);
assertTrue( ( (Object[]) ( (Object[]) (decodeResult[2]) )[0]).length == 0);
assertTrue( ( (Object[]) ( (Object[]) (decodeResult[2]) )[1]).length == 1);
assertTrue( ( (Object[]) ( (Object[]) ( (Object[]) (decodeResult[2]) )[1] )[0]).length == 0);
}
@Test
public void testRlpEncode() {
assertEquals(result01, asHex(RlpEncoder.encode(test01)));
assertEquals(result02, asHex(RlpEncoder.encode(test02)));
assertEquals(result03, asHex(RlpEncoder.encode(test03)));
assertEquals(result04, asHex(RlpEncoder.encode(test04)));
assertEquals(result05, asHex(RlpEncoder.encode(test05)));
assertEquals(result06, asHex(RlpEncoder.encode(test06)));
assertEquals(result07, asHex(RlpEncoder.encode(test07)));
assertEquals(result08, asHex(RlpEncoder.encode(test08)));
assertEquals(result09, asHex(RlpEncoder.encode(test09)));
assertEquals(result10, asHex(RlpEncoder.encode(test10)));
assertEquals(result11, asHex(RlpEncoder.encode(test11)));
assertEquals(result12, asHex(RlpEncoder.encode(test12)));
assertEquals(result13, asHex(RlpEncoder.encode(test13)));
assertEquals(result14, asHex(RlpEncoder.encode(test14)));
assertEquals(result15, asHex(RlpEncoder.encode(test15)));
assertEquals(result16, asHex(RlpEncoder.encode(test16)));
}
@Test
public void testRlpDecode() {
int pos = 0;
byte[] decodedByte;
byte[] decodedData;
Object[] decodedList;
decodedByte = (byte[]) RlpEncoder.decode(fromHex(result01), pos).getDecoded();
assertEquals(test01, toInt(decodedByte));
decodedData = (byte[]) RlpEncoder.decode(fromHex(result02), pos).getDecoded();
assertEquals(test02, bytesToAscii(decodedData));
decodedData = (byte[]) RlpEncoder.decode(fromHex(result03), pos).getDecoded();
assertEquals(test03, bytesToAscii(decodedData));
decodedData = (byte[]) RlpEncoder.decode(fromHex(result04), pos).getDecoded();
assertEquals(test04, bytesToAscii(decodedData));
decodedData = (byte[]) RlpEncoder.decode(fromHex(result05), pos).getDecoded();
assertEquals(test05, bytesToAscii(decodedData));
decodedList = (Object[]) RlpEncoder.decode(fromHex(result06), pos).getDecoded();
assertEquals(test06[0], bytesToAscii((byte[]) decodedList[0]));
assertEquals(test06[1], bytesToAscii((byte[]) decodedList[1]));
decodedList = (Object[]) RlpEncoder.decode(fromHex(result07), pos).getDecoded();
assertEquals(test07[0], bytesToAscii((byte[]) decodedList[0]));
assertEquals(test07[1], bytesToAscii((byte[]) decodedList[1]));
assertEquals(test07[2], bytesToAscii((byte[]) decodedList[2]));
// 1
decodedData = (byte[]) RlpEncoder.decode(fromHex(result08), pos).getDecoded();
assertEquals(test08, toInt(decodedData));
// 10
decodedData = (byte[]) RlpEncoder.decode(fromHex(result09), pos).getDecoded();
assertEquals(test09, toInt(decodedData));
// 100
decodedData = (byte[]) RlpEncoder.decode(fromHex(result10), pos).getDecoded();
assertEquals(test10, toInt(decodedData));
// 1000
decodedData = (byte[]) RlpEncoder.decode(fromHex(result11), pos).getDecoded();
assertEquals(test11, toInt(decodedData));
decodedData = (byte[]) RlpEncoder.decode(fromHex(result12), pos).getDecoded();
assertTrue(test12.compareTo(new BigInteger(decodedData)) == 0);
decodedData = (byte[]) RlpEncoder.decode(fromHex(result13), pos).getDecoded();
assertTrue(test13.compareTo(new BigInteger(decodedData)) == 0);
// Need to test with different expected value, because decoding doesn't recognize types
Object testObject1 = RlpEncoder.decode(fromHex(result14), pos).getDecoded();
assertTrue(DeepEquals.deepEquals(expected14, testObject1));
Object testObject2 = RlpEncoder.decode(fromHex(result15), pos).getDecoded();
assertTrue(DeepEquals.deepEquals(test15, testObject2));
// Need to test with different expected value, because decoding doesn't recognize types
Object testObject3 = RlpEncoder.decode(fromHex(result16), pos).getDecoded();
assertTrue(DeepEquals.deepEquals(expected16, testObject3));
}
@Test
public void testEncodeLength() {
int length;
int offset;
byte[] encodedLength;
String expected;
// length < 56
length = 1; offset = 128;
encodedLength = RlpEncoder.encodeLength(length, offset);
expected = "81";
assertEquals(expected, asHex(encodedLength));
// 56 > length < 2^64
length = 56; offset = 192;
encodedLength = RlpEncoder.encodeLength(length, offset);
expected = "f838";
assertEquals(expected, asHex(encodedLength));
// length > 2^64
// TODO: Fix this test - when casting double to int, information gets lost since 'int' is max (2^31)-1
double maxLength = Math.pow(256, 8); offset = 192;
try {
encodedLength = RlpEncoder.encodeLength( (int) maxLength, offset);
System.out.println("length: " + length + ", offset: " + offset + ", encoded: " + Arrays.toString(encodedLength));
fail("Expecting RuntimeException: 'Input too long'");
} catch(RuntimeException e) {
// Success!
}
}
// Code from: http://stackoverflow.com/a/9855338/459349
protected final static char[] hexArray = "0123456789abcdef".toCharArray();
private static String asHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
// Code from: http://stackoverflow.com/a/4785776/459349
private String bytesToAscii(byte[] b) {
String hex = asHex(b);
StringBuilder output = new StringBuilder();
for (int i = 0; i < hex.length(); i+=2) {
String str = hex.substring(i, i+2);
output.append((char)Integer.parseInt(str, 16));
}
return output.toString();
}
// Code from: http://stackoverflow.com/a/140861/459349
public static byte[] fromHex(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}