diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Block.java b/ethereumj-core/src/main/java/org/ethereum/core/Block.java index 8611c00c..49f22253 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -1,6 +1,7 @@ package org.ethereum.core; import org.ethereum.crypto.HashUtil; +import org.ethereum.util.RLP; import org.ethereum.util.RLPElement; import org.ethereum.util.RLPItem; import org.ethereum.util.RLPList; @@ -24,8 +25,7 @@ public class Block { /* A scalar value equal to the current limit of gas expenditure per block */ private static int GAS_LIMIT = (int) Math.pow(10, 6); - private RLPList rawData; - private byte[] encodedBlock; + private byte[] rlpEncoded; private boolean parsed = false; private byte[] hash; @@ -72,19 +72,15 @@ public class Block { private List transactionsList = new ArrayList(); private List uncleList = new ArrayList(); - public Block(RLPList rawData) { - this.rawData = rawData; + public Block(byte[] rawData) { + this.rlpEncoded = rawData; this.parsed = false; } - public Block(byte[] encodedBlock) { - this.encodedBlock = encodedBlock; - } - public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase, byte[] stateRoot, byte[] txTrieRoot, byte[] difficulty, - long timestamp, long number, long minGasPrice, long gasLimit, - long gasUsed, byte[] extraData, byte[] nonce, + long number, long minGasPrice, long gasLimit, long gasUsed, + long timestamp, byte[] extraData, byte[] nonce, List transactionsList, List uncleList) { this.parentHash = parentHash; this.unclesHash = unclesHash; @@ -92,11 +88,11 @@ public class Block { this.stateRoot = stateRoot; this.txTrieRoot = txTrieRoot; this.difficulty = difficulty; - this.timestamp = timestamp; this.number = number; this.minGasPrice = minGasPrice; this.gasLimit = gasLimit; this.gasUsed = gasUsed; + this.timestamp = timestamp; this.extraData = extraData; this.nonce = nonce; this.transactionsList = transactionsList; @@ -105,47 +101,49 @@ public class Block { } // [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root, - // difficulty, timestamp, number, minGasPrice, gasLimit, gasUsed, + // difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp, // extradata, nonce] private void parseRLP() { + + RLPList params = (RLPList) RLP.decode2(rlpEncoded); - RLPList params = (RLPList) rawData.get(0); + this.hash = HashUtil.sha3(rlpEncoded); - this.hash = HashUtil.sha3(rawData.getRLPData()); + RLPList block = (RLPList) params.get(0); + RLPList header = (RLPList) block.get(0); + + this.parentHash = ((RLPItem) header.get(0)).getRLPData(); + this.unclesHash = ((RLPItem) header.get(1)).getRLPData(); + this.coinbase = ((RLPItem) header.get(2)).getRLPData(); + this.stateRoot = ((RLPItem) header.get(3)).getRLPData(); + this.txTrieRoot = ((RLPItem) header.get(4)).getRLPData(); + this.difficulty = ((RLPItem) header.get(5)).getRLPData(); - - this.parentHash = ((RLPItem) params.get(0)).getData(); - this.unclesHash = ((RLPItem) params.get(1)).getData(); - this.coinbase = ((RLPItem) params.get(2)).getData(); - this.stateRoot = ((RLPItem) params.get(3)).getData(); - this.txTrieRoot = ((RLPItem) params.get(4)).getData(); - this.difficulty = ((RLPItem) params.get(5)).getData(); - - byte[] tsBytes = ((RLPItem) params.get(6)).getData(); - byte[] nrBytes = ((RLPItem) params.get(7)).getData(); - byte[] gpBytes = ((RLPItem) params.get(8)).getData(); - byte[] glBytes = ((RLPItem) params.get(9)).getData(); - byte[] guBytes = ((RLPItem) params.get(10)).getData(); - - this.timestamp = tsBytes == null ? 0 : (new BigInteger(tsBytes)).longValue(); + byte[] nrBytes = ((RLPItem) header.get(6)).getRLPData(); + byte[] gpBytes = ((RLPItem) header.get(7)).getRLPData(); + byte[] glBytes = ((RLPItem) header.get(8)).getRLPData(); + byte[] guBytes = ((RLPItem) header.get(9)).getRLPData(); + byte[] tsBytes = ((RLPItem) header.get(10)).getRLPData(); + this.number = nrBytes == null ? 0 : (new BigInteger(nrBytes)).longValue(); this.minGasPrice = gpBytes == null ? 0 : (new BigInteger(gpBytes)).longValue(); this.gasLimit = glBytes == null ? 0 : (new BigInteger(glBytes)).longValue(); this.gasUsed = guBytes == null ? 0 : (new BigInteger(guBytes)).longValue(); + this.timestamp = tsBytes == null ? 0 : (new BigInteger(tsBytes)).longValue(); - this.extraData = ((RLPItem) params.get(11)).getData(); - this.nonce = ((RLPItem) params.get(12)).getData(); + this.extraData = ((RLPItem) header.get(11)).getRLPData(); + this.nonce = ((RLPItem) header.get(12)).getRLPData(); // parse transactions - RLPList transactions = (RLPList) rawData.get(1); + RLPList transactions = (RLPList) block.get(1); for (RLPElement rlpTx : transactions){ - Transaction tx = new Transaction((RLPList)rlpTx); + Transaction tx = new Transaction(rlpTx.getRLPData()); this.transactionsList.add(tx); } // parse uncles - RLPList uncleBlocks = (RLPList) rawData.get(2); + RLPList uncleBlocks = (RLPList) block.get(2); for (RLPElement rawUncle : uncleBlocks){ - Block blockData = new Block((RLPList)rawUncle); + Block blockData = new Block(rawUncle.getRLPData()); this.uncleList.add(blockData); } this.parsed = true; @@ -153,7 +151,7 @@ public class Block { public byte[] getHash(){ if (!parsed) parseRLP(); - return hash; + return HashUtil.sha3(this.getEncoded()); } public Block getParent() { @@ -197,18 +195,22 @@ public class Block { } public long getNumber() { + if (!parsed) parseRLP(); return number; } public long getMinGasPrice() { + if (!parsed) parseRLP(); return minGasPrice; } public long getGasLimit() { + if (!parsed) parseRLP(); return gasLimit; } public long getGasUsed() { + if (!parsed) parseRLP(); return gasUsed; } @@ -239,7 +241,7 @@ public class Block { } // [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root, - // difficulty, timestamp, number, minGasPrice, gasLimit, gasUsed, + // difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp, // extradata, nonce] @Override public String toString() { @@ -290,11 +292,39 @@ public class Block { } public byte[] getEncoded() { - if (this.rawData.getRLPData() == null) parseRLP(); - return this.rawData.getRLPData(); - } + if(rlpEncoded == null) { + + // TODO: Alternative clean way to encode, using RLP.encode() after it's optimized + // Object[] header = new Object[] { parentHash, unclesHash, coinbase, + // stateRoot, txTrieRoot, difficulty, number, minGasPrice, + // gasLimit, gasUsed, timestamp, extraData, nonce }; + // Object[] transactions = this.getTransactionsList().toArray(); + // Object[] uncles = this.getUncleList().toArray(); + // return RLP.encode(new Object[] { header, transactions, uncles }); + + byte[] parentHash = RLP.encodeElement(this.parentHash); + byte[] unclesHash = RLP.encodeElement(this.unclesHash); + byte[] coinbase = RLP.encodeElement(this.coinbase); + byte[] stateRoot = RLP.encodeElement(this.stateRoot); + byte[] txTrieRoot = RLP.encodeElement(this.txTrieRoot); + byte[] difficulty = RLP.encodeElement(this.difficulty); + byte[] number = RLP.encodeBigInteger(BigInteger.valueOf(this.number)); + byte[] minGasPrice = RLP.encodeBigInteger(BigInteger.valueOf(this.minGasPrice)); + byte[] gasLimit = RLP.encodeBigInteger(BigInteger.valueOf(this.gasLimit)); + byte[] gasUsed = RLP.encodeBigInteger(BigInteger.valueOf(this.gasUsed)); + byte[] timestamp = RLP.encodeBigInteger(BigInteger.valueOf(this.timestamp)); + byte[] extraData = RLP.encodeElement(this.extraData); + byte[] nonce = RLP.encodeElement(this.nonce); - public byte[] hash() { - return HashUtil.sha3(this.getEncoded()); + byte[] header = RLP.encodeList(parentHash, unclesHash, coinbase, + stateRoot, txTrieRoot, difficulty, number, + minGasPrice, gasLimit, gasUsed, timestamp, extraData, nonce); + + byte[] transactions = RLP.encodeList(); + byte[] uncles = RLP.encodeList(); + + this.rlpEncoded = RLP.encodeList(header, transactions, uncles); + } + return rlpEncoded; } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java b/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java new file mode 100644 index 00000000..2913ba62 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java @@ -0,0 +1,22 @@ +package org.ethereum.core; + +import java.math.BigDecimal; +import java.math.BigInteger; + +public enum Denomination { + + WEI(BigInteger.ONE), + SZABO(BigDecimal.valueOf(Math.pow(10, 12)).toBigInteger()), + FINNY(BigDecimal.valueOf(Math.pow(10, 15)).toBigInteger()), + ETHER(BigDecimal.valueOf(Math.pow(10, 18)).toBigInteger()); + + private BigInteger amount; + + private Denomination(BigInteger value) { + this.amount = value; + } + + public BigInteger getDenomination() { + return amount; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java index a5626699..a1ea5674 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java @@ -1,11 +1,17 @@ package org.ethereum.core; +import java.math.BigInteger; + import org.ethereum.crypto.ECKey.ECDSASignature; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; +import org.ethereum.util.RLP; import org.ethereum.util.RLPItem; import org.ethereum.util.RLPList; import org.ethereum.util.Utils; +import org.spongycastle.util.encoders.Hex; + +import edu.emory.mathcs.backport.java.util.Arrays; /** * A transaction (formally, T ) is a single cryptographically @@ -17,7 +23,7 @@ import org.ethereum.util.Utils; */ public class Transaction { - private RLPList rawData; + private byte[] rlpEncoded; private boolean parsed = false; /* creation contract tx @@ -44,8 +50,7 @@ public class Transaction { * to the state or transaction list consumes some gas. */ private byte[] gasLimit; /* An unlimited size byte array specifying - * either input [data] of the message call - * or the [body] for a new contract */ + * input [data] of the message call */ private byte[] data; /* Initialisation code for a new contract */ private byte[] init; @@ -53,60 +58,72 @@ public class Transaction { * (including public key recovery bits) */ private ECDSASignature signature; - public Transaction(RLPList rawData) { - this.rawData = rawData; + public Transaction(byte[] rawData) { + System.out.println("Transaction created from RLP: " + Hex.toHexString(rawData)); + this.rlpEncoded = rawData; parsed = false; } - public Transaction(byte[] nonce, byte[] value, byte[] recieveAddress, byte[] gasPrice, byte[] gas, byte[] data, byte v, byte[] r, byte[] s) { + public Transaction(byte[] nonce, byte[] value, byte[] recieveAddress, byte[] gasPrice, byte[] gas, byte[] data) { this.nonce = nonce; this.value = value; this.receiveAddress = recieveAddress; this.gasPrice = gasPrice; this.gasLimit = gas; - this.data = data; - this.signature = ECDSASignature.fromComponents(r, s, v); + if(recieveAddress == null) { + this.init = data; + } else { + this.data = data; + } parsed = true; } public void rlpParse(){ - RLPList params = (RLPList) rawData.get(0); + RLPList decodedTxList = RLP.decode2(rlpEncoded); + RLPList transaction = (RLPList) ((RLPList) decodedTxList.get(0)).get(0); - this.hash = HashUtil.sha3(rawData.getRLPData()); - this.nonce = ((RLPItem) params.get(0)).getData(); - this.value = ((RLPItem) params.get(1)).getData(); - this.receiveAddress = ((RLPItem) params.get(2)).getData(); - this.gasPrice = ((RLPItem) params.get(3)).getData(); - this.gasLimit = ((RLPItem) params.get(4)).getData(); - this.data = ((RLPItem) params.get(5)).getData(); + this.hash = HashUtil.sha3(rlpEncoded); + + /* Temporary order for an RLP encoded transaction in cpp client */ + this.nonce = ((RLPItem) transaction.get(0)).getRLPData(); + this.gasPrice = ((RLPItem) transaction.get(1)).getRLPData(); + this.gasLimit = ((RLPItem) transaction.get(2)).getRLPData(); + this.receiveAddress = ((RLPItem) transaction.get(3)).getRLPData(); + this.value = ((RLPItem) transaction.get(4)).getRLPData(); + this.data = ((RLPItem) transaction.get(5)).getRLPData(); + + /* Order of the Yellow Paper / eth-go & pyethereum clients + this.nonce = ((RLPItem) transaction.get(0)).getRLPData(); + this.value = ((RLPItem) transaction.get(1)).getRLPData(); + this.receiveAddress = ((RLPItem) transaction.get(2)).getRLPData(); + this.gasPrice = ((RLPItem) transaction.get(3)).getRLPData(); + this.gasLimit = ((RLPItem) transaction.get(4)).getRLPData(); + this.data = ((RLPItem) transaction.get(5)).getRLPData(); + */ - if (params.size() == 9){ // Simple transaction - byte v = ((RLPItem) params.get(6)).getData()[0]; - byte[] r = ((RLPItem) params.get(7)).getData(); - byte[] s = ((RLPItem) params.get(8)).getData(); + if (transaction.size() == 9){ // Simple transaction + byte v = ((RLPItem) transaction.get(6)).getRLPData()[0]; + byte[] r = ((RLPItem) transaction.get(7)).getRLPData(); + byte[] s = ((RLPItem) transaction.get(8)).getRLPData(); this.signature = ECDSASignature.fromComponents(r, s, v); - } else if (params.size() == 10){ // Contract creation transaction - this.init = ((RLPItem) params.get(6)).getData(); - byte v = ((RLPItem) params.get(7)).getData()[0]; - byte[] r = ((RLPItem) params.get(8)).getData(); - byte[] s = ((RLPItem) params.get(9)).getData(); + } else if (transaction.size() == 10){ // Contract creation transaction + this.init = ((RLPItem) transaction.get(6)).getRLPData(); + byte v = ((RLPItem) transaction.get(7)).getRLPData()[0]; + byte[] r = ((RLPItem) transaction.get(8)).getRLPData(); + byte[] s = ((RLPItem) transaction.get(9)).getRLPData(); this.signature = ECDSASignature.fromComponents(r, s, v); } else throw new RuntimeException("Wrong tx data element list size"); this.parsed = true; } - public RLPList getRawData() { - return rawData; - } - public boolean isParsed() { return parsed; } public byte[] getHash() { if (!parsed) rlpParse(); - return hash; + return HashUtil.sha3(this.getEncoded(false)); } public byte[] getNonce() { @@ -194,4 +211,47 @@ public class Transaction { ", signatureS=" + Utils.toHexString(signature.s.toByteArray()) + ']'; } + + public byte[] getEncoded(boolean signed) { + if(rlpEncoded == null) { + + // TODO: Alternative clean way to encode, using RLP.encode() after it's optimized + // return new Object[] { nonce, value, receiveAddress, gasPrice, + // gasLimit, data, init, signature }; + + /* Temporary order for an RLP encoded transaction in cpp client */ + byte[] nonce = RLP.encodeElement(this.nonce); + byte[] gasPrice = RLP.encodeElement(this.gasPrice); + byte[] gasLimit = RLP.encodeElement(this.gasLimit); + byte[] receiveAddress = RLP.encodeElement(this.receiveAddress); + byte[] value = RLP.encodeElement(this.value); + byte[] data = RLP.encodeElement(this.data); + + if(signed) { +// byte[] signature = RLP.encodeElement(this.signature); + } + + if(Arrays.equals(this.receiveAddress, new byte[0])) { + byte[] init = RLP.encodeElement(this.init); + this.rlpEncoded = RLP.encodeList(nonce, value, receiveAddress, + gasPrice, gasLimit, data, init); + } else { + this.rlpEncoded = RLP.encodeList(nonce, value, receiveAddress, + gasPrice, gasLimit, data); + } + + /* Order of the Yellow Paper / eth-go & pyethereum clients + byte[] nonce = RLP.encodeElement(this.nonce); + byte[] value = RLP.encodeElement(this.value); + byte[] receiveAddress = RLP.encodeElement(this.receiveAddress); + byte[] gasPrice = RLP.encodeElement(this.gasPrice); + byte[] gasLimit = RLP.encodeElement(this.gasLimit); + byte[] data = RLP.encodeElement(this.data); + byte[] init = RLP.encodeElement(this.init); + */ + + + } + return rlpEncoded; + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/crypto/ECKey.java b/ethereumj-core/src/main/java/org/ethereum/crypto/ECKey.java index 5a8b6f7d..369508d6 100644 --- a/ethereumj-core/src/main/java/org/ethereum/crypto/ECKey.java +++ b/ethereumj-core/src/main/java/org/ethereum/crypto/ECKey.java @@ -356,7 +356,7 @@ public class ECKey implements Serializable { * Signs the given hash and returns the R and S components as BigIntegers * and put them in ECDSASignature * - * @param data to sign + * @param rlpData to sign * @return ECDSASignature signature that contains the R and S components */ public ECDSASignature doSign(byte[] input) { diff --git a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java index c1f4a74a..5e40f7fb 100644 --- a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java +++ b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java @@ -76,7 +76,7 @@ public class MainData { if (blockChainDB.isEmpty()) return StaticMessages.GENESSIS_HASH; else - return blockChainDB.get(blockChainDB.size() - 1).hash(); + return blockChainDB.get(blockChainDB.size() - 1).getHash(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java index 2e418a0e..2453a22c 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/BlocksMessage.java @@ -28,13 +28,13 @@ public class BlocksMessage extends Message { RLPList paramsList = (RLPList) rawData.get(0); - if (Command.fromInt(((RLPItem) (paramsList).get(0)).getData()[0]) != BLOCKS) { + if (Command.fromInt(((RLPItem) (paramsList).get(0)).getRLPData()[0]) != BLOCKS) { throw new Error("BlocksMessage: parsing for mal data"); } for (int i = 1; i < paramsList.size(); ++i) { RLPList rlpData = ((RLPList) paramsList.get(i)); - Block blockData = new Block(rlpData); + Block blockData = new Block(rlpData.getRLPData()); this.blockDataList.add(blockData); } parsed = true; diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/DisconnectMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/DisconnectMessage.java index 4155e835..e5318f0d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/DisconnectMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/DisconnectMessage.java @@ -25,11 +25,11 @@ public class DisconnectMessage extends Message { RLPList paramsList = (RLPList) rawData.get(0); - if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0]) != DISCONNECT){ + if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0]) != DISCONNECT){ throw new Error("Disconnect: parsing for mal data"); } - byte[] reasonB = ((RLPItem)paramsList.get(1)).getData(); + byte[] reasonB = ((RLPItem)paramsList.get(1)).getRLPData(); if (reasonB == null){ this.reason = DISCONNECT_REQUESTED; } else { diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/GetChainMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/GetChainMessage.java index 986f2f37..d2f765da 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/GetChainMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/GetChainMessage.java @@ -49,17 +49,17 @@ public class GetChainMessage extends Message { RLPList paramsList = (RLPList) rawData.get(0); - if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0]) != GET_CHAIN){ + if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0]) != GET_CHAIN){ throw new Error("GetChain: parsing for mal data"); } int size = paramsList.size(); for (int i = 1; i < size - 1; ++i){ - blockHashList.add(((RLPItem) paramsList.get(i)).getData()); + blockHashList.add(((RLPItem) paramsList.get(i)).getRLPData()); } // the last element is the num of requested blocks - byte[] blockNumB = ((RLPItem)paramsList.get(size - 1)).getData(); + byte[] blockNumB = ((RLPItem)paramsList.get(size - 1)).getRLPData(); this.blockNum = new BigInteger(blockNumB); this.parsed = true; diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java index 96139fb8..c138006f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java @@ -44,21 +44,21 @@ public class HelloMessage extends Message { // 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 - if (((RLPItem)(paramsList).get(0)).getData() != null){ + if (((RLPItem)(paramsList).get(0)).getRLPData() != null){ throw new Error("HelloMessage: parsing for mal data"); } - this.protocolVersion = ((RLPItem) paramsList.get(1)).getData()[0]; + this.protocolVersion = ((RLPItem) paramsList.get(1)).getRLPData()[0]; - byte[] networkIdBytes = ((RLPItem) paramsList.get(2)).getData(); + byte[] networkIdBytes = ((RLPItem) paramsList.get(2)).getRLPData(); this.networkId = networkIdBytes == null ? 0 : networkIdBytes[0] ; - this.clientId = new String(((RLPItem) paramsList.get(3)).getData()); - this.capabilities = ((RLPItem) paramsList.get(4)).getData()[0]; + this.clientId = new String(((RLPItem) paramsList.get(3)).getRLPData()); + this.capabilities = ((RLPItem) paramsList.get(4)).getRLPData()[0]; - ByteBuffer bb = ByteBuffer.wrap(((RLPItem) paramsList.get(5)).getData()); + ByteBuffer bb = ByteBuffer.wrap(((RLPItem) paramsList.get(5)).getRLPData()); this.peerPort = bb.getShort(); - this.peerId = ((RLPItem) paramsList.get(6)).getData(); + this.peerId = ((RLPItem) paramsList.get(6)).getRLPData(); this.parsed = true; // todo: what to do when mal data ? } diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/NotInChainMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/NotInChainMessage.java index 82b5e47c..b0e34736 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/NotInChainMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/NotInChainMessage.java @@ -25,10 +25,10 @@ public class NotInChainMessage extends Message { public void parseRLP() { RLPList paramsList = (RLPList) rawData.get(0); - if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0] & 0xFF) != NOT_IN_CHAIN){ + if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0] & 0xFF) != NOT_IN_CHAIN){ throw new Error("NotInChain Message: parsing for mal data"); } - hash = ((RLPItem)paramsList.get(1)).getData(); + hash = ((RLPItem)paramsList.get(1)).getRLPData(); } @Override diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/PeersMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/PeersMessage.java index 298d0984..1cb966e3 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/PeersMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/PeersMessage.java @@ -33,15 +33,15 @@ public class PeersMessage extends Message { RLPList paramsList = (RLPList) rawData.get(0); - if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0] & 0xFF) != PEERS){ + if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0] & 0xFF) != PEERS){ throw new Error("PeersMessage: parsing for mal data"); } for (int i = 1; i < paramsList.size(); ++i){ RLPList peerParams = (RLPList)paramsList.get(i); - byte[] ip = ((RLPItem) peerParams.get(0)).getData(); - byte[] shortData = ((RLPItem) peerParams.get(1)).getData(); + byte[] ip = ((RLPItem) peerParams.get(0)).getRLPData(); + byte[] shortData = ((RLPItem) peerParams.get(1)).getRLPData(); short peerPort = 0; if (shortData.length == 1) peerPort = shortData[0]; @@ -49,7 +49,7 @@ public class PeersMessage extends Message { ByteBuffer bb = ByteBuffer.wrap(shortData, 0, shortData.length); peerPort = bb.getShort(); } - byte[] peerId = ((RLPItem) peerParams.get(2)).getData(); + byte[] peerId = ((RLPItem) peerParams.get(2)).getRLPData(); PeerData peer = new PeerData(ip, peerPort, peerId); peers.add(peer); } diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/TransactionsMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/TransactionsMessage.java index 5a05bd62..fb0f5bcb 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/TransactionsMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/TransactionsMessage.java @@ -30,7 +30,7 @@ public class TransactionsMessage extends Message { public void parseRLP() { RLPList paramsList = (RLPList) rawData.get(0); - if (Command.fromInt(((RLPItem)(paramsList).get(0)).getData()[0] & 0xFF) != TRANSACTIONS) { + if (Command.fromInt(((RLPItem)(paramsList).get(0)).getRLPData()[0] & 0xFF) != TRANSACTIONS) { throw new Error("TransactionMessage: parsing for mal data"); } @@ -38,7 +38,7 @@ public class TransactionsMessage extends Message { int size = paramsList.size(); for (int i = 1; i < size; ++i){ RLPList rlpTxData = (RLPList) paramsList.get(i); - Transaction tx = new Transaction(rlpTxData); + Transaction tx = new Transaction(rlpTxData.getRLPData()); transactions.add(tx); } parsed = true; diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java index 0a94942d..e40c455c 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java @@ -789,7 +789,9 @@ public class RLP { public static byte[] encodeElement(byte[] srcData) { - if (srcData.length <= 0x37) { + if (srcData == null){ + return new byte[] { 0x00 }; + } else if (srcData.length <= 0x37) { // length = 8X byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length); byte[] data = Arrays.copyOf(srcData, srcData.length + 1); diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLPElement.java b/ethereumj-core/src/main/java/org/ethereum/util/RLPElement.java index 98b339fa..cc9141a7 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLPElement.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLPElement.java @@ -1,8 +1,11 @@ package org.ethereum.util; +import java.io.Serializable; + /** * Wrapper class for decoded elements from an RLP encoded byte array. */ -public interface RLPElement { +public interface RLPElement extends Serializable { + public byte[] getRLPData(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLPItem.java b/ethereumj-core/src/main/java/org/ethereum/util/RLPItem.java index 29fbbedc..cb9518e7 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLPItem.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLPItem.java @@ -1,24 +1,22 @@ package org.ethereum.util; -import java.io.Serializable; /** * www.ethereumJ.com * User: Roman Mandeleil * Created on: 21/04/14 16:26 */ -public class RLPItem implements RLPElement, Serializable { +public class RLPItem implements RLPElement { - byte[] data; - - public RLPItem(byte[] data) { - this.data = data; + byte[] rlpData; + + public RLPItem(byte[] rlpData) { + this.rlpData = rlpData; } - - public byte[] getData() { - - if (data.length == 0) + + public byte[] getRLPData() { + if (rlpData.length == 0) return null; - return data; + return rlpData; } } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLPList.java b/ethereumj-core/src/main/java/org/ethereum/util/RLPList.java index 54751cda..29f3a1fa 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLPList.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLPList.java @@ -11,7 +11,7 @@ public class RLPList extends ArrayList implements RLPElement { byte[] rlpData; - public void setRLPData(byte[] rlpData){ + public void setRLPData(byte[] rlpData) { this.rlpData = rlpData; } @@ -32,7 +32,7 @@ public class RLPList extends ArrayList implements RLPElement { } System.out.print("]"); } else { - String hex = Utils.toHexString(((RLPItem) element).getData()); + String hex = Utils.toHexString(((RLPItem) element).getRLPData()); System.out.print(hex + ", "); } } diff --git a/ethereumj-core/src/test/java/org/ethereum/block/BlockTest.java b/ethereumj-core/src/test/java/org/ethereum/core/BlockTest.java similarity index 94% rename from ethereumj-core/src/test/java/org/ethereum/block/BlockTest.java rename to ethereumj-core/src/test/java/org/ethereum/core/BlockTest.java index f88eaf33..1b95bc98 100644 --- a/ethereumj-core/src/test/java/org/ethereum/block/BlockTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/core/BlockTest.java @@ -1,19 +1,16 @@ -package org.ethereum.block; +package org.ethereum.core; import org.spongycastle.util.encoders.Hex; import org.ethereum.core.Block; import org.ethereum.core.Genesis; import org.ethereum.crypto.HashUtil; -import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; -import org.ethereum.util.RLPList; import org.junit.Test; import static org.junit.Assert.*; import java.io.IOException; import java.math.BigInteger; -import java.nio.ByteBuffer; public class BlockTest { @@ -123,7 +120,7 @@ public class BlockTest { public void testGenesisFromRLP(){ // from RLP encoding byte[] genesisBytes = Hex.decode(CPP_PoC5_GENESIS_HEX_RLP_ENCODED); - Block genesis = new Block(RLP.decode2(genesisBytes)); + Block genesis = new Block(genesisBytes); assertEquals(CPP_PoC5_GENESIS_HEX_HASH, Hex.toHexString(genesis.getHash())); } @@ -158,11 +155,8 @@ public class BlockTest { String blocksMsg = "F8C8F8C4A07B2536237CBF114A043B0F9B27C76F84AC160EA5B87B53E42C7E76148964D450A01DCC4DE8DEC75D7AAB85B567B6CCD41AD312451B948A7413F0A142FD40D49347943854AAF203BA5F8D49B1EC221329C7AEBCF050D3A07A3BE0EE10ECE4B03097BF74AABAC628AA0FAE617377D30AB1B97376EE31F41AA01DCC4DE8DEC75D7AAB85B567B6CCD41AD312451B948A7413F0A142FD40D49347833FBFE884533F1CE880A0000000000000000000000000000000000000000000000000F3DEEA84969B6E95C0C0"; byte[] payload = Hex.decode(blocksMsg); - - RLPList rlpList = RLP.decode2(payload); - - Block blockData = new Block(rlpList); - RLPList.recursivePrint(rlpList); + Block blockData = new Block(payload); + System.out.println(blockData.toString()); } @Test /* create BlockData from part of real RLP BLOCKS message POC-5 */ @@ -171,15 +165,9 @@ public class BlockTest { String blocksMsg = "F8D1A0085F6A51A63D1FBA43D6E5FE166A47BED64A8B93A99012537D50F3279D4CEA52A01DCC4DE8DEC75D7AAB85B567B6CCD41AD312451B948A7413F0A142FD40D4934794D8758B101609A9F2A881A017BA86CBE6B7F0581DA068472689EA736CFC6B18FCAE9BA7454BADF9C65333A0317DFEFAE1D4AFFF6F90A000000000000000000000000000000000000000000000000000000000000000008401EDF1A18222778609184E72A0008080845373B0B180A0000000000000000000000000000000000000000000000000D1C0D8BC6D744943C0C0"; byte[] payload = Hex.decode(blocksMsg); - - RLPList rlpList = RLP.decode2(payload); - - Block blockData = new Block(rlpList); - + Block blockData = new Block(payload); System.out.println(blockData.toString()); - RLPList.recursivePrint(rlpList); } - } /* diff --git a/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java new file mode 100644 index 00000000..d7810b92 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java @@ -0,0 +1,69 @@ +package org.ethereum.core; + +import static org.junit.Assert.*; + +import java.math.BigInteger; + +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; + +public class TransactionTest { + + private static String RLP_ENCODED_TX = "f88b8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc10000a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4701ca00502e84be138ca397e49f96e8c82e5a99afc09e0ea4582cc109ea221eeb479efa078f18d645b39ec44778c12ffc4b0"; + private static String RLP_ENCODED_TX2 = "f8ccf8a6808609184e72a0008227109491a10664d0cd489085a7a018beb5245d4f2272f180b840000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011ca0c2604bd6eeca76afce4e7775d87960e3d4ed3b69235a3f94d6f1497c9831b50ca0664124a6b323350dd57a650434dc6bf8ddf37cd1a2686fee377e512aa12f1214a0c84f20b2df6abd635babd7af64cd76756cd4e39c0ccb87eaaaad609f21d1fe51820334"; + private static String HASH_RAW_TX = ""; + private static String HASH_SIGNED_TX = ""; + + @Test + public void testTransactionFromRLP() { + // from RLP encoding + + byte[] encodedTxBytes = Hex.decode(RLP_ENCODED_TX2); + Transaction tx = new Transaction(encodedTxBytes); + assertNull(Hex.toHexString(tx.getNonce())); + assertNull(Hex.toHexString(tx.getValue())); + assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getReceiveAddress())); + assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getGasPrice())); + assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getGasLimit())); + assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getData())); + assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getInit())); + assertEquals(28, tx.getSignature().v); + assertEquals("c2604bd6eeca76afce4e7775d87960e3d4ed3b69235a3f94d6f1497c9831b50c", tx.getSignature().r); + assertEquals("664124a6b323350dd57a650434dc6bf8ddf37cd1a2686fee377e512aa12f1214", tx.getSignature().s); + + assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getEncoded(false))); + } + + @Test + public void testTransactionFromNew() throws Exception { + byte[] privKeyBytes = Hex.decode("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4"); + +// nonce=0, gasprice=10 ** 12, startgas=10000, to=, value=10 ** 16, data='').sign(k) +// byte[] nonce = BigInteger.ZERO.toByteArray(); +// byte[] value = Denomination.ETHER.getDenomination().toByteArray(); +// byte[] recieveAddress = Hex.decode("8a40bfaa73256b60764c1bf40675a99083efb075"); +// byte[] gasPrice = Denomination.SZABO.getDenomination().toByteArray(); +// byte[] gas = new BigInteger("10000").toByteArray(); +// byte[] data = new byte[0]; + + + byte[] nonce = null; + byte[] value = null; + byte[] recieveAddress = Hex.decode("91a10664d0cd489085a7a018beb5245d4f2272f1"); + byte[] gasPrice = Hex.decode("09184e72a000"); + byte[] gas = Hex.decode("2710"); + byte[] data = Hex.decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); + + Transaction tx = new Transaction(nonce, value, recieveAddress, gasPrice, gas, data); + byte[] encoded = tx.getEncoded(false); + String test = Hex.toHexString(encoded); + + System.out.println(RLP_ENCODED_TX2); + System.out.println(test); + + assertEquals(RLP_ENCODED_TX2, test); + assertEquals(HASH_RAW_TX, Hex.toHexString(tx.getHash())); + tx.sign(privKeyBytes); + assertEquals(HASH_RAW_TX, Hex.toHexString(tx.getHash())); + } +} diff --git a/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java b/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java index 1f2930f7..e3c3e4b4 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java @@ -1,8 +1,6 @@ package org.ethereum.util; import org.spongycastle.util.encoders.Hex; -import org.ethereum.core.Block; -import org.ethereum.core.Genesis; import org.ethereum.crypto.HashUtil; import org.ethereum.util.Utils; import org.junit.Test; @@ -234,6 +232,13 @@ public class RLPTest { byte[] actuals = RLP.encodeList(); assertArrayEquals(new byte[] { (byte) 0xc0 }, actuals); } + + @Test /** encode list */ + public void testEncodeElementNull(){ + + byte[] actuals = RLP.encodeElement(null); + assertArrayEquals(new byte[] { (byte) 0x00 }, actuals); + } @Test /** found bug encode list affects element value, hhh... not really at the end but keep the test */