After the first contract creation succeed

This commit is contained in:
romanman 2014-05-26 19:06:55 +03:00
parent 7645262d55
commit 99e141f667
11 changed files with 277 additions and 108 deletions

3
.gitignore vendored
View File

@ -18,4 +18,5 @@ bin
/target
/src/main/java/samples
*.db
*.db
*.xlsx

View File

@ -30,6 +30,7 @@ public class Transaction {
private static final int CALL_SIZE = 9;
private static final int CONTRACT_SIZE = 10;
public static final byte[] ZERO_ADDRESS = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/* creation contract tx
* [ nonce, endowment, 0, gasPrice, gasDeposit (for init), body, init, signature(v, r, s) ]
@ -83,13 +84,14 @@ public class Transaction {
parsed = false;
}
public Transaction(byte[] nonce, byte[] gasPrice, byte[] gasLimit, byte[] recieveAddress, byte[] value, byte[] data) {
public Transaction(byte[] nonce, byte[] gasPrice, byte[] gasLimit, byte[] receiveAddress, byte[] value, byte[] data) {
this.nonce = nonce;
this.gasPrice = gasPrice;
this.gasLimit = gasLimit;
this.receiveAddress = recieveAddress;
this.receiveAddress = receiveAddress;
this.value = value;
if(recieveAddress == null || receiveAddress.length == 0) {
if(receiveAddress == null || receiveAddress.length == 0) {
this.receiveAddress = ZERO_ADDRESS;
this.init = data;
} else {
this.data = data;
@ -107,9 +109,11 @@ public class Transaction {
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();
if (transaction.size() == CALL_SIZE){ // Simple transaction
if (Arrays.equals(this.receiveAddress, ZERO_ADDRESS)){ // Simple transaction
this.init = ((RLPItem) transaction.get(5)).getRLPData();
// only parse signature in case tx is signed
if(((RLPItem) transaction.get(6)).getRLPData() != null) {
byte v = ((RLPItem) transaction.get(6)).getRLPData()[0];
@ -119,18 +123,18 @@ public class Transaction {
} else {
logger.debug("RLP encoded tx is not signed!");
}
} else if (transaction.size() == CONTRACT_SIZE){ // Contract creation transaction
this.init = ((RLPItem) transaction.get(6)).getRLPData();
} else { // Contract creation transaction
this.init = ((RLPItem) transaction.get(5)).getRLPData();
// only parse signature in case tx is signed
if(((RLPItem) transaction.get(6)).getRLPData() != null) {
byte v = ((RLPItem) transaction.get(7)).getRLPData()[0];
byte[] r = ((RLPItem) transaction.get(8)).getRLPData();
byte[] s = ((RLPItem) transaction.get(9)).getRLPData();
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 {
logger.debug("RLP encoded tx is not signed!");
}
} else throw new RuntimeException("Wrong tx data element list size");
}
this.parsed = true;
this.hash = this.getHash();
}
@ -239,6 +243,7 @@ public class Transaction {
*/
public byte[] getEncodedRaw(){
if (!parsed) rlpParse();
if (rlpRaw != null) return rlpRaw;
byte[] nonce = RLP.encodeElement(this.nonce);
@ -248,10 +253,10 @@ public class Transaction {
byte[] value = RLP.encodeElement(this.value);
byte[] data = RLP.encodeElement(this.data);
if(Arrays.equals(this.receiveAddress, new byte[0])) {
if(Arrays.equals(this.receiveAddress, ZERO_ADDRESS)) {
byte[] init = RLP.encodeElement(this.init);
this.rlpRaw = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, value,
data, init);
init);
} else {
this.rlpRaw = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, value,
data);
@ -282,14 +287,16 @@ public class Transaction {
s = RLP.encodeElement(new byte[0]);
}
if(Arrays.equals(this.receiveAddress, new byte[0])) {
if(Arrays.equals(ZERO_ADDRESS, this.receiveAddress)) {
byte[] init = RLP.encodeElement(this.init);
this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, value,
data, init, v, r, s);
init, v, r, s);
} else {
this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, value,
data, v, r, s);
}
return rlpEncoded;
}
}

View File

@ -6,15 +6,10 @@ import org.ethereum.net.client.ClientPeer;
import org.ethereum.util.Utils;
import org.ethereum.wallet.AddressState;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import java.awt.*;
@ -25,26 +20,31 @@ import java.math.BigInteger;
import java.net.URL;
import java.util.Collection;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 18/05/14 22:21
*/
class ContractSubmitDialog extends JDialog {
class ContractSubmitDialog extends JDialog implements MessageAwareDialog{
ContractSubmitDialog dialog;
JComboBox<AddressStateWraper> creatorAddressCombo;
final JTextField gasInput;
private byte[] initByteCode;
AddressState addressState = null;
JLabel statusMsg = null;
public ContractSubmitDialog(Frame parent, String byteCode) {
public ContractSubmitDialog(Frame parent, byte[] byteCode) {
super(parent, "Contract Details: ", false);
dialog = this;
this.initByteCode = byteCode;
this.addressState = addressState;
final JTextField gasInput = new JTextField(5);
gasInput = new JTextField(5);
GUIUtils.addStyle(gasInput, "Gas: ");
JTextArea contractInitTA = new JTextArea();
@ -57,8 +57,12 @@ class ContractSubmitDialog extends JDialog {
contractDataTA.setLineWrap(true);
JScrollPane contractDataInput = new JScrollPane(contractDataTA);
GUIUtils.addStyle(contractDataTA, null, false);
GUIUtils.addStyle(contractDataInput, "Data:");
contractDataTA.setText(byteCode);
GUIUtils.addStyle(contractDataInput, "Body:");
String byteCodeText = GUIUtils.getHexStyledText(byteCode);
contractDataTA.setText(byteCodeText);
contractDataTA.setEditable(false);
contractDataTA.setCaretPosition(0);
this.getContentPane().setBackground(Color.WHITE);
this.getContentPane().setLayout(null);
@ -106,11 +110,11 @@ class ContractSubmitDialog extends JDialog {
this.getContentPane().add(approveLabel);
approveLabel.setVisible(true);
approveLabel.addMouseListener(
new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
submitContract();
}
}
);
@ -118,9 +122,7 @@ class ContractSubmitDialog extends JDialog {
gasInput.setText("1000");
JComboBox jComboBox = new JComboBox(){
JComboBox<AddressStateWraper> creatorAddressCombo = new JComboBox<AddressStateWraper>(){
@Override
public ComboBoxUI getUI() {
@ -129,15 +131,16 @@ class ContractSubmitDialog extends JDialog {
return super.getUI();
}
};
jComboBox.setOpaque(true);
jComboBox.setEnabled(true);
creatorAddressCombo.setOpaque(true);
creatorAddressCombo.setEnabled(true);
jComboBox.setBackground(Color.WHITE);
jComboBox.setFocusable(false);
creatorAddressCombo.setBackground(Color.WHITE);
creatorAddressCombo.setFocusable(false);
this.creatorAddressCombo = creatorAddressCombo;
final Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
// jComboBox.setBorder(line);
JComponent editor = (JComponent)(jComboBox.getEditor().getEditorComponent());
JComponent editor = (JComponent)(creatorAddressCombo.getEditor().getEditorComponent());
editor.setForeground(Color.RED);
Collection<AddressState> addressStates =
@ -145,17 +148,10 @@ class ContractSubmitDialog extends JDialog {
for (AddressState addressState : addressStates){
String addressShort = Utils.getAddressShortString(addressState.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(addressState.getBalance());
String addresText =
String.format(" By: [%s] %s",
addressShort, valueShort);
jComboBox.addItem(addresText);
creatorAddressCombo.addItem(new AddressStateWraper(addressState));
}
jComboBox.setRenderer(new DefaultListCellRenderer() {
creatorAddressCombo.setRenderer(new DefaultListCellRenderer() {
@Override
public void paint(Graphics g) {
@ -168,28 +164,28 @@ class ContractSubmitDialog extends JDialog {
});
jComboBox.setPopupVisible(false);
creatorAddressCombo.setPopupVisible(false);
Object child = jComboBox.getAccessibleContext().getAccessibleChild(0);
Object child = creatorAddressCombo.getAccessibleContext().getAccessibleChild(0);
BasicComboPopup popup = (BasicComboPopup)child;
JList list = popup.getList();
list.setSelectionBackground(Color.cyan);
list.setBorder(null);
for (int i = 0; i < jComboBox.getComponentCount(); i++)
for (int i = 0; i < creatorAddressCombo.getComponentCount(); i++)
{
if (jComboBox.getComponent(i) instanceof CellRendererPane) {
if (creatorAddressCombo.getComponent(i) instanceof CellRendererPane) {
CellRendererPane crp = ((CellRendererPane) (jComboBox.getComponent(i)));
CellRendererPane crp = ((CellRendererPane) (creatorAddressCombo.getComponent(i)));
}
if (jComboBox.getComponent(i) instanceof AbstractButton) {
((AbstractButton) jComboBox.getComponent(i)).setBorder(line);
if (creatorAddressCombo.getComponent(i) instanceof AbstractButton) {
((AbstractButton) creatorAddressCombo.getComponent(i)).setBorder(line);
}
}
jComboBox.setBounds(73, 387, 230, 36);
this.getContentPane().add(jComboBox);
creatorAddressCombo.setBounds(73, 387, 230, 36);
this.getContentPane().add(creatorAddressCombo);
this.getContentPane().revalidate();
@ -236,16 +232,76 @@ class ContractSubmitDialog extends JDialog {
}
public void submitContract(){
ClientPeer peer = MainData.instance.getActivePeer();
if (peer == null) {
dialog.alertStatusMsg("Not connected to any peer");
return;
}
AddressState addressState = ((AddressStateWraper)creatorAddressCombo.getSelectedItem()).getAddressState();
byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes();
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray();
byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
BigInteger gasBI = new BigInteger(gasInput.getText());
byte[] gasValue = BigIntegers.asUnsignedByteArray(gasBI);
byte[] endowment = BigIntegers.asUnsignedByteArray(new BigInteger("1000"));
byte[] zeroAddress = null;
Transaction tx = new Transaction(nonce, gasPrice, gasValue,
zeroAddress, endowment, initByteCode);
try {
tx.sign(senderPrivKey);
} catch (Exception e1) {
dialog.alertStatusMsg("Failed to sign the transaction");
return;
}
// SwingWorker
DialogWorker worker = new DialogWorker(tx, this);
worker.execute();
}
public static void main(String args[]) {
AddressState as = new AddressState();
ContractSubmitDialog pod = new ContractSubmitDialog(null, "");
ContractSubmitDialog pod = new ContractSubmitDialog(null, null);
pod.setVisible(true);
}
public class AddressStateWraper{
private AddressState addressState;
public AddressStateWraper(AddressState addressState) {
this.addressState = addressState;
}
public AddressState getAddressState() {
return addressState;
}
@Override
public String toString() {
String addressShort = Utils.getAddressShortString(addressState.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(addressState.getBalance());
String result =
String.format(" By: [%s] %s",
addressShort, valueShort);
return result;
}
}
}

View File

@ -0,0 +1,58 @@
package org.ethereum.gui;
import org.ethereum.core.Transaction;
import org.ethereum.manager.MainData;
import org.ethereum.net.submit.TransactionExecutor;
import org.ethereum.net.submit.TransactionTask;
import javax.swing.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.ethereum.config.SystemProperties.CONFIG;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 26/05/2014 12:27
*/
public class DialogWorker extends SwingWorker {
Transaction tx;
MessageAwareDialog dialog;
DialogWorker(Transaction tx, MessageAwareDialog dialog) {
this.tx = tx;
this.dialog = dialog;
}
@Override
protected Object doInBackground() throws Exception {
TransactionTask transactionTask = new TransactionTask(tx);
Future future = TransactionExecutor.instance.submitTransaction(transactionTask);
dialog.infoStatusMsg("Transaction sent to the network, waiting for approve");
try {
future.get(CONFIG.transactionApproveTimeout(), TimeUnit.SECONDS);
} catch (TimeoutException e1) {
e1.printStackTrace();
dialog.alertStatusMsg("Transaction wasn't approved, network timeout");
return null;
} catch (InterruptedException e1) {
e1.printStackTrace();
dialog.alertStatusMsg("Transaction wasn't approved");
return null;
} catch (ExecutionException e1) {
e1.printStackTrace();
dialog.alertStatusMsg("Transaction wasn't approved");
return null;
}
dialog.infoStatusMsg("Transaction got approved");
MainData.instance.getWallet().applyTransaction(tx);
return null;
}
}

View File

@ -1,11 +1,14 @@
package org.ethereum.gui;
import org.spongycastle.util.encoders.Hex;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.util.Arrays;
/**
* www.ethereumJ.com
@ -61,5 +64,17 @@ public class GUIUtils {
}
public static String getHexStyledText(byte[] data){
String[] dataHex = Hex.toHexString(data).split("(?<=\\G.{2})");
StringBuffer sb = new StringBuffer();
for (int i = 0; i < dataHex.length; ++i){
sb.append(dataHex[i]).append(" ");
if ((i + 1) % 8 == 0 && i != 0) sb.append("\n");
}
return sb.toString();
}
}

View File

@ -0,0 +1,16 @@
package org.ethereum.gui;
import javax.swing.*;
import java.awt.*;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 26/05/2014 12:29
*/
public interface MessageAwareDialog {
public void infoStatusMsg(final String text);
public void alertStatusMsg(final String text);
}

View File

@ -29,7 +29,7 @@ import static org.ethereum.config.SystemProperties.CONFIG;
* User: Roman Mandeleil
* Created on: 18/05/14 22:21
*/
class PayOutDialog extends JDialog {
class PayOutDialog extends JDialog implements MessageAwareDialog{
PayOutDialog dialog;
@ -112,7 +112,6 @@ class PayOutDialog extends JDialog {
}
byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes();
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray();
// todo: in the future it should be retrieved from the block
@ -129,8 +128,9 @@ class PayOutDialog extends JDialog {
dialog.alertStatusMsg("Failed to sign the transaction");
return;
}
// SwingWorker
DialogWorker worker = new DialogWorker(tx);
DialogWorker worker = new DialogWorker(tx, dialog);
worker.execute();
}
});
@ -198,41 +198,6 @@ class PayOutDialog extends JDialog {
});
}
class DialogWorker extends SwingWorker {
Transaction tx;
DialogWorker(Transaction tx) {
this.tx = tx;
}
@Override
protected Object doInBackground() throws Exception {
TransactionTask transactionTask = new TransactionTask(tx);
Future future = TransactionExecutor.instance.submitTransaction(transactionTask);
dialog.infoStatusMsg("Transaction sent to the network, waiting for approve");
try {
future.get(CONFIG.transactionApproveTimeout(), TimeUnit.SECONDS);
} catch (TimeoutException e1) {
e1.printStackTrace();
dialog.alertStatusMsg("Transaction wasn't approved, network timeout");
return null;
} catch (InterruptedException e1) {
e1.printStackTrace();
dialog.alertStatusMsg("Transaction wasn't approved");
return null;
} catch (ExecutionException e1) {
e1.printStackTrace();
dialog.alertStatusMsg("Transaction wasn't approved");
return null;
}
dialog.infoStatusMsg("Transaction got approved");
MainData.instance.getWallet().applyTransaction(tx);
return null;
}
}
public static void main(String args[]) {
AddressState as = new AddressState();

View File

@ -118,7 +118,8 @@ public class SerpentEditor extends JFrame {
}
String machineCode = SerpentCompiler.compileAssemblyToMachine(asmResult);
byte[] machineCode =
SerpentCompiler.compileAssemblyToMachine(asmResult);
ContractSubmitDialog payOutDialog =
new ContractSubmitDialog((Frame)SwingUtilities.getAncestorOfClass(JFrame.class,

View File

@ -1,11 +1,15 @@
package org.ethereum.serpent;
import io.netty.buffer.ByteBuf;
import org.antlr.v4.runtime.tree.ParseTree;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.OpCode;
import org.spongycastle.util.BigIntegers;
import sun.nio.ByteBuffered;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -31,9 +35,10 @@ public class SerpentCompiler {
}
public static String compileAssemblyToMachine(String code){
public static byte[] compileAssemblyToMachine(String code){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StringBuffer assemblyCode = new StringBuffer();
String[] lexaArr = code.split("\\s+");
List<String> lexaList = new ArrayList<String>();
@ -104,15 +109,13 @@ public class SerpentCompiler {
for (String lexa : lexaList){
if (OpCode.contains(lexa))
assemblyCode.append( OpCode.byteVal(lexa) );
baos.write( OpCode.byteVal(lexa) );
else{
assemblyCode.append( lexa );
baos.write(Byte.parseByte(lexa));
}
assemblyCode.append(" ");
}
return assemblyCode.toString();
return baos.toByteArray();
}

View File

@ -227,4 +227,47 @@ public class TransactionTest {
assertEquals(RLP_TX_SIGNED, encodedSigned);
assertEquals(HASH_TX_UNSIGNED, Hex.toHexString(tx.getHash()));
}
@Test
public void testTransactionCreateContract(){
// String rlp = "f89f808609184e72a0008203e8808203e8b84b4560005444602054600f60056002600a02010b0d630000001d596002602054630000003b5860066000530860056006600202010a0d6300000036596004604054630000003b5860056060541ca0ddc901d83110ea50bc40803f42083afea1bbd420548f6392a679af8e24b21345a06620b3b512bea5f0a272703e8d6933177c23afc79516fd0ca4a204aa6e34c7e9";
byte[] senderPrivKey = HashUtil.sha3("cow".getBytes());
byte[] nonce = BigIntegers.asUnsignedByteArray(BigInteger.ZERO);
byte[] gasPrice = Hex.decode("09184e72a000"); // 10000000000000
byte[] gas = Hex.decode("03e8"); // 1000
byte[] recieveAddress = null;
byte[] endowment = Hex.decode("03e8"); //10000000000000000"
byte[] init = Hex.decode("4560005444602054600f60056002600a02010b0d630000001d596002602054630000003b5860066000530860056006600202010a0d6300000036596004604054630000003b586005606054");
Transaction tx1 = new Transaction(nonce, gasPrice, gas,
recieveAddress, endowment, init);
tx1.sign(senderPrivKey);
byte[] payload = tx1.getEncoded();
System.out.println(Hex.toHexString(payload));
Transaction tx2 = new Transaction(payload);
// tx2.getSender();
String plainTx1 = Hex.toHexString( tx1.getEncodedRaw() );
String plainTx2 = Hex.toHexString( tx2.getEncodedRaw() );
// Transaction tx = new Transaction(Hex.decode(rlp));
System.out.println("tx1.hash: " + Hex.toHexString( tx1.getHash() ));
System.out.println("tx2.hash: " + Hex.toHexString( tx2.getHash() ));
System.out.println();
System.out.println("plainTx1: " + plainTx1 );
System.out.println("plainTx2: " + plainTx2 );
System.out.println( Hex.toHexString( tx2.getSender() ));
}
}

View File

@ -1,6 +1,7 @@
package org.ethereum.serpent;
import org.antlr.v4.runtime.tree.ParseTree;
import org.ethereum.gui.GUIUtils;
import org.ethereum.util.ByteUtil;
import org.junit.Assert;
import org.junit.Test;
@ -1227,9 +1228,12 @@ public class SerpentCompileTest {
String expected = "97 1 0 96 0 84 96 1 96 0 83 11 12 13 99 0 0 0 53 89 96 0 96 2 96 0 83 6 12 13 99 0 0 0 39 89 96 2 96 0 83 4 96 0 84 99 0 0 0 51 88 96 1 96 0 83 96 3 2 1 96 0 84 99 0 0 0 6 88";
String asmResult = SerpentCompiler.compile(code);
String machineCode = SerpentCompiler.compileAssemblyToMachine(asmResult);
byte[] machineCode = SerpentCompiler.compileAssemblyToMachine(asmResult);
Assert.assertEquals(expected, machineCode.trim());
String text = GUIUtils.getHexStyledText(machineCode);
System.out.println(text);
// Assert.assertEquals(expected, machineCode.trim());
}