Merge pull request #2 from nicksavers/master
Merge RLP classes and tests
This commit is contained in:
commit
b1a965c549
|
@ -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);
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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.*;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
|
||||||
+ " ]";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 ?
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
|
||||||
|
|
||||||
}
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.ethereum.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for decoded elements from an RLP encoded byte array.
|
||||||
|
*/
|
||||||
|
public interface RLPElement {
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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("]");
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue