Transactions, RLP, and more cool stuff...

This commit is contained in:
romanman 2014-05-20 19:16:07 +03:00
parent 4c1d3e79eb
commit cf82f9b459
15 changed files with 134 additions and 97 deletions

View File

@ -7,6 +7,12 @@
<version>0.1-SNAPSHOT</version> <version>0.1-SNAPSHOT</version>
<name>EthereumJ</name> <name>EthereumJ</name>
<url>http://www.ethereumj.org</url> <url>http://www.ethereumj.org</url>
<!-- * To deploy all the classes and dependencies to one jar,
* that's one option to pack stand alone.
mvn clean package dependency:copy-dependencies
-->
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Third-party dependencies --> <!-- Third-party dependencies -->
@ -114,6 +120,20 @@
<target>1.7</target> <target>1.7</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.ethereum.gui.ToolBar</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -1,7 +1,6 @@
package org.ethereum.core; package org.ethereum.core;
import org.ethereum.crypto.ECKey; import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
@ -16,34 +15,34 @@ import java.util.Arrays;
public class Address { public class Address {
byte[] privKey; byte[] privKey;
byte[] pubKey; byte[] address;
public Address(){ public Address(){
privKey = new BigInteger(130, Utils.getRandom()).toString(32).getBytes(); privKey = new BigInteger(130, Utils.getRandom()).toString(32).getBytes();
this.pubKey = ECKey.fromPrivate(privKey).getAddress(); this.address = ECKey.fromPrivate(privKey).getAddress();
} }
public Address(byte[] privKey) { public Address(byte[] privKey) {
this.privKey = privKey; this.privKey = privKey;
this.pubKey = ECKey.fromPrivate(privKey).getAddress(); this.address = ECKey.fromPrivate(privKey).getAddress();
} }
public Address(byte[] privKey, byte[] pubKey) { public Address(byte[] privKey, byte[] address) {
this.privKey = privKey; this.privKey = privKey;
this.pubKey = pubKey; this.address = address;
} }
public byte[] getPrivKey() { public byte[] getPrivKey() {
return privKey; return privKey;
} }
public byte[] getPubKey() { public byte[] getAddress() {
return pubKey; return address;
} }
@Override @Override
public String toString() { public String toString() {
return Hex.toHexString(pubKey); return Hex.toHexString(address);
} }
@Override @Override
@ -53,13 +52,13 @@ public class Address {
Address address = (Address) o; Address address = (Address) o;
if (!Arrays.equals(pubKey, address.pubKey)) return false; if (!Arrays.equals(this.address, address.address)) return false;
return true; return true;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Arrays.hashCode(pubKey); return Arrays.hashCode(address);
} }
} }

View File

@ -9,6 +9,7 @@ import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem; import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList; import org.ethereum.util.RLPList;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import edu.emory.mathcs.backport.java.util.Arrays; import edu.emory.mathcs.backport.java.util.Arrays;
@ -24,6 +25,8 @@ import edu.emory.mathcs.backport.java.util.Arrays;
public class Transaction { public class Transaction {
private byte[] rlpEncoded; private byte[] rlpEncoded;
private byte[] unsignedRLPEncoded;
private boolean parsed = false; private boolean parsed = false;
/* creation contract tx /* creation contract tx
@ -129,8 +132,11 @@ public class Transaction {
} }
public byte[] getHash() { public byte[] getHash() {
if (!parsed) rlpParse(); if (!parsed) rlpParse();
return HashUtil.sha3(this.getEncoded(false)); byte[] plainMsg = this.getRlpUnsigned();
return HashUtil.sha3(plainMsg);
} }
public byte[] getNonce() { public byte[] getNonce() {
@ -218,71 +224,77 @@ public class Transaction {
", signatureS=" + Utils.toHexString(signature.s.toByteArray()) + ", 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, * For signature games you have to keep also
// gasLimit, data, init, signature }; * rlp of the transaction without any signature data
*/
public byte[] getRlpUnsigned(){
/* Temporary order for an RLP encoded transaction in cpp client */ if (unsignedRLPEncoded != null) return unsignedRLPEncoded;
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);
byte[] v = null; byte[] nonce = RLP.encodeElement(this.nonce);
byte[] r = null; byte[] gasPrice = RLP.encodeElement(this.gasPrice);
byte[] s = null; 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 && signature != null) { if(Arrays.equals(this.receiveAddress, new byte[0])) {
// byte[] signature = RLP.encodeElement(this.signature); byte[] init = RLP.encodeElement(this.init);
this.unsignedRLPEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress,
data, init);
} else {
this.unsignedRLPEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress,
data);
}
v = RLP.encodeByte( signature.v ); return unsignedRLPEncoded;
r = RLP.encodeElement(signature.r.toByteArray()); }
s = RLP.encodeElement(signature.s.toByteArray());
if(Arrays.equals(this.receiveAddress, new byte[0])) { public byte[] getEncoded() {
byte[] init = RLP.encodeElement(this.init);
this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress,
data, init, v, r, s);
} else {
this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress,
data, v, r, s);
}
} else { if(rlpEncoded != null) return rlpEncoded;
byte[] result; /* 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(Arrays.equals(this.receiveAddress, new byte[0])) { byte[] v = null;
byte[] init = RLP.encodeElement(this.init); byte[] r = null;
result = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress, byte[] s = null;
data, init);
} else {
result = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress,
data);
}
return result;
}
/* Order of the Yellow Paper / eth-go & pyethereum clients v = RLP.encodeByte( signature.v );
byte[] nonce = RLP.encodeElement(this.nonce); r = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.r));
byte[] value = RLP.encodeElement(this.value); s = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.s));
byte[] receiveAddress = RLP.encodeElement(this.receiveAddress);
byte[] gasPrice = RLP.encodeElement(this.gasPrice); if(Arrays.equals(this.receiveAddress, new byte[0])) {
byte[] gasLimit = RLP.encodeElement(this.gasLimit); byte[] init = RLP.encodeElement(this.init);
byte[] data = RLP.encodeElement(this.data); this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress,
byte[] init = RLP.encodeElement(this.init); data, init, v, r, s);
*/ } else {
this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, value, receiveAddress,
data, v, r, s);
} }
return rlpEncoded;
/* 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;
} }
} }

View File

@ -8,7 +8,6 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer; import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource; import javax.xml.transform.dom.DOMSource;
@ -197,7 +196,7 @@ public class Wallet {
raw.setAttributeNode(id); raw.setAttributeNode(id);
Element addressE = doc.createElement("address"); Element addressE = doc.createElement("address");
addressE.setTextContent(Hex.toHexString(address.getPubKey())); addressE.setTextContent(Hex.toHexString(address.getAddress()));
Attr nonce = doc.createAttribute("nonce"); Attr nonce = doc.createAttribute("nonce");
nonce.setValue("0"); nonce.setValue("0");

View File

@ -69,11 +69,11 @@ public class ConnectionConsoleWindow extends JFrame implements PeerListener{
Thread t = new Thread() { Thread t = new Thread() {
public void run() { public void run() {
// new ClientPeer(thisConsole).connect("82.217.72.169", 30303); // new ClientPeer(thisConsole).connect("54.201.28.117", 30303); // peer discovery
// new ClientPeer(thisConsole).connect("54.201.28.117", 30303);
// new ClientPeer(thisConsole).connect("82.217.72.169", 30303); // Nick // new ClientPeer(thisConsole).connect("82.217.72.169", 30303); // Nick
new ClientPeer(thisConsole).connect("54.204.10.41", 30303); // new ClientPeer(thisConsole).connect("54.204.10.41", 30303);
new ClientPeer(thisConsole).connect("54.211.14.10", 30303);
// new ClientPeer(thisConsole).connect("192.168.1.102", 30303); // new ClientPeer(thisConsole).connect("192.168.1.102", 30303);
} }
}; };

View File

@ -5,18 +5,14 @@ import org.ethereum.core.Transaction;
import org.ethereum.crypto.HashUtil; import org.ethereum.crypto.HashUtil;
import org.ethereum.manager.MainData; import org.ethereum.manager.MainData;
import org.ethereum.net.client.ClientPeer; import org.ethereum.net.client.ClientPeer;
import org.ethereum.net.message.TransactionsMessage;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
/** /**
* www.ethereumJ.com * www.ethereumJ.com
@ -75,7 +71,7 @@ class PayOutDialog extends JDialog {
byte[] gas = Hex.decode("4255"); byte[] gas = Hex.decode("4255");
Transaction tx = new Transaction(null, value.toByteArray(), Transaction tx = new Transaction(null, value.toByteArray(),
receiveAddress.getPubKey(), gasPrice, gas, null); receiveAddress.getAddress(), gasPrice, gas, null);
try { try {
tx.sign(senderPrivKey); tx.sign(senderPrivKey);

View File

@ -35,7 +35,7 @@ public class WalletAddressPanel extends JPanel{
addressField.setBorder(border); addressField.setBorder(border);
addressField.setEnabled(true); addressField.setEnabled(true);
addressField.setEditable(false); addressField.setEditable(false);
addressField.setText(Hex.toHexString(address.getPubKey()).toUpperCase()); addressField.setText(Hex.toHexString(address.getAddress()).toUpperCase());
addressField.setForeground(new Color(143, 170, 220)); addressField.setForeground(new Color(143, 170, 220));
addressField.setFont(new Font("Monospaced", 0, 12)); addressField.setFont(new Font("Monospaced", 0, 12));
addressField.setPreferredSize(new Dimension(300, 35)); addressField.setPreferredSize(new Dimension(300, 35));

View File

@ -57,8 +57,7 @@ public class MainData {
// if it is the first block to add // if it is the first block to add
// check that the parent is the genesis // check that the parent is the genesis
if (blockChainDB.isEmpty() && if (blockChainDB.isEmpty() &&
!"69a7356a245f9dc5b865475ada5ee4e89b18f93c06503a9db3b3630e88e9fb4e". !Arrays.equals(StaticMessages.GENESSIS_HASH, firstBlockToAdd.getParentHash())){
equals(Hex.toHexString(firstBlockToAdd.getParentHash()))){
return; return;
} }

View File

@ -95,7 +95,7 @@ public class ClientPeer {
*/ */
public void sendTransaction(Transaction transaction){ public void sendTransaction(Transaction transaction){
transaction.getEncoded(true); transaction.getEncoded();
java.util.List<Transaction> txList = new ArrayList<Transaction>(); java.util.List<Transaction> txList = new ArrayList<Transaction>();
txList.add(transaction); txList.add(transaction);
TransactionsMessage transactionsMessage = new TransactionsMessage(txList); TransactionsMessage transactionsMessage = new TransactionsMessage(txList);

View File

@ -50,14 +50,14 @@ public class StaticMessages {
public static final byte[] GET_CHAIN = Hex.decode("2240089100000027F82514A069A7356A245F9DC5B865475ADA5EE4E89B18F93C06503A9DB3B3630E88E9FB4E820100"); public static final byte[] GET_CHAIN = Hex.decode("2240089100000027F82514A069A7356A245F9DC5B865475ADA5EE4E89B18F93C06503A9DB3B3630E88E9FB4E820100");
public static final byte[] GENESSIS_HASH = Hex.decode("69a7356a245f9dc5b865475ada5ee4e89b18f93c06503a9db3b3630e88e9fb4e"); public static final byte[] GENESSIS_HASH = Hex.decode("f5232afe32aba6b366f8aa86a6939437c5e13d1fd71a0f51e77735d3456eb1a6");
public static final byte[] MAGIC_PACKET = Hex.decode("22400891"); public static final byte[] MAGIC_PACKET = Hex.decode("22400891");
static { static {
byte[] peerIdBytes = HashUtil.randomPeerId(); byte[] peerIdBytes = HashUtil.randomPeerId();
HELLO_MESSAGE = new HelloMessage((byte)0x0F, (byte)0x00, "EthereumJ [v0.0.1] pure java [by Roman Mandeleil]", HELLO_MESSAGE = new HelloMessage((byte)0x10, (byte)0x00, "EthereumJ [v0.5.1] pure java by RomanJ",
(byte)0b00000111, (short)30303, peerIdBytes); (byte)0b00000111, (short)30303, peerIdBytes);
/* /*

View File

@ -32,7 +32,7 @@ public class TransactionsMessage extends Message {
for (Transaction tx : transactionList){ for (Transaction tx : transactionList){
byte[] txPayload = tx.getEncoded(true); byte[] txPayload = tx.getEncoded();
try { try {
baos.write(txPayload); baos.write(txPayload);
} catch (IOException e) { } catch (IOException e) {
@ -43,7 +43,7 @@ public class TransactionsMessage extends Message {
byte[][] elements = new byte[transactionList.size() + 1][]; byte[][] elements = new byte[transactionList.size() + 1][];
elements[0] = new byte[]{Command.TRANSACTIONS.asByte()}; elements[0] = new byte[]{Command.TRANSACTIONS.asByte()};
for (int i = 0; i < transactionList.size(); ++i){ for (int i = 0; i < transactionList.size(); ++i){
elements[i + 1] = transactionList.get(i).getEncoded(true); elements[i + 1] = transactionList.get(i).getEncoded();
} }
payload = RLP.encodeList(elements); payload = RLP.encodeList(elements);

View File

@ -98,7 +98,7 @@ public class RLP {
* followed by the concatenation of the RLP encodings of the items. The * followed by the concatenation of the RLP encodings of the items. The
* range of the first byte is thus [0xf8, 0xff]. * range of the first byte is thus [0xf8, 0xff].
*/ */
private static int OFFSET_LONG_LIST = 0xf8; private static int OFFSET_LONG_LIST = 0xf7;
/* ****************************************************** /* ******************************************************

View File

@ -31,7 +31,7 @@ public class TransactionTest {
assertEquals("c2604bd6eeca76afce4e7775d87960e3d4ed3b69235a3f94d6f1497c9831b50c", tx.getSignature().r); assertEquals("c2604bd6eeca76afce4e7775d87960e3d4ed3b69235a3f94d6f1497c9831b50c", tx.getSignature().r);
assertEquals("664124a6b323350dd57a650434dc6bf8ddf37cd1a2686fee377e512aa12f1214", tx.getSignature().s); assertEquals("664124a6b323350dd57a650434dc6bf8ddf37cd1a2686fee377e512aa12f1214", tx.getSignature().s);
assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getEncoded(false))); assertEquals(RLP_ENCODED_TX2, Hex.toHexString(tx.getEncoded()));
} }
@Test @Test
@ -55,7 +55,7 @@ public class TransactionTest {
byte[] data = Hex.decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); byte[] data = Hex.decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
Transaction tx = new Transaction(nonce, value, recieveAddress, gasPrice, gas, data); Transaction tx = new Transaction(nonce, value, recieveAddress, gasPrice, gas, data);
byte[] encoded = tx.getEncoded(false); byte[] encoded = tx.getEncoded();
String test = Hex.toHexString(encoded); String test = Hex.toHexString(encoded);
System.out.println(RLP_ENCODED_TX2); System.out.println(RLP_ENCODED_TX2);

View File

@ -7,7 +7,6 @@ import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import junit.framework.Assert;
import org.ethereum.core.Address; import org.ethereum.core.Address;
import org.ethereum.core.Block; import org.ethereum.core.Block;
import org.ethereum.core.Transaction; import org.ethereum.core.Transaction;
@ -536,10 +535,10 @@ public class MessagesTest {
byte[] gas = Hex.decode("4255"); byte[] gas = Hex.decode("4255");
Transaction tx = new Transaction(null, value.toByteArray(), Transaction tx = new Transaction(null, value.toByteArray(),
receiveAddress.getPubKey(), gasPrice, gas, null); receiveAddress.getAddress(), gasPrice, gas, null);
tx.sign(privKey); tx.sign(privKey);
tx.getEncoded(true); tx.getEncoded();
List<Transaction> txList = new ArrayList<Transaction>(); List<Transaction> txList = new ArrayList<Transaction>();
txList.add(tx); txList.add(tx);

View File

@ -5,13 +5,14 @@ import java.math.BigInteger;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException;
import org.ethereum.core.Address; import org.ethereum.core.Address;
import org.ethereum.core.Transaction; import org.ethereum.core.Transaction;
import org.ethereum.crypto.ECKey; import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil; import org.ethereum.crypto.HashUtil;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
public class TransactionTest { public class TransactionTest {
@ -53,16 +54,28 @@ public class TransactionTest {
byte[] gas = Hex.decode("4255"); byte[] gas = Hex.decode("4255");
Transaction tx = new Transaction(null, value.toByteArray(), Transaction tx = new Transaction(null, value.toByteArray(),
receiveAddress.getPubKey(), gasPrice, gas, null); receiveAddress.getAddress(), gasPrice, gas, null);
tx.sign(senderPrivKey); tx.sign(senderPrivKey);
System.out.println(tx.toString());
ECKey key = ECKey.signatureToKey(HashUtil.sha3(tx.getEncoded(true)), tx.getSignature().toBase64());
System.out.println("r: " + Hex.toHexString(tx.getSignature().r.toByteArray()));
System.out.println("s: " + Hex.toHexString(tx.getSignature().s.toByteArray()));
System.out.println(Hex.toHexString( tx.getEncoded() ));
// retrieve the signer/sender of the transaction
ECKey key = ECKey.signatureToKey(tx.getHash(), tx.getSignature().toBase64());
System.out.println("Tx unsigned RLP: " + Hex.toHexString( tx.getRlpUnsigned() ));
System.out.println("Tx signed RLP: " + Hex.toHexString( tx.getEncoded() ));
System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey())); System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey()));
System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress())); System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress()));
Assert.assertEquals("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826",
Hex.toHexString(key.getAddress()).toUpperCase());
} }
@ -78,10 +91,10 @@ public class TransactionTest {
byte[] gas = Hex.decode("4255"); byte[] gas = Hex.decode("4255");
Transaction tx = new Transaction(null, value.toByteArray(), Transaction tx = new Transaction(null, value.toByteArray(),
receiveAddress.getPubKey(), gasPrice, gas, null); receiveAddress.getAddress(), gasPrice, gas, null);
tx.sign(privKey); tx.sign(privKey);
byte[] payload = tx.getEncoded(true); byte[] payload = tx.getEncoded();
System.out.println(Hex.toHexString( payload )); System.out.println(Hex.toHexString( payload ));
} }