Merge RLP classes and tests but needs more scrutiny
This commit is contained in:
parent
f50703f69c
commit
7eaf9f0dba
|
@ -1,9 +1,9 @@
|
|||
package org.ethereum.net.vo;
|
||||
package org.ethereum.core;
|
||||
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.net.rlp.RLPElement;
|
||||
import org.ethereum.net.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.util.RLPElement;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.ethereum.util.Utils;
|
||||
|
||||
import java.math.BigInteger;
|
|
@ -1,10 +1,10 @@
|
|||
package org.ethereum.net.vo;
|
||||
package org.ethereum.core;
|
||||
|
||||
import org.ethereum.crypto.ECKey.ECDSASignature;
|
||||
import org.ethereum.crypto.ECKey;
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.net.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.ethereum.util.Utils;
|
||||
|
||||
/**
|
||||
|
@ -89,7 +89,7 @@ public class Transaction {
|
|||
byte[] r = ((RLPItem) rawData.getElement(8)).getData();
|
||||
byte[] s = ((RLPItem) rawData.getElement(9)).getData();
|
||||
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;
|
||||
}
|
||||
|
|
@ -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 org.ethereum.core.Block;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.geodb.IpGeoDB;
|
||||
import org.ethereum.net.vo.Block;
|
||||
import org.ethereum.net.vo.PeerData;
|
||||
import org.ethereum.net.vo.Transaction;
|
||||
import org.ethereum.net.client.PeerData;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
package org.ethereum.net.client;
|
||||
|
||||
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.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
@ -21,6 +11,7 @@ import java.util.List;
|
|||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.gui.PeerListener;
|
||||
import org.ethereum.manager.MainData;
|
||||
import org.ethereum.net.Command;
|
||||
|
@ -33,9 +24,8 @@ import org.ethereum.net.message.NotInChainMessage;
|
|||
import org.ethereum.net.message.PeersMessage;
|
||||
import org.ethereum.net.message.StaticMessages;
|
||||
import org.ethereum.net.message.TransactionsMessage;
|
||||
import org.ethereum.net.rlp.RLP;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.net.vo.Block;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.ethereum.util.Utils;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
|
@ -140,9 +130,8 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
// got HELLO
|
||||
if (Command.fromInt(command) == HELLO) {
|
||||
System.out.println("[Recv: HELLO]" );
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
|
||||
HelloMessage helloMessage = new HelloMessage(rlpList);
|
||||
System.out.println(helloMessage.toString());
|
||||
if (peerListener != null) peerListener.console(helloMessage.toString());
|
||||
|
@ -152,8 +141,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
System.out.println("[Recv: DISCONNECT]");
|
||||
if (peerListener != null) peerListener.console("[Recv: DISCONNECT]");
|
||||
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
DisconnectMessage disconnectMessage = new DisconnectMessage(rlpList);
|
||||
|
||||
System.out.println(disconnectMessage);
|
||||
|
@ -198,8 +186,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
System.out.println("[Recv: PEERS]");
|
||||
if (peerListener != null) peerListener.console("[Recv: PEERS]");
|
||||
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
PeersMessage peersMessage = new PeersMessage(rlpList);
|
||||
|
||||
MainData.instance.addPeers(peersMessage.getPeers());
|
||||
|
@ -212,8 +199,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
System.out.println("Recv: TRANSACTIONS]");
|
||||
if (peerListener != null) peerListener.console("Recv: TRANSACTIONS]");
|
||||
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
TransactionsMessage transactionsMessage = new TransactionsMessage(rlpList);
|
||||
MainData.instance.addTransactions(transactionsMessage.getTransactions());
|
||||
|
||||
|
@ -226,8 +212,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
System.out.println("[Recv: BLOCKS]");
|
||||
if (peerListener != null) peerListener.console("[Recv: BLOCKS]");
|
||||
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
|
||||
BlocksMessage blocksMessage = new BlocksMessage(rlpList);
|
||||
List<Block> blockList = blocksMessage.getBlockDataList();
|
||||
|
@ -241,8 +226,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
System.out.println("[Recv: GET_CHAIN]");
|
||||
if (peerListener != null) peerListener.console("[Recv: GET_CHAIN]");
|
||||
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
GetChainMessage getChainMessage = new GetChainMessage(rlpList);
|
||||
|
||||
System.out.println(getChainMessage);
|
||||
|
@ -253,8 +237,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
System.out.println("[Recv: NOT_IN_CHAIN]");
|
||||
if (peerListener != null) peerListener.console("[Recv: NOT_IN_CHAIN]");
|
||||
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
NotInChainMessage notInChainMessage = new NotInChainMessage(rlpList);
|
||||
|
||||
System.out.println(notInChainMessage);
|
||||
|
@ -287,7 +270,6 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
|
|||
private void sendMsg(Message msg, ChannelHandlerContext ctx){
|
||||
|
||||
byte[] data = msg.getPayload();
|
||||
|
||||
final ByteBuf buffer = ctx.alloc().buffer(data.length + 8);
|
||||
byte[] packetLen = calcPacketLength(data);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.ethereum.net.vo;
|
||||
package org.ethereum.net.client;
|
||||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
|
@ -5,11 +5,11 @@ import java.util.List;
|
|||
|
||||
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.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.net.vo.Block;
|
||||
import org.ethereum.net.vo.Transaction;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
|
@ -18,51 +18,50 @@ import org.ethereum.net.vo.Transaction;
|
|||
*/
|
||||
public class BlocksMessage extends Message {
|
||||
|
||||
private List<Block> blockDataList = new ArrayList<Block>();
|
||||
private List<Block> blockDataList = new ArrayList<Block>();
|
||||
|
||||
public BlocksMessage(RLPList rawData) {
|
||||
super(rawData);
|
||||
}
|
||||
public BlocksMessage(RLPList rawData) {
|
||||
super(rawData);
|
||||
}
|
||||
|
||||
public void parseRLP() {
|
||||
public void parseRLP() {
|
||||
|
||||
RLPList paramsList = (RLPList) rawData.getElement(0);
|
||||
RLPList paramsList = (RLPList) rawData.getElement(0);
|
||||
|
||||
if (Command.fromInt(((RLPItem) (paramsList).getElement(0)).getData()[0]) != BLOCKS) {
|
||||
throw new Error("BlocksMessage: parsing for mal data");
|
||||
}
|
||||
|
||||
if ( Command.fromInt(((RLPItem)(paramsList).getElement(0)).getData()[0]) != BLOCKS){
|
||||
throw new Error("BlocksMessage: parsing for mal data");
|
||||
}
|
||||
for (int i = 1; i < paramsList.size(); ++i) {
|
||||
RLPList rlpData = ((RLPList) paramsList.getElement(i));
|
||||
Block blockData = new Block(rlpData);
|
||||
this.blockDataList.add(blockData);
|
||||
}
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
for (int i = 1; i < paramsList.size(); ++i){
|
||||
RLPList rlpData = ((RLPList)paramsList.getElement(i));
|
||||
Block blockData = new Block(rlpData);
|
||||
this.blockDataList.add(blockData);
|
||||
}
|
||||
parsed = true;
|
||||
}
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Block> getBlockDataList() {
|
||||
if (!parsed) parseRLP();
|
||||
return blockDataList;
|
||||
}
|
||||
public List<Block> getBlockDataList() {
|
||||
if (!parsed)
|
||||
parseRLP();
|
||||
return blockDataList;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (Block blockData : this.getBlockDataList()){
|
||||
sb.append(" ").append( blockData.toString() ).append("\n");
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (Block blockData : this.getBlockDataList()) {
|
||||
sb.append(" ").append(blockData.toString()).append("\n");
|
||||
|
||||
List<Transaction> transactions = blockData.getTransactionsList();
|
||||
for (Transaction transactionData : transactions){
|
||||
sb.append("[").append(transactionData).append("]\n");
|
||||
}
|
||||
}
|
||||
return "Blocks Message [\n" +
|
||||
sb.toString()
|
||||
+ " ]";
|
||||
}
|
||||
List<Transaction> transactions = blockData.getTransactionsList();
|
||||
for (Transaction transactionData : transactions) {
|
||||
sb.append("[").append(transactionData).append("]\n");
|
||||
}
|
||||
}
|
||||
return "Blocks Message [\n" + sb.toString() + " ]";
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
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.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
|
||||
import static org.ethereum.net.Command.DISCONNECT;
|
||||
import static org.ethereum.net.message.ReasonCode.DISCONNECT_REQUESTED;
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import static org.ethereum.net.Command.GET_CHAIN;
|
||||
|
||||
import org.ethereum.net.Command;
|
||||
import org.ethereum.net.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.ethereum.util.Utils;
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,9 +4,9 @@ import org.spongycastle.util.encoders.Hex;
|
|||
|
||||
import static org.ethereum.net.Command.HELLO;
|
||||
|
||||
import org.ethereum.net.rlp.RLP;
|
||||
import org.ethereum.net.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.ethereum.net.message;
|
||||
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.util.RLPList;
|
||||
|
||||
/**
|
||||
* 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.message.Message;
|
||||
import org.ethereum.net.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.ethereum.util.Utils;
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,10 +5,11 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import static org.ethereum.net.Command.PEERS;
|
||||
|
||||
import org.ethereum.net.Command;
|
||||
import org.ethereum.net.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.net.vo.PeerData;
|
||||
import org.ethereum.net.client.PeerData;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package org.ethereum.net.message;
|
||||
|
||||
import static org.ethereum.net.Command.TRANSACTIONS;
|
||||
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.net.Command;
|
||||
import org.ethereum.net.rlp.RLPItem;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.net.vo.Transaction;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -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,11 +1,20 @@
|
|||
package org.ethereum.net.rlp;
|
||||
package org.ethereum.util;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
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.
|
||||
*
|
||||
|
@ -34,6 +43,9 @@ import java.util.Queue;
|
|||
*/
|
||||
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:
|
||||
* - 56 bytes maximizes the benefit of both options
|
||||
|
@ -45,13 +57,30 @@ public class RLP {
|
|||
**/
|
||||
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 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
|
||||
* items) is 0-55 bytes long, the RLP encoding consists of a single byte
|
||||
|
@ -60,9 +89,22 @@ public class RLP {
|
|||
* [0xc0, 0xf7].
|
||||
*/
|
||||
private static int OFFSET_SHORT_LIST = 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 OFFSET_LONG_LIST = 0xf8;
|
||||
|
||||
|
||||
/* ******************************************************
|
||||
* DECODING *
|
||||
* ******************************************************/
|
||||
|
||||
private static byte decodeOneByteItem(byte[] data, int index) {
|
||||
|
||||
// null item
|
||||
if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM) {
|
||||
return (byte) (data[index] - OFFSET_SHORT_ITEM);
|
||||
|
@ -91,7 +133,7 @@ public class RLP {
|
|||
pow--;
|
||||
}
|
||||
} else {
|
||||
throw new Error("wrong decode attempt");
|
||||
throw new RuntimeException("wrong decode attempt");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
@ -122,7 +164,7 @@ public class RLP {
|
|||
pow--;
|
||||
}
|
||||
} else {
|
||||
throw new Error("wrong decode attempt");
|
||||
throw new RuntimeException("wrong decode attempt");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
@ -143,7 +185,7 @@ public class RLP {
|
|||
value = new String(data, index + 1, length);
|
||||
|
||||
} else {
|
||||
throw new Error("wrong decode attempt");
|
||||
throw new RuntimeException("wrong decode attempt");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
@ -163,7 +205,7 @@ public class RLP {
|
|||
length = (byte) (data[index] - OFFSET_SHORT_ITEM);
|
||||
|
||||
} else {
|
||||
throw new Error("wrong decode attempt");
|
||||
throw new RuntimeException("wrong decode attempt");
|
||||
}
|
||||
byte[] valueBytes = new byte[length];
|
||||
System.arraycopy(data, index, valueBytes, 0, length);
|
||||
|
@ -186,7 +228,7 @@ public class RLP {
|
|||
length = (byte) (data[index] - OFFSET_SHORT_ITEM);
|
||||
|
||||
} else {
|
||||
throw new Error("wrong decode attempt");
|
||||
throw new RuntimeException("wrong decode attempt");
|
||||
}
|
||||
byte[] valueBytes = new byte[length];
|
||||
System.arraycopy(data, index, valueBytes, 0, length);
|
||||
|
@ -209,14 +251,14 @@ public class RLP {
|
|||
length = (byte) (data[index] - OFFSET_SHORT_ITEM);
|
||||
|
||||
} else {
|
||||
throw new Error("wrong decode attempt");
|
||||
throw new RuntimeException("wrong decode attempt");
|
||||
}
|
||||
byte[] valueBytes = new byte[length];
|
||||
System.arraycopy(data, index, valueBytes, 0, length);
|
||||
value = valueBytes;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
private static int nextItemLength(byte[] data, int index) {
|
||||
|
||||
if (index >= data.length)
|
||||
|
@ -278,13 +320,8 @@ public class RLP {
|
|||
offset = offset + 1;
|
||||
byte dByte = decodeOneByteItem(data, index + offset);
|
||||
|
||||
byte[] ip = new byte[4];
|
||||
ip[0] = aByte;
|
||||
ip[1] = bByte;
|
||||
ip[2] = cByte;
|
||||
ip[3] = dByte;
|
||||
|
||||
return ip;
|
||||
// return IP address
|
||||
return new byte[] { aByte, bByte, cByte, dByte } ;
|
||||
}
|
||||
|
||||
public static int getFirstListElement(byte[] payload, int pos) {
|
||||
|
@ -293,22 +330,14 @@ public class RLP {
|
|||
return -1;
|
||||
|
||||
if ((payload[pos] & 0xFF) >= 0xF7) {
|
||||
|
||||
byte lengthOfLength = (byte) (payload[pos] - 0xF7);
|
||||
|
||||
return pos + lengthOfLength + 1;
|
||||
}
|
||||
|
||||
if ((payload[pos] & 0xFF) >= OFFSET_SHORT_LIST && (payload[pos] & 0xFF) < 0xF7) {
|
||||
|
||||
byte length = (byte) ((payload[pos] & 0xFF) - OFFSET_SHORT_LIST);
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
}
|
||||
if ((payload[pos] & 0xFF) >= 0xB7 && (payload[pos] & 0xFF) < OFFSET_SHORT_LIST) {
|
||||
|
||||
byte lengthOfLength = (byte) (payload[pos] - 0xB7);
|
||||
int length = calcLength(lengthOfLength, payload, pos);
|
||||
return pos + lengthOfLength + 1;
|
||||
}
|
||||
|
||||
|
@ -353,11 +382,12 @@ public class RLP {
|
|||
/**
|
||||
* Get exactly one message payload
|
||||
*/
|
||||
public static void fullTraverse(byte[] msgData, int level, int startPos,
|
||||
int endPos, int levelToIndex, Queue<Integer> index) {
|
||||
public static void fullTraverse(byte[] msgData, int level, int startPos,
|
||||
int endPos, int levelToIndex, Queue<Integer> index) {
|
||||
|
||||
try {
|
||||
|
||||
if (msgData == null || msgData.length == 0)
|
||||
if (msgData == null || msgData.length == 0)
|
||||
return;
|
||||
int pos = startPos;
|
||||
|
||||
|
@ -443,7 +473,7 @@ public class RLP {
|
|||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
throw new Error("wire packet not parsed correctly",
|
||||
throw new RuntimeException("wire packet not parsed correctly",
|
||||
th.fillInStackTrace());
|
||||
}
|
||||
}
|
||||
|
@ -467,6 +497,14 @@ public class RLP {
|
|||
}
|
||||
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
|
||||
|
@ -476,15 +514,17 @@ public class RLP {
|
|||
* @param rlpList
|
||||
* - outcome of recursive RLP structure
|
||||
*/
|
||||
public static void parseObjects(byte[] msgData, RLPList rlpList) {
|
||||
RLP.fullTraverse(msgData, 0, 0, msgData.length, 1, rlpList);
|
||||
public static RLPList decode2(byte[] msgData) {
|
||||
RLPList rlpList = new RLPList();
|
||||
fullTraverse(msgData, 0, 0, msgData.length, 1, rlpList);
|
||||
return rlpList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exactly one message payload
|
||||
*/
|
||||
private static void fullTraverse(byte[] msgData, int level, int startPos,
|
||||
int endPos, int levelToIndex, RLPList rlpList) {
|
||||
private static void fullTraverse(byte[] msgData, int level, int startPos,
|
||||
int endPos, int levelToIndex, RLPList rlpList) {
|
||||
|
||||
try {
|
||||
if (msgData == null || msgData.length == 0)
|
||||
|
@ -604,116 +644,128 @@ public class RLP {
|
|||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
throw new Error("wire packet not parsed correctly",
|
||||
th.fillInStackTrace());
|
||||
throw new RuntimeException("wire packet not parsed correctly",
|
||||
th.fillInStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
private static String rlpEncode(Object item) {
|
||||
if (item instanceof String) {
|
||||
String str = ((String) item);
|
||||
int length = str.length();
|
||||
if (length == 1 && str.charAt(0) < OFFSET_SHORT_ITEM)
|
||||
return str;
|
||||
else
|
||||
return encodeLength(str.length(), OFFSET_SHORT_ITEM) + str;
|
||||
} else if (item instanceof List) {
|
||||
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;
|
||||
|
||||
if (data[0] >= 0xF7) {
|
||||
/*
|
||||
* It's a list with a payload more than 55 bytes
|
||||
* data[0] - 0xF7 = how many next bytes allocated for the length of the list
|
||||
*/
|
||||
byte lengthOfLength = (byte) (data[0] - 0xF7);
|
||||
|
||||
byte pow = (byte) (lengthOfLength - 1);
|
||||
long length = 0;
|
||||
for (int i = 1; i <= lengthOfLength; ++i) {
|
||||
length += data[i] << (8 * pow);
|
||||
pow--;
|
||||
}
|
||||
System.out.println(length);
|
||||
// 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;
|
||||
for (int i = 1; i <= lengthOfLength; ++i) {
|
||||
length += data[i] << (8 * pow);
|
||||
pow--;
|
||||
}
|
||||
System.out.println(length);
|
||||
// now we can parse an item for data[1]..data[length]
|
||||
}
|
||||
if (data[0] >= OFFSET_SHORT_ITEM && data[0] < 0xB7) {
|
||||
/*
|
||||
* It's an item less than 55 bytes long,
|
||||
* data[0] - OFFSET_SHORT_ITEM == length of the item
|
||||
*/
|
||||
;
|
||||
}
|
||||
if (data[0] == OFFSET_SHORT_ITEM)
|
||||
/* null item */
|
||||
;
|
||||
if (data[0] < OFFSET_SHORT_ITEM)
|
||||
/* single byte item */
|
||||
;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads any RLP encoded byte-array and returns all objects as byte-array or list of byte-arrays
|
||||
*
|
||||
* @param data RLP encoded byte-array
|
||||
* @param pos position in the array to start reading
|
||||
* @return DecodeResult encapsulates the decoded items as a single Object and the final read position
|
||||
*/
|
||||
public static DecodeResult decode(byte[] data, int pos) {
|
||||
if (data == null || data.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int prefix = data[pos] & 0xFF;
|
||||
if (prefix == OFFSET_SHORT_ITEM) {
|
||||
return new DecodeResult(pos+1, new byte[0]); // means no length or 0
|
||||
} else if (prefix < OFFSET_SHORT_ITEM) {
|
||||
return new DecodeResult(pos+1, new byte[] { data[pos] }); // byte is its own RLP encoding
|
||||
} 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 = 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 < 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 = 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);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/* ******************************************************
|
||||
* ENCODING *
|
||||
* ******************************************************/
|
||||
|
||||
/**
|
||||
* Turn Object into its RLP encoded equivalent of a byte-array
|
||||
* Support for String, Integer, BigInteger and Lists of any of these types.
|
||||
*
|
||||
* @param item as object or List of objects
|
||||
* @return byte[] RLP encoded
|
||||
*/
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] encodeByte(byte singleByte) {
|
||||
|
||||
if ((singleByte & 0xFF) == 0) {
|
||||
|
@ -816,4 +868,26 @@ public class RLP {
|
|||
}
|
||||
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,4 +1,4 @@
|
|||
package org.ethereum.net.rlp;
|
||||
package org.ethereum.util;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
|
@ -1,16 +1,14 @@
|
|||
package org.ethereum.net.rlp;
|
||||
package org.ethereum.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.ethereum.util.Utils;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* User: Roman Mandeleil
|
||||
* Created on: 21/04/14 16:26
|
||||
*/
|
||||
public class RLPList implements RLPElement{
|
||||
public class RLPList implements RLPElement {
|
||||
|
||||
byte[] rlpData;
|
||||
List<RLPElement> list;
|
||||
|
@ -46,7 +44,7 @@ public class RLPList implements RLPElement{
|
|||
public static void recursivePrint(RLPElement element) {
|
||||
|
||||
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) {
|
||||
|
||||
RLPList rlpList = (RLPList) element;
|
|
@ -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) {
|
||||
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() {
|
||||
return RlpEncoder.encode(value);
|
||||
return RLP.encode(value);
|
||||
}
|
||||
|
||||
public boolean cmp(Value o) {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package org.ethereum.block;
|
||||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.net.rlp.RLP;
|
||||
import org.ethereum.net.rlp.RLPList;
|
||||
import org.ethereum.net.vo.Block;
|
||||
import org.ethereum.util.RlpEncoder;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
@ -129,7 +128,7 @@ public class BlockTest {
|
|||
public void testGenesisFromNew() {
|
||||
|
||||
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(CPP_PoC5_GENESIS_HEX_RLP_ENCODED.getBytes(), 0).getDecoded();
|
||||
// TODO: verify genesis items with expected values
|
||||
|
||||
/* From: https://ethereum.etherpad.mozilla.org/11
|
||||
|
@ -183,8 +182,7 @@ public class BlockTest {
|
|||
|
||||
byte[] payload = Hex.decode(blocksMsg);
|
||||
|
||||
RLPList rlpList = new RLPList();
|
||||
RLP.parseObjects(payload, rlpList);
|
||||
RLPList rlpList = RLP.decode2(payload);
|
||||
|
||||
Block blockData = new Block(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