Add initial Transaction encoding code - fix parseRLP for cpp compatibility
This commit is contained in:
parent
0c9e304b90
commit
a4b919a989
|
@ -119,17 +119,17 @@ public class Block {
|
|||
this.txTrieRoot = ((RLPItem) header.get(4)).getRLPData();
|
||||
this.difficulty = ((RLPItem) header.get(5)).getRLPData();
|
||||
|
||||
byte[] tsBytes = ((RLPItem) header.get(6)).getRLPData();
|
||||
byte[] nrBytes = ((RLPItem) header.get(7)).getRLPData();
|
||||
byte[] gpBytes = ((RLPItem) header.get(8)).getRLPData();
|
||||
byte[] glBytes = ((RLPItem) header.get(9)).getRLPData();
|
||||
byte[] guBytes = ((RLPItem) header.get(10)).getRLPData();
|
||||
|
||||
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) header.get(11)).getRLPData();
|
||||
this.nonce = ((RLPItem) header.get(12)).getRLPData();
|
||||
|
@ -137,7 +137,7 @@ public class Block {
|
|||
// parse transactions
|
||||
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
|
||||
|
@ -195,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)).getRLPData();
|
||||
this.value = ((RLPItem) params.get(1)).getRLPData();
|
||||
this.receiveAddress = ((RLPItem) params.get(2)).getRLPData();
|
||||
this.gasPrice = ((RLPItem) params.get(3)).getRLPData();
|
||||
this.gasLimit = ((RLPItem) params.get(4)).getRLPData();
|
||||
this.data = ((RLPItem) params.get(5)).getRLPData();
|
||||
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)).getRLPData()[0];
|
||||
byte[] r = ((RLPItem) params.get(7)).getRLPData();
|
||||
byte[] s = ((RLPItem) params.get(8)).getRLPData();
|
||||
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)).getRLPData();
|
||||
byte v = ((RLPItem) params.get(7)).getRLPData()[0];
|
||||
byte[] r = ((RLPItem) params.get(8)).getRLPData();
|
||||
byte[] s = ((RLPItem) params.get(9)).getRLPData();
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.ethereum.block;
|
||||
package org.ethereum.core;
|
||||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.ethereum.core.Block;
|
|
@ -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()));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue