diff --git a/.gitignore b/.gitignore index f725d58d1..f816a06a1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ *un~ .DS_Store */**/.DS_Store +./ethereum/ethereum diff --git a/README.md b/README.md index a29bfb6d3..04c0cf78a 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,17 @@ Build ======= For build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)) -Command line options +General command line options ==================== ``` --c Launch the developer console +-c Launch the developer console (node only) -m Start mining blocks -genaddr Generates a new address and private key (destructive action) -p Port on which the server will accept incomming connections (= 30303) -upnp Enable UPnP (= false) -x Desired amount of peers (= 5) -h This help --gui Launch with GUI (= true) -dir Data directory used to store configs and databases (=".ethereum") -import Import a private key (hex) ``` @@ -33,8 +32,9 @@ Developer console commands ========================== ``` -addp : Connect to the given host -tx Send Wei to the specified +addp : Connect to the given host +tx Send Wei to the specified +contract Creates a new contract and launches the editor ``` See the "help" command for *developer* options. @@ -57,7 +57,7 @@ Coding standards Sources should be formatted according to the [Go Formatting Style](http://golang.org/doc/effective_go.html#formatting). -Unless structs fields are supposed to be directly accesible, provide +Unless structs fields are supposed to be directly accessible, provide Getters and hide the fields through Go's exporting facility. When you comment put meaningfull comments. Describe in detail what you diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 7fc7f5447..9093e3ca8 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -158,20 +158,35 @@ ApplicationWindow { anchors.leftMargin: 5 anchors.topMargin: 5 TextField { - id: txAmount - width: 200 - placeholderText: "Amount" + id: txRecipient + placeholderText: "Recipient address (or empty for contract)" + Layout.fillWidth: true } TextField { - id: txReceiver - placeholderText: "Receiver Address (or empty for contract)" - Layout.fillWidth: true + id: txValue + width: 200 + placeholderText: "Amount" + } + TextField { + id: txGas + width: 200 + placeholderText: "Gas" + anchors.left: txValue + anchors.leftMargin: 5 + } + TextField { + id: txGasPrice + width: 200 + placeholderText: "Gas price" + anchors.left: txGas + anchors.leftMargin: 5 } Label { text: "Transaction data" } + TextArea { id: codeView anchors.topMargin: 5 @@ -180,9 +195,11 @@ ApplicationWindow { } Button { + id: txButton text: "Send" onClicked: { - console.log(eth.createTx(txReceiver.text, txAmount.text, codeView.text)) + //this.enabled = false + console.log(eth.createTx(txRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text)) } } } diff --git a/ethereal/config.go b/ethereal/config.go index a534bb182..ac4484d0b 100644 --- a/ethereal/config.go +++ b/ethereal/config.go @@ -16,6 +16,7 @@ var UseSeed bool var ImportKey string var ExportKey bool var DataDir string +var AssetPath string func Init() { flag.BoolVar(&StartConsole, "c", false, "debug and testing console") @@ -29,6 +30,7 @@ func Init() { flag.StringVar(&DataDir, "dir", ".ethereal", "ethereum data directory") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") + flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory") flag.Parse() } diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go index 618d2b00f..99f3b0b52 100644 --- a/ethereal/ethereum.go +++ b/ethereal/ethereum.go @@ -100,11 +100,11 @@ func main() { os.Exit(0) } - log.Printf("Starting Ethereum v%s\n", ethutil.Config.Ver) + log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver) // Set the max peers ethereum.MaxPeers = MaxPeer gui := ethui.New(ethereum) - gui.Start() + gui.Start(AssetPath) } diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go index c8f4bedab..89736ac29 100644 --- a/ethereal/ui/gui.go +++ b/ethereal/ui/gui.go @@ -53,6 +53,7 @@ type Gui struct { txDb *ethdb.LDBDatabase addr []byte + } // Create GUI, but doesn't start it @@ -71,7 +72,7 @@ func New(ethereum *eth.Ethereum) *Gui { return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr} } -func (ui *Gui) Start() { +func (ui *Gui) Start(assetPath string) { defer ui.txDb.Close() // Register ethereum functions @@ -89,14 +90,16 @@ func (ui *Gui) Start() { // Expose the eth library and the ui library to QML context.SetVar("eth", ui.lib) - context.SetVar("ui", &UiLib{engine: ui.engine, eth: ui.eth}) + uiLib := NewUiLib(ui.engine, ui.eth, assetPath) + context.SetVar("ui", uiLib) // Load the main QML interface - component, err := ui.engine.LoadFile(AssetPath("qml/wallet.qml")) + component, err := ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml")) if err != nil { + ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'") panic(err) } - ui.engine.LoadFile(AssetPath("qml/transactions.qml")) + ui.engine.LoadFile(uiLib.AssetPath("qml/transactions.qml")) ui.win = component.CreateWindow(nil) diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go index 05fffd579..6f8cb6f65 100644 --- a/ethereal/ui/library.go +++ b/ethereal/ui/library.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" + "github.com/obscuren/mutan" "strings" ) @@ -14,6 +15,50 @@ type EthLib struct { txPool *ethchain.TxPool } +func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, data string) string { + var hash []byte + var contractCreation bool + if len(recipient) == 0 { + contractCreation = true + } else { + var err error + hash, err = hex.DecodeString(recipient) + if err != nil { + return err.Error() + } + } + + keyPair := ethutil.Config.Db.GetKeys()[0] + value := ethutil.Big(valueStr) + gas := ethutil.Big(gasStr) + gasPrice := ethutil.Big(gasPriceStr) + var tx *ethchain.Transaction + // Compile and assemble the given data + if contractCreation { + asm, err := mutan.Compile(strings.NewReader(data), false) + if err != nil { + return err.Error() + } + + code := ethutil.Assemble(asm...) + tx = ethchain.NewContractCreationTx(value, gasPrice, code) + } else { + tx = ethchain.NewTransactionMessage(hash, value, gasPrice, gas, []string{}) + } + tx.Nonce = lib.stateManager.GetAddrState(keyPair.Address()).Nonce + tx.Sign(keyPair.PrivateKey) + lib.txPool.QueueTransaction(tx) + + if contractCreation { + ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:]) + } else { + ethutil.Config.Log.Infof("Tx hash %x", tx.Hash()) + } + + return ethutil.Hex(tx.Hash()) +} + +/* func (lib *EthLib) CreateTx(receiver, a, data string) string { var hash []byte if len(receiver) == 0 { @@ -31,7 +76,7 @@ func (lib *EthLib) CreateTx(receiver, a, data string) string { amount := ethutil.Big(a) code := ethchain.Compile(strings.Split(data, "\n")) - tx := ethchain.NewTransaction(hash, amount, code) + tx := ethchain.NewTx(hash, amount, code) tx.Nonce = lib.stateManager.GetAddrState(keyPair.Address()).Nonce tx.Sign(keyPair.PrivateKey) @@ -46,6 +91,7 @@ func (lib *EthLib) CreateTx(receiver, a, data string) string { return ethutil.Hex(tx.Hash()) } +*/ func (lib *EthLib) GetBlock(hexHash string) *Block { hash, err := hex.DecodeString(hexHash) diff --git a/ethereal/ui/ui_lib.go b/ethereal/ui/ui_lib.go index edff02286..5c3c98fb9 100644 --- a/ethereal/ui/ui_lib.go +++ b/ethereal/ui/ui_lib.go @@ -16,6 +16,14 @@ type UiLib struct { engine *qml.Engine eth *eth.Ethereum connected bool + assetPath string +} + +func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { + if assetPath == "" { + assetPath = DefaultAssetPath() + } + return &UiLib{engine: engine, eth: eth, assetPath: assetPath} } // Opens a QML file (external application) @@ -45,10 +53,10 @@ func (ui *UiLib) ConnectToPeer(addr string) { } func (ui *UiLib) AssetPath(p string) string { - return AssetPath(p) + return path.Join(ui.assetPath, p) } -func AssetPath(p string) string { +func DefaultAssetPath() string { var base string // If the current working directory is the go-ethereum dir // assume a debug build and use the source directory as @@ -71,5 +79,5 @@ func AssetPath(p string) string { } } - return path.Join(base, p) + return base } diff --git a/ethereum/dev_console.go b/ethereum/dev_console.go index ead4b55e5..421c3fa60 100644 --- a/ethereum/dev_console.go +++ b/ethereum/dev_console.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" + "github.com/obscuren/mutan" _ "math/big" "os" "strings" @@ -52,15 +53,15 @@ func (i *Console) ValidateInput(action string, argumentLength int) error { case action == "gettx" && argumentLength != 1: err = true expArgCount = 1 - case action == "tx" && argumentLength != 2: + case action == "tx" && argumentLength != 4: err = true - expArgCount = 2 + expArgCount = 4 case action == "getaddr" && argumentLength != 1: err = true expArgCount = 1 - case action == "contract" && argumentLength != 1: + case action == "contract" && argumentLength != 2: err = true - expArgCount = 1 + expArgCount = 2 case action == "say" && argumentLength != 1: err = true expArgCount = 1 @@ -79,7 +80,7 @@ func (i *Console) ValidateInput(action string, argumentLength int) error { } } -func (i *Console) Editor() []string { +func (i *Console) Editor() string { var buff bytes.Buffer for { reader := bufio.NewReader(os.Stdin) @@ -94,15 +95,7 @@ func (i *Console) Editor() []string { } } - scanner := bufio.NewScanner(strings.NewReader(buff.String())) - scanner.Split(bufio.ScanLines) - - var lines []string - for scanner.Scan() { - lines = append(lines, scanner.Text()) - } - - return lines + return buff.String() } func (i *Console) PrintRoot() { @@ -178,7 +171,7 @@ func (i *Console) ParseInput(input string) bool { if err != nil { fmt.Println("recipient err:", err) } else { - tx := ethchain.NewTransaction(recipient, ethutil.Big(tokens[2]), []string{""}) + tx := ethchain.NewTransactionMessage(recipient, ethutil.Big(tokens[2]), ethutil.Big(tokens[3]), ethutil.Big(tokens[4]), []string{""}) key := ethutil.Config.Db.GetKeys()[0] tx.Sign(key.PrivateKey) @@ -197,9 +190,15 @@ func (i *Console) ParseInput(input string) bool { } case "contract": fmt.Println("Contract editor (Ctrl-D = done)") - code := ethchain.Compile(i.Editor()) + asm, err := mutan.Compile(strings.NewReader(i.Editor()), false) + if err != nil { + fmt.Println(err) + break + } - contract := ethchain.NewTransaction(ethchain.ContractAddr, ethutil.Big(tokens[1]), code) + code := ethutil.Assemble(asm) + + contract := ethchain.NewContractCreationTx(ethutil.Big(tokens[0]), ethutil.Big(tokens[1]), code) key := ethutil.Config.Db.GetKeys()[0] contract.Sign(key.PrivateKey)