diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 8118b4509c..e0a932f8b4 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -17,8 +17,8 @@
},
{
"ImportPath": "github.com/codegangsta/cli",
- "Comment": "1.2.0-74-g50c77ec",
- "Rev": "50c77ecec0068c9aef9d90ae0fd0fdf410041da3"
+ "Comment": "1.2.0-81-g3e09053",
+ "Rev": "3e0905345cd2c5366530dbcdce62457f2ce16e7c"
},
{
"ImportPath": "github.com/ethereum/ethash",
diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app.go
index 928983ebd3..3e7d5a63ca 100644
--- a/Godeps/_workspace/src/github.com/codegangsta/cli/app.go
+++ b/Godeps/_workspace/src/github.com/codegangsta/cli/app.go
@@ -43,9 +43,11 @@ type App struct {
CommandNotFound func(context *Context, command string)
// Compilation date
Compiled time.Time
- // Author
+ // List of all authors who contributed
+ Authors []Author
+ // Name of Author (Note: Use App.Authors, this is deprecated)
Author string
- // Author e-mail
+ // Email of Author (Note: Use App.Authors, this is deprecated)
Email string
// Writer writer to write output to
Writer io.Writer
@@ -70,14 +72,19 @@ func NewApp() *App {
BashComplete: DefaultAppComplete,
Action: helpCommand.Action,
Compiled: compileTime(),
- Author: "Author",
- Email: "unknown@email",
+ Author: "Dr. James",
+ Email: "who@gmail.com",
+ Authors: []Author{{"Jim", "jim@corporate.com"}, {"Hank", "hank@indiepalace.com"}},
Writer: os.Stdout,
}
}
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
+ if a.Author != "" && a.Author != "" {
+ a.Authors = append(a.Authors, Author{a.Author, a.Email})
+ }
+
if HelpPrinter == nil {
defer func() {
HelpPrinter = nil
@@ -294,3 +301,19 @@ func (a *App) appendFlag(flag Flag) {
a.Flags = append(a.Flags, flag)
}
}
+
+// Author represents someone who has contributed to a cli project.
+type Author struct {
+ Name string // The Authors name
+ Email string // The Authors email
+}
+
+// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
+func (a Author) String() string {
+ e := ""
+ if a.Email != "" {
+ e = "<" + a.Email + "> "
+ }
+
+ return fmt.Sprintf("%v %v", a.Name, e)
+}
diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
index fd2b0e8262..6143d364b5 100644
--- a/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
+++ b/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
@@ -21,6 +21,9 @@ func ExampleApp() {
app.Action = func(c *cli.Context) {
fmt.Printf("Hello %v\n", c.String("name"))
}
+ app.Author = "Harrison"
+ app.Email = "harrison@lolwut.com"
+ app.Authors = []cli.Author{{"Oliver Allen", "oliver@toyshop.com"}}
app.Run(os.Args)
// Output:
// Hello Jeremy
diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/command.go b/Godeps/_workspace/src/github.com/codegangsta/cli/command.go
index 5747e52e8e..07c919a87f 100644
--- a/Godeps/_workspace/src/github.com/codegangsta/cli/command.go
+++ b/Godeps/_workspace/src/github.com/codegangsta/cli/command.go
@@ -119,7 +119,7 @@ func (c Command) Run(ctx *Context) error {
// Returns true if Command.Name or Command.ShortName matches given name
func (c Command) HasName(name string) bool {
- return c.Name == name || c.ShortName == name
+ return c.Name == name || (c.ShortName != "" && c.ShortName == name)
}
func (c Command) startApp(ctx *Context) error {
diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/help.go b/Godeps/_workspace/src/github.com/codegangsta/cli/help.go
index bfb2788519..8d176556a2 100644
--- a/Godeps/_workspace/src/github.com/codegangsta/cli/help.go
+++ b/Godeps/_workspace/src/github.com/codegangsta/cli/help.go
@@ -12,11 +12,10 @@ USAGE:
{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]
VERSION:
- {{.Version}}{{if or .Author .Email}}
+ {{.Version}}
-AUTHOR:{{if .Author}}
- {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}}
- {{.Email}}{{end}}{{end}}
+AUTHOR(S):
+ {{range .Authors}}{{ . }} {{end}}
COMMANDS:
{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
@@ -112,6 +111,12 @@ func DefaultAppComplete(c *Context) {
// Prints help for the given command
func ShowCommandHelp(c *Context, command string) {
+ // show the subcommand help for a command with subcommands
+ if command == "" {
+ HelpPrinter(SubcommandHelpTemplate, c.App)
+ return
+ }
+
for _, c := range c.App.Commands {
if c.HasName(command) {
HelpPrinter(CommandHelpTemplate, c)
diff --git a/accounts/account_manager.go b/accounts/account_manager.go
index 3e9fa77991..646dc8376e 100644
--- a/accounts/account_manager.go
+++ b/accounts/account_manager.go
@@ -33,7 +33,10 @@ and accounts persistence is derived from stored keys' addresses
package accounts
import (
+ "bytes"
+ "crypto/ecdsa"
crand "crypto/rand"
+ "os"
"errors"
"sync"
@@ -42,77 +45,117 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)
-var ErrLocked = errors.New("account is locked; please request passphrase")
+var (
+ ErrLocked = errors.New("account is locked")
+ ErrNoKeys = errors.New("no keys in store")
+)
-// TODO: better name for this struct?
type Account struct {
Address []byte
}
-type AccountManager struct {
- keyStore crypto.KeyStore2
- unlockedKeys map[string]crypto.Key
- unlockMilliseconds time.Duration
- mutex sync.RWMutex
+type Manager struct {
+ keyStore crypto.KeyStore2
+ unlocked map[string]*unlocked
+ mutex sync.RWMutex
}
-func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliseconds time.Duration) AccountManager {
- keysMap := make(map[string]crypto.Key)
- am := &AccountManager{
- keyStore: keyStore,
- unlockedKeys: keysMap,
- unlockMilliseconds: unlockMilliseconds,
+type unlocked struct {
+ *crypto.Key
+ abort chan struct{}
+}
+
+func NewManager(keyStore crypto.KeyStore2) *Manager {
+ return &Manager{
+ keyStore: keyStore,
+ unlocked: make(map[string]*unlocked),
}
- return *am
}
-func (am AccountManager) DeleteAccount(address []byte, auth string) error {
+func (am *Manager) HasAccount(addr []byte) bool {
+ accounts, _ := am.Accounts()
+ for _, acct := range accounts {
+ if bytes.Compare(acct.Address, addr) == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// Coinbase returns the account address that mining rewards are sent to.
+func (am *Manager) Coinbase() (addr []byte, err error) {
+ // TODO: persist coinbase address on disk
+ return am.firstAddr()
+}
+
+func (am *Manager) firstAddr() ([]byte, error) {
+ addrs, err := am.keyStore.GetKeyAddresses()
+ if os.IsNotExist(err) {
+ return nil, ErrNoKeys
+ } else if err != nil {
+ return nil, err
+ }
+ if len(addrs) == 0 {
+ return nil, ErrNoKeys
+ }
+ return addrs[0], nil
+}
+
+func (am *Manager) DeleteAccount(address []byte, auth string) error {
return am.keyStore.DeleteKey(address, auth)
}
-func (am *AccountManager) Sign(fromAccount *Account, toSign []byte) (signature []byte, err error) {
+func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) {
am.mutex.RLock()
- unlockedKey := am.unlockedKeys[string(fromAccount.Address)]
+ unlockedKey, found := am.unlocked[string(a.Address)]
am.mutex.RUnlock()
- if unlockedKey.Address == nil {
+ if !found {
return nil, ErrLocked
}
signature, err = crypto.Sign(toSign, unlockedKey.PrivateKey)
return signature, err
}
-func (am *AccountManager) SignLocked(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) {
- key, err := am.keyStore.GetKey(fromAccount.Address, keyAuth)
+// TimedUnlock unlocks the account with the given address.
+// When timeout has passed, the account will be locked again.
+func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duration) error {
+ key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil {
- return nil, err
+ return err
}
- am.mutex.RLock()
- am.unlockedKeys[string(fromAccount.Address)] = *key
- am.mutex.RUnlock()
- go unlockLater(am, fromAccount.Address)
- signature, err = crypto.Sign(toSign, key.PrivateKey)
- return signature, err
+ u := am.addUnlocked(addr, key)
+ go am.dropLater(addr, u, timeout)
+ return nil
}
-func (am AccountManager) NewAccount(auth string) (*Account, error) {
+// Unlock unlocks the account with the given address. The account
+// stays unlocked until the program exits or until a TimedUnlock
+// timeout (started after the call to Unlock) expires.
+func (am *Manager) Unlock(addr []byte, keyAuth string) error {
+ key, err := am.keyStore.GetKey(addr, keyAuth)
+ if err != nil {
+ return err
+ }
+ am.addUnlocked(addr, key)
+ return nil
+}
+
+func (am *Manager) NewAccount(auth string) (Account, error) {
key, err := am.keyStore.GenerateNewKey(crand.Reader, auth)
if err != nil {
- return nil, err
+ return Account{}, err
}
- ua := &Account{
- Address: key.Address,
- }
- return ua, err
+ return Account{Address: key.Address}, nil
}
-func (am *AccountManager) Accounts() ([]Account, error) {
+func (am *Manager) Accounts() ([]Account, error) {
addresses, err := am.keyStore.GetKeyAddresses()
- if err != nil {
+ if os.IsNotExist(err) {
+ return nil, ErrNoKeys
+ } else if err != nil {
return nil, err
}
-
accounts := make([]Account, len(addresses))
-
for i, addr := range addresses {
accounts[i] = Account{
Address: addr,
@@ -121,12 +164,47 @@ func (am *AccountManager) Accounts() ([]Account, error) {
return accounts, err
}
-func unlockLater(am *AccountManager, addr []byte) {
- select {
- case <-time.After(time.Millisecond * am.unlockMilliseconds):
+func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked {
+ u := &unlocked{Key: key, abort: make(chan struct{})}
+ am.mutex.Lock()
+ prev, found := am.unlocked[string(addr)]
+ if found {
+ // terminate dropLater for this key to avoid unexpected drops.
+ close(prev.abort)
+ // the key is zeroed here instead of in dropLater because
+ // there might not actually be a dropLater running for this
+ // key, i.e. when Unlock was used.
+ zeroKey(prev.PrivateKey)
+ }
+ am.unlocked[string(addr)] = u
+ am.mutex.Unlock()
+ return u
+}
+
+func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) {
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ select {
+ case <-u.abort:
+ // just quit
+ case <-t.C:
+ am.mutex.Lock()
+ // only drop if it's still the same key instance that dropLater
+ // was launched with. we can check that using pointer equality
+ // because the map stores a new pointer every time the key is
+ // unlocked.
+ if am.unlocked[string(addr)] == u {
+ zeroKey(u.PrivateKey)
+ delete(am.unlocked, string(addr))
+ }
+ am.mutex.Unlock()
+ }
+}
+
+// zeroKey zeroes a private key in memory.
+func zeroKey(k *ecdsa.PrivateKey) {
+ b := k.D.Bits()
+ for i := range b {
+ b[i] = 0
}
- am.mutex.RLock()
- // TODO: how do we know the key is actually gone from memory?
- delete(am.unlockedKeys, string(addr))
- am.mutex.RUnlock()
}
diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go
index 44d1d72f17..427114cbde 100644
--- a/accounts/accounts_test.go
+++ b/accounts/accounts_test.go
@@ -1,43 +1,36 @@
package accounts
import (
+ "io/ioutil"
+ "os"
"testing"
+ "time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/randentropy"
- "github.com/ethereum/go-ethereum/ethutil"
- "time"
)
-func TestAccountManager(t *testing.T) {
- ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir() + "/testaccounts")
- am := NewAccountManager(ks, 100)
+func TestSign(t *testing.T) {
+ dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain)
+ defer os.RemoveAll(dir)
+
+ am := NewManager(ks)
pass := "" // not used but required by API
a1, err := am.NewAccount(pass)
toSign := randentropy.GetEntropyCSPRNG(32)
- _, err = am.SignLocked(a1, pass, toSign)
+ am.Unlock(a1.Address, "")
+
+ _, err = am.Sign(a1, toSign)
if err != nil {
t.Fatal(err)
}
-
- // Cleanup
- time.Sleep(time.Millisecond * 150) // wait for locking
-
- accounts, err := am.Accounts()
- if err != nil {
- t.Fatal(err)
- }
- for _, account := range accounts {
- err := am.DeleteAccount(account.Address, pass)
- if err != nil {
- t.Fatal(err)
- }
- }
}
-func TestAccountManagerLocking(t *testing.T) {
- ks := crypto.NewKeyStorePassphrase(ethutil.DefaultDataDir() + "/testaccounts")
- am := NewAccountManager(ks, 200)
+func TestTimedUnlock(t *testing.T) {
+ dir, ks := tmpKeyStore(t, crypto.NewKeyStorePassphrase)
+ defer os.RemoveAll(dir)
+
+ am := NewManager(ks)
pass := "foo"
a1, err := am.NewAccount(pass)
toSign := randentropy.GetEntropyCSPRNG(32)
@@ -45,38 +38,32 @@ func TestAccountManagerLocking(t *testing.T) {
// Signing without passphrase fails because account is locked
_, err = am.Sign(a1, toSign)
if err != ErrLocked {
- t.Fatal(err)
+ t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err)
}
// Signing with passphrase works
- _, err = am.SignLocked(a1, pass, toSign)
- if err != nil {
+ if err = am.TimedUnlock(a1.Address, pass, 100*time.Millisecond); err != nil {
t.Fatal(err)
}
// Signing without passphrase works because account is temp unlocked
_, err = am.Sign(a1, toSign)
if err != nil {
- t.Fatal(err)
+ t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
}
- // Signing without passphrase fails after automatic locking
- time.Sleep(time.Millisecond * time.Duration(250))
-
+ // Signing fails again after automatic locking
+ time.Sleep(150 * time.Millisecond)
_, err = am.Sign(a1, toSign)
if err != ErrLocked {
- t.Fatal(err)
+ t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
}
+}
- // Cleanup
- accounts, err := am.Accounts()
+func tmpKeyStore(t *testing.T, new func(string) crypto.KeyStore2) (string, crypto.KeyStore2) {
+ d, err := ioutil.TempDir("", "eth-keystore-test")
if err != nil {
t.Fatal(err)
}
- for _, account := range accounts {
- err := am.DeleteAccount(account.Address, pass)
- if err != nil {
- t.Fatal(err)
- }
- }
+ return d, new(d)
}
diff --git a/cmd/blocktest/flags.go b/cmd/blocktest/flags.go
deleted file mode 100644
index c811e5b857..0000000000
--- a/cmd/blocktest/flags.go
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- This file is part of go-ethereum
-
- go-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- go-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with go-ethereum. If not, see .
-*/
-/**
- * @authors
- * Gustav Simonsson
- */
-package main
-
-import (
- "flag"
- "fmt"
- "os"
-)
-
-var (
- TestFile string
-)
-
-func Init() {
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "%s \n", os.Args[0])
- flag.PrintDefaults()
- }
- flag.Parse()
-
- TestFile = flag.Arg(0)
-}
diff --git a/cmd/blocktest/main.go b/cmd/blocktest/main.go
index 579aa850ad..ec2779e2ec 100644
--- a/cmd/blocktest/main.go
+++ b/cmd/blocktest/main.go
@@ -25,34 +25,26 @@ package main
import (
"bytes"
- "crypto/ecdsa"
"encoding/hex"
"encoding/json"
+ "flag"
"fmt"
"io/ioutil"
"log"
"math/big"
- "path"
+ "os"
"runtime"
- "strconv"
"strings"
- "time"
"github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/core"
types "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/rlp"
)
-const (
- ClientIdentifier = "Ethereum(G)"
- Version = "0.8.6"
-)
-
type Account struct {
Balance string
Code string
@@ -78,6 +70,7 @@ type BlockHeader struct {
TransactionsTrie string
UncleHash string
}
+
type Tx struct {
Data string
GasLimit string
@@ -103,108 +96,44 @@ type Test struct {
Pre map[string]Account
}
-var (
- Identifier string
- KeyRing string
- DiffTool bool
- DiffType string
- KeyStore string
- StartRpc bool
- StartWebSockets bool
- RpcListenAddress string
- RpcPort int
- WsPort int
- OutboundPort string
- ShowGenesis bool
- AddPeer string
- MaxPeer int
- GenAddr bool
- BootNodes string
- NodeKey *ecdsa.PrivateKey
- NAT nat.Interface
- SecretFile string
- ExportDir string
- NonInteractive bool
- Datadir string
- LogFile string
- ConfigFile string
- DebugFile string
- LogLevel int
- LogFormat string
- Dump bool
- DumpHash string
- DumpNumber int
- VmType int
- ImportChain string
- SHH bool
- Dial bool
- PrintVersion bool
- MinerThreads int
-)
-
-// flags specific to cli client
-var (
- StartMining bool
- StartJsConsole bool
- InputFile string
-)
-
func main() {
- init_vars()
-
- Init()
-
- if len(TestFile) < 1 {
- log.Fatal("Please specify test file")
- }
- blocks, err := loadBlocksFromTestFile(TestFile)
- if err != nil {
- panic(err)
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "%s \n", os.Args[0])
+ flag.PrintDefaults()
}
+ flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU())
+ logger.AddLogSystem(logger.NewStdLogSystem(os.Stderr, log.LstdFlags, logger.DebugDetailLevel))
+ defer func() { logger.Flush() }()
- defer func() {
- logger.Flush()
- }()
+ if len(os.Args) < 2 {
+ utils.Fatalf("Please specify a test file as the first argument.")
+ }
+ blocks, err := loadBlocksFromTestFile(os.Args[1])
+ if err != nil {
+ utils.Fatalf("Could not load blocks: %v", err)
+ }
- //utils.HandleInterrupt()
+ chain := memchain()
+ chain.ResetWithGenesisBlock(blocks[0])
+ if err = chain.InsertChain(types.Blocks{blocks[1]}); err != nil {
+ utils.Fatalf("Error: %v", err)
+ } else {
+ fmt.Println("PASS")
+ }
+}
- utils.InitConfig(VmType, ConfigFile, Datadir, "ethblocktest")
-
- ethereum, err := eth.New(ð.Config{
- Name: p2p.MakeName(ClientIdentifier, Version),
- KeyStore: KeyStore,
- DataDir: Datadir,
- LogFile: LogFile,
- LogLevel: LogLevel,
- LogFormat: LogFormat,
- MaxPeers: MaxPeer,
- Port: OutboundPort,
- NAT: NAT,
- KeyRing: KeyRing,
- Shh: true,
- Dial: Dial,
- BootNodes: BootNodes,
- NodeKey: NodeKey,
- MinerThreads: MinerThreads,
- })
-
- utils.StartEthereumForTest(ethereum)
- utils.StartRpc(ethereum, RpcListenAddress, RpcPort)
-
- ethereum.ChainManager().ResetWithGenesisBlock(blocks[0])
- // bph := ethereum.ChainManager().GetBlock(blocks[1].Header().ParentHash)
- // fmt.Println("bph: ", bph)
-
- //fmt.Println("b0: ", hex.EncodeToString(ethutil.Encode(blocks[0].RlpData())))
- //fmt.Println("b0: ", hex.EncodeToString(blocks[0].Hash()))
- //fmt.Println("b1: ", hex.EncodeToString(ethutil.Encode(blocks[1].RlpData())))
- //fmt.Println("b1: ", hex.EncodeToString(blocks[1].Hash()))
-
- go ethereum.ChainManager().InsertChain(types.Blocks{blocks[1]})
- fmt.Println("OK! ")
- ethereum.WaitForShutdown()
+func memchain() *core.ChainManager {
+ blockdb, err := ethdb.NewMemDatabase()
+ if err != nil {
+ utils.Fatalf("Could not create in-memory database: %v", err)
+ }
+ statedb, err := ethdb.NewMemDatabase()
+ if err != nil {
+ utils.Fatalf("Could not create in-memory database: %v", err)
+ }
+ return core.NewChainManager(blockdb, statedb, new(event.TypeMux))
}
func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) {
@@ -212,9 +141,8 @@ func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) {
if err != nil {
return
}
- bt := *new(map[string]Test)
- err = json.Unmarshal(fileContent, &bt)
- if err != nil {
+ bt := make(map[string]Test)
+ if err = json.Unmarshal(fileContent, &bt); err != nil {
return
}
@@ -280,49 +208,6 @@ func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) {
return
}
-func init_vars() {
- VmType = 0
- Identifier = ""
- KeyRing = ""
- KeyStore = "db"
- RpcListenAddress = "127.0.0.1"
- RpcPort = 8545
- WsPort = 40404
- StartRpc = true
- StartWebSockets = false
- NonInteractive = false
- GenAddr = false
- SecretFile = ""
- ExportDir = ""
- LogFile = ""
-
- timeStr := strconv.FormatInt(time.Now().UnixNano(), 10)
-
- Datadir = path.Join(ethutil.DefaultDataDir(), timeStr)
- ConfigFile = path.Join(ethutil.DefaultDataDir(), timeStr, "conf.ini")
-
- DebugFile = ""
- LogLevel = 5
- LogFormat = "std"
- DiffTool = false
- DiffType = "all"
- ShowGenesis = false
- ImportChain = ""
- Dump = false
- DumpHash = ""
- DumpNumber = -1
- StartMining = false
- StartJsConsole = false
- PrintVersion = false
- MinerThreads = runtime.NumCPU()
-
- Dial = false
- OutboundPort = "30303"
- BootNodes = ""
- MaxPeer = 1
-
-}
-
func hex_decode(s string) (res []byte, err error) {
return hex.DecodeString(strings.TrimPrefix(s, "0x"))
}
diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go
index e3165d3f5c..5432fb9b1b 100644
--- a/cmd/ethereum/js.go
+++ b/cmd/ethereum/js.go
@@ -22,11 +22,9 @@ import (
"fmt"
"io/ioutil"
"os"
- "os/signal"
"path"
"strings"
- "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
@@ -37,51 +35,123 @@ import (
"github.com/peterh/liner"
)
-func execJsFile(ethereum *eth.Ethereum, filename string) {
- file, err := os.Open(filename)
- if err != nil {
- utils.Fatalf("%v", err)
- }
- content, err := ioutil.ReadAll(file)
- if err != nil {
- utils.Fatalf("%v", err)
- }
- re := javascript.NewJSRE(xeth.New(ethereum, nil))
- if _, err := re.Run(string(content)); err != nil {
- utils.Fatalf("Javascript Error: %v", err)
- }
+type prompter interface {
+ AppendHistory(string)
+ Prompt(p string) (string, error)
+ PasswordPrompt(p string) (string, error)
}
-type repl struct {
+type dumbterm struct{ r *bufio.Reader }
+
+func (r dumbterm) Prompt(p string) (string, error) {
+ fmt.Print(p)
+ return r.r.ReadString('\n')
+}
+
+func (r dumbterm) PasswordPrompt(p string) (string, error) {
+ fmt.Println("!! Unsupported terminal, password will echo.")
+ fmt.Print(p)
+ input, err := bufio.NewReader(os.Stdin).ReadString('\n')
+ fmt.Println()
+ return input, err
+}
+
+func (r dumbterm) AppendHistory(string) {}
+
+type jsre struct {
re *javascript.JSRE
ethereum *eth.Ethereum
xeth *xeth.XEth
- prompt string
- lr *liner.State
+ ps1 string
+ atexit func()
+
+ prompter
}
-func runREPL(ethereum *eth.Ethereum) {
- xeth := xeth.New(ethereum, nil)
- repl := &repl{
- re: javascript.NewJSRE(xeth),
- xeth: xeth,
- ethereum: ethereum,
- prompt: "> ",
- }
- repl.initStdFuncs()
+func newJSRE(ethereum *eth.Ethereum) *jsre {
+ js := &jsre{ethereum: ethereum, ps1: "> "}
+ js.xeth = xeth.New(ethereum, js)
+ js.re = javascript.NewJSRE(js.xeth)
+ js.initStdFuncs()
+
if !liner.TerminalSupported() {
- repl.dumbRead()
+ js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
} else {
lr := liner.NewLiner()
- defer lr.Close()
+ js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
lr.SetCtrlCAborts(true)
- repl.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
- repl.read(lr)
- repl.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
+ js.prompter = lr
+ js.atexit = func() {
+ js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
+ lr.Close()
+ }
+ }
+ return js
+}
+
+func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool {
+ p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx)
+ answer, _ := self.Prompt(p)
+ return strings.HasPrefix(strings.Trim(answer, " "), "y")
+}
+
+func (self *jsre) UnlockAccount(addr []byte) bool {
+ fmt.Printf("Please unlock account %x.\n", addr)
+ pass, err := self.PasswordPrompt("Passphrase: ")
+ if err != nil {
+ return false
+ }
+ // TODO: allow retry
+ if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil {
+ return false
+ } else {
+ fmt.Println("Account is now unlocked for this session.")
+ return true
}
}
-func (self *repl) withHistory(op func(*os.File)) {
+func (self *jsre) exec(filename string) error {
+ file, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ content, err := ioutil.ReadAll(file)
+ if err != nil {
+ return err
+ }
+ if _, err := self.re.Run(string(content)); err != nil {
+ return fmt.Errorf("Javascript Error: %v", err)
+ }
+ return nil
+}
+
+func (self *jsre) interactive() {
+ for {
+ input, err := self.Prompt(self.ps1)
+ if err != nil {
+ break
+ }
+ if input == "" {
+ continue
+ }
+ str += input + "\n"
+ self.setIndent()
+ if indentCount <= 0 {
+ if input == "exit" {
+ break
+ }
+ hist := str[:len(str)-1]
+ self.AppendHistory(hist)
+ self.parseInput(str)
+ str = ""
+ }
+ }
+ if self.atexit != nil {
+ self.atexit()
+ }
+}
+
+func (self *jsre) withHistory(op func(*os.File)) {
hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Printf("unable to open history file: %v\n", err)
@@ -91,7 +161,7 @@ func (self *repl) withHistory(op func(*os.File)) {
hist.Close()
}
-func (self *repl) parseInput(code string) {
+func (self *jsre) parseInput(code string) {
defer func() {
if r := recover(); r != nil {
fmt.Println("[native] error", r)
@@ -108,79 +178,21 @@ func (self *repl) parseInput(code string) {
var indentCount = 0
var str = ""
-func (self *repl) setIndent() {
+func (self *jsre) setIndent() {
open := strings.Count(str, "{")
open += strings.Count(str, "(")
closed := strings.Count(str, "}")
closed += strings.Count(str, ")")
indentCount = open - closed
if indentCount <= 0 {
- self.prompt = "> "
+ self.ps1 = "> "
} else {
- self.prompt = strings.Join(make([]string, indentCount*2), "..")
- self.prompt += " "
+ self.ps1 = strings.Join(make([]string, indentCount*2), "..")
+ self.ps1 += " "
}
}
-func (self *repl) read(lr *liner.State) {
- for {
- input, err := lr.Prompt(self.prompt)
- if err != nil {
- return
- }
- if input == "" {
- continue
- }
- str += input + "\n"
- self.setIndent()
- if indentCount <= 0 {
- if input == "exit" {
- return
- }
- hist := str[:len(str)-1]
- lr.AppendHistory(hist)
- self.parseInput(str)
- str = ""
- }
- }
-}
-
-func (self *repl) dumbRead() {
- fmt.Println("Unsupported terminal, line editing will not work.")
-
- // process lines
- readDone := make(chan struct{})
- go func() {
- r := bufio.NewReader(os.Stdin)
- loop:
- for {
- fmt.Print(self.prompt)
- line, err := r.ReadString('\n')
- switch {
- case err != nil || line == "exit":
- break loop
- case line == "":
- continue
- default:
- self.parseInput(line + "\n")
- }
- }
- close(readDone)
- }()
-
- // wait for Ctrl-C
- sigc := make(chan os.Signal, 1)
- signal.Notify(sigc, os.Interrupt, os.Kill)
- defer signal.Stop(sigc)
-
- select {
- case <-readDone:
- case <-sigc:
- os.Stdin.Close() // terminate read
- }
-}
-
-func (self *repl) printValue(v interface{}) {
+func (self *jsre) printValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v)
if err == nil {
@@ -191,7 +203,7 @@ func (self *repl) printValue(v interface{}) {
}
}
-func (self *repl) initStdFuncs() {
+func (self *jsre) initStdFuncs() {
t, _ := self.re.Vm.Get("eth")
eth := t.Object()
eth.Set("connect", self.connect)
@@ -205,7 +217,7 @@ func (self *repl) initStdFuncs() {
* The following methods are natively implemented javascript functions.
*/
-func (self *repl) dump(call otto.FunctionCall) otto.Value {
+func (self *jsre) dump(call otto.FunctionCall) otto.Value {
var block *types.Block
if len(call.ArgumentList) > 0 {
@@ -236,17 +248,17 @@ func (self *repl) dump(call otto.FunctionCall) otto.Value {
return v
}
-func (self *repl) stopMining(call otto.FunctionCall) otto.Value {
+func (self *jsre) stopMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Stop()
return otto.TrueValue()
}
-func (self *repl) startMining(call otto.FunctionCall) otto.Value {
+func (self *jsre) startMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Start()
return otto.TrueValue()
}
-func (self *repl) connect(call otto.FunctionCall) otto.Value {
+func (self *jsre) connect(call otto.FunctionCall) otto.Value {
nodeURL, err := call.Argument(0).ToString()
if err != nil {
return otto.FalseValue()
@@ -257,7 +269,7 @@ func (self *repl) connect(call otto.FunctionCall) otto.Value {
return otto.TrueValue()
}
-func (self *repl) export(call otto.FunctionCall) otto.Value {
+func (self *jsre) export(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
fmt.Println("err: require file name")
return otto.FalseValue()
diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go
index 12e205f375..c9f6201428 100644
--- a/cmd/ethereum/main.go
+++ b/cmd/ethereum/main.go
@@ -21,19 +21,23 @@
package main
import (
+ "bufio"
"fmt"
"os"
"runtime"
"strconv"
+ "strings"
"time"
"github.com/codegangsta/cli"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
+ "github.com/peterh/liner"
)
const (
@@ -43,12 +47,10 @@ const (
var (
clilogger = logger.NewLogger("CLI")
- app = cli.NewApp()
+ app = utils.NewApp(Version, "the go-ethereum command line interface")
)
func init() {
- app.Version = Version
- app.Usage = "the go-ethereum command-line client"
app.Action = run
app.HideVersion = true // we have a command to print the version
app.Commands = []cli.Command{
@@ -60,6 +62,23 @@ func init() {
The output of this command is supposed to be machine-readable.
`,
},
+ {
+ Action: accountList,
+ Name: "account",
+ Usage: "manage accounts",
+ Subcommands: []cli.Command{
+ {
+ Action: accountList,
+ Name: "list",
+ Usage: "print account addresses",
+ },
+ {
+ Action: accountCreate,
+ Name: "new",
+ Usage: "create a new account",
+ },
+ },
+ },
{
Action: dump,
Name: "dump",
@@ -93,13 +112,10 @@ runtime will execute the file and exit.
Usage: `export blockchain into file`,
},
}
- app.Author = ""
- app.Email = ""
app.Flags = []cli.Flag{
+ utils.UnlockedAccountFlag,
utils.BootnodesFlag,
utils.DataDirFlag,
- utils.KeyRingFlag,
- utils.KeyStoreFlag,
utils.ListenPortFlag,
utils.LogFileFlag,
utils.LogFormatFlag,
@@ -140,38 +156,100 @@ func main() {
func run(ctx *cli.Context) {
fmt.Printf("Welcome to the FRONTIER\n")
utils.HandleInterrupt()
- eth := utils.GetEthereum(ClientIdentifier, Version, ctx)
+ eth, err := utils.GetEthereum(ClientIdentifier, Version, ctx)
+ if err == accounts.ErrNoKeys {
+ utils.Fatalf(`No accounts configured.
+Please run 'ethereum account new' to create a new account.`)
+ } else if err != nil {
+ utils.Fatalf("%v", err)
+ }
+
startEth(ctx, eth)
// this blocks the thread
eth.WaitForShutdown()
}
func runjs(ctx *cli.Context) {
- eth := utils.GetEthereum(ClientIdentifier, Version, ctx)
- startEth(ctx, eth)
- if len(ctx.Args()) == 0 {
- runREPL(eth)
- eth.Stop()
- eth.WaitForShutdown()
- } else if len(ctx.Args()) == 1 {
- execJsFile(eth, ctx.Args()[0])
- } else {
- utils.Fatalf("This command can handle at most one argument.")
+ eth, err := utils.GetEthereum(ClientIdentifier, Version, ctx)
+ if err == accounts.ErrNoKeys {
+ utils.Fatalf(`No accounts configured.
+Please run 'ethereum account new' to create a new account.`)
+ } else if err != nil {
+ utils.Fatalf("%v", err)
}
+
+ startEth(ctx, eth)
+ repl := newJSRE(eth)
+ if len(ctx.Args()) == 0 {
+ repl.interactive()
+ } else {
+ for _, file := range ctx.Args() {
+ repl.exec(file)
+ }
+ }
+ eth.Stop()
+ eth.WaitForShutdown()
}
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
utils.StartEthereum(eth)
+
+ // Load startup keys. XXX we are going to need a different format
+ account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
+ if len(account) > 0 {
+ split := strings.Split(account, ":")
+ if len(split) != 2 {
+ utils.Fatalf("Illegal 'unlock' format (address:password)")
+ }
+ am := utils.GetAccountManager(ctx)
+ // Attempt to unlock the account
+ err := am.Unlock(ethutil.Hex2Bytes(split[0]), split[1])
+ if err != nil {
+ utils.Fatalf("Unlock account failed '%v'", err)
+ }
+ }
+ // Start auxiliary services if enabled.
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
- addr := ctx.GlobalString(utils.RPCListenAddrFlag.Name)
- port := ctx.GlobalInt(utils.RPCPortFlag.Name)
- utils.StartRpc(eth, addr, port)
+ utils.StartRPC(eth, ctx)
}
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
eth.Miner().Start()
}
}
+func accountList(ctx *cli.Context) {
+ am := utils.GetAccountManager(ctx)
+ accts, err := am.Accounts()
+ if err != nil {
+ utils.Fatalf("Could not list accounts: %v", err)
+ }
+ for _, acct := range accts {
+ fmt.Printf("Address: %#x\n", acct)
+ }
+}
+
+func accountCreate(ctx *cli.Context) {
+ am := utils.GetAccountManager(ctx)
+ fmt.Println("The new account will be encrypted with a passphrase.")
+ fmt.Println("Please enter a passphrase now.")
+ auth, err := readPassword("Passphrase: ", true)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+ confirm, err := readPassword("Repeat Passphrase: ", false)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+ if auth != confirm {
+ utils.Fatalf("Passphrases did not match.")
+ }
+ acct, err := am.NewAccount(auth)
+ if err != nil {
+ utils.Fatalf("Could not create the account: %v", err)
+ }
+ fmt.Printf("Address: %#x\n", acct.Address)
+}
+
func importchain(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
@@ -221,12 +299,6 @@ func dump(ctx *cli.Context) {
}
}
-// hashish returns true for strings that look like hashes.
-func hashish(x string) bool {
- _, err := strconv.Atoi(x)
- return err != nil
-}
-
func version(c *cli.Context) {
fmt.Printf(`%v
Version: %v
@@ -238,3 +310,24 @@ GOPATH=%s
GOROOT=%s
`, ClientIdentifier, Version, eth.ProtocolVersion, eth.NetworkId, runtime.Version(), runtime.GOOS, os.Getenv("GOPATH"), runtime.GOROOT())
}
+
+// hashish returns true for strings that look like hashes.
+func hashish(x string) bool {
+ _, err := strconv.Atoi(x)
+ return err != nil
+}
+
+func readPassword(prompt string, warnTerm bool) (string, error) {
+ if liner.TerminalSupported() {
+ lr := liner.NewLiner()
+ defer lr.Close()
+ return lr.PasswordPrompt(prompt)
+ }
+ if warnTerm {
+ fmt.Println("!! Unsupported terminal, password will be echoed.")
+ }
+ fmt.Print(prompt)
+ input, err := bufio.NewReader(os.Stdin).ReadString('\n')
+ fmt.Println()
+ return input, err
+}
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index d6a93460e6..960558bb4f 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -59,8 +59,6 @@ func main() {
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))
- ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
-
db, _ := ethdb.NewMemDatabase()
statedb := state.New(nil, db)
sender := statedb.NewStateObject([]byte("sender"))
diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml
index e06ddbd71b..8558ebd51e 100644
--- a/cmd/mist/assets/qml/main.qml
+++ b/cmd/mist/assets/qml/main.qml
@@ -190,6 +190,11 @@ ApplicationWindow {
}
}
+ MenuItem {
+ text: "Generate key"
+ shortcut: "Ctrl+k"
+ onTriggered: gui.generateKey()
+ }
}
Menu {
diff --git a/cmd/mist/assets/qml/views/info.qml b/cmd/mist/assets/qml/views/info.qml
index b2d2f521c9..0187bba6d6 100644
--- a/cmd/mist/assets/qml/views/info.qml
+++ b/cmd/mist/assets/qml/views/info.qml
@@ -54,7 +54,6 @@ Rectangle {
height: 200
anchors {
left: parent.left
- right: logLevelSlider.left
bottom: parent.bottom
top: parent.top
}
@@ -107,46 +106,6 @@ Rectangle {
}
}
}
-
- /*
- TableView {
- id: logView
- headerVisible: false
- anchors {
- right: logLevelSlider.left
- left: parent.left
- bottom: parent.bottom
- top: parent.top
- }
-
- TableViewColumn{ role: "description" ; title: "log" }
-
- model: logModel
- }
- */
-
- Slider {
- id: logLevelSlider
- value: gui.getLogLevelInt()
- anchors {
- right: parent.right
- top: parent.top
- bottom: parent.bottom
-
- rightMargin: 5
- leftMargin: 5
- topMargin: 5
- bottomMargin: 5
- }
-
- orientation: Qt.Vertical
- maximumValue: 5
- stepSize: 1
-
- onValueChanged: {
- gui.setLogLevel(value)
- }
- }
}
property var logModel: ListModel {
diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go
index c63f115918..b56c0dddf5 100644
--- a/cmd/mist/bindings.go
+++ b/cmd/mist/bindings.go
@@ -28,7 +28,6 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
)
@@ -37,19 +36,7 @@ type plugin struct {
Path string `json:"path"`
}
-// LogPrint writes to the GUI log.
-func (gui *Gui) LogPrint(level logger.LogLevel, msg string) {
- /*
- str := strings.TrimRight(s, "\n")
- lines := strings.Split(str, "\n")
-
- view := gui.getObjectByName("infoView")
- for _, line := range lines {
- view.Call("addLog", line)
- }
- */
-}
-func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, error) {
+func (gui *Gui) Transact(from, recipient, value, gas, gasPrice, d string) (string, error) {
var data string
if len(recipient) == 0 {
code, err := ethutil.Compile(d, false)
@@ -61,18 +48,7 @@ func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, err
data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
}
- return gui.xeth.Transact(recipient, value, gas, gasPrice, data)
-}
-
-// functions that allow Gui to implement interface guilogger.LogSystem
-func (gui *Gui) SetLogLevel(level logger.LogLevel) {
- gui.logLevel = level
- gui.eth.Logger().SetLogLevel(level)
- gui.config.Save("loglevel", level)
-}
-
-func (gui *Gui) GetLogLevel() logger.LogLevel {
- return gui.logLevel
+ return gui.xeth.Transact(from, recipient, value, gas, gasPrice, data)
}
func (self *Gui) AddPlugin(pluginPath string) {
@@ -89,11 +65,6 @@ func (self *Gui) RemovePlugin(pluginPath string) {
ethutil.WriteFile(self.eth.DataDir+"/plugins.json", json)
}
-// this extra function needed to give int typecast value to gui widget
-// that sets initial loglevel to default
-func (gui *Gui) GetLogLevelInt() int {
- return int(gui.logLevel)
-}
func (self *Gui) DumpState(hash, path string) {
var stateDump []byte
diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go
deleted file mode 100644
index 139af5923b..0000000000
--- a/cmd/mist/flags.go
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- This file is part of go-ethereum
-
- go-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- go-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with go-ethereum. If not, see .
-*/
-/**
- * @authors
- * Jeffrey Wilcke
- */
-package main
-
-import (
- "crypto/ecdsa"
- "flag"
- "fmt"
- "log"
- "os"
- "path"
- "runtime"
-
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/p2p/nat"
- "github.com/ethereum/go-ethereum/vm"
-)
-
-var (
- Identifier string
- KeyRing string
- KeyStore string
- StartRpc bool
- RpcListenAddress string
- RpcPort int
- OutboundPort string
- ShowGenesis bool
- AddPeer string
- MaxPeer int
- GenAddr bool
- BootNodes string
- NodeKey *ecdsa.PrivateKey
- NAT nat.Interface
- SecretFile string
- ExportDir string
- NonInteractive bool
- Datadir string
- LogFile string
- ConfigFile string
- DebugFile string
- LogLevel int
- VmType int
- MinerThreads int
-)
-
-// flags specific to gui client
-var AssetPath string
-var defaultConfigFile = path.Join(ethutil.DefaultDataDir(), "conf.ini")
-
-func Init() {
- // TODO: move common flag processing to cmd/utils
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "%s [options] [filename]:\noptions precedence: default < config file < environment variables < command line\n", os.Args[0])
- flag.PrintDefaults()
- }
-
- flag.IntVar(&VmType, "vm", 0, "Virtual Machine type: 0-1: standard, debug")
- flag.StringVar(&Identifier, "id", "", "Custom client identifier")
- flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
- flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file")
- flag.StringVar(&RpcListenAddress, "rpcaddr", "127.0.0.1", "address for json-rpc server to listen on")
- flag.IntVar(&RpcPort, "rpcport", 8545, "port to start json-rpc server on")
- flag.BoolVar(&StartRpc, "rpc", true, "start rpc server")
- flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
- flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
- flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
- flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
- flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
- flag.StringVar(&Datadir, "datadir", ethutil.DefaultDataDir(), "specifies the datadir to use")
- flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
- flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
- flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5 (= silent,error,warn,info,debug,debug detail)")
-
- flag.StringVar(&AssetPath, "asset_path", ethutil.DefaultAssetPath(), "absolute path to GUI assets directory")
-
- // Network stuff
- var (
- nodeKeyFile = flag.String("nodekey", "", "network private key file")
- nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)")
- natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:)")
- )
- flag.StringVar(&OutboundPort, "port", "30303", "listening port")
- flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
- flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
-
- flag.IntVar(&MinerThreads, "minerthreads", runtime.NumCPU(), "number of miner threads")
-
- flag.Parse()
-
- var err error
- if NAT, err = nat.Parse(*natstr); err != nil {
- log.Fatalf("-nat: %v", err)
- }
- switch {
- case *nodeKeyFile != "" && *nodeKeyHex != "":
- log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
- case *nodeKeyFile != "":
- if NodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil {
- log.Fatalf("-nodekey: %v", err)
- }
- case *nodeKeyHex != "":
- if NodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil {
- log.Fatalf("-nodekeyhex: %v", err)
- }
- }
-
- if VmType >= int(vm.MaxVmTy) {
- log.Fatal("Invalid VM type ", VmType)
- }
-}
diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go
index 53ca35574e..a49e9f6f82 100644
--- a/cmd/mist/gui.go
+++ b/cmd/mist/gui.go
@@ -23,7 +23,6 @@ package main
import "C"
import (
- "bytes"
"encoding/json"
"fmt"
"io/ioutil"
@@ -70,20 +69,18 @@ type Gui struct {
txDb *ethdb.LDBDatabase
- logLevel logger.LogLevel
- open bool
+ open bool
xeth *xeth.XEth
Session string
- config *ethutil.ConfigManager
plugins map[string]plugin
}
// Create GUI, but doesn't start it
-func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, session string, logLevel int) *Gui {
- db, err := ethdb.NewLDBDatabase("tx_database")
+func NewWindow(ethereum *eth.Ethereum) *Gui {
+ db, err := ethdb.NewLDBDatabase(path.Join(ethereum.DataDir, "tx_database"))
if err != nil {
panic(err)
}
@@ -92,10 +89,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, session st
gui := &Gui{eth: ethereum,
txDb: db,
xeth: xeth,
- logLevel: logger.LogLevel(logLevel),
- Session: session,
open: false,
- config: config,
plugins: make(map[string]plugin),
serviceEvents: make(chan ServEv, 1),
}
@@ -142,18 +136,12 @@ func (gui *Gui) Start(assetPath string) {
gui.open = true
win.Show()
- // only add the gui guilogger after window is shown otherwise slider wont be shown
- logger.AddLogSystem(gui)
win.Wait()
-
- // need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel)
- gui.logLevel = logger.Silence
gui.open = false
}
func (gui *Gui) Stop() {
if gui.open {
- gui.logLevel = logger.Silence
gui.open = false
gui.win.Hide()
}
@@ -172,7 +160,11 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
return gui.win, nil
}
-func (gui *Gui) ImportKey(filePath string) {
+func (gui *Gui) GenerateKey() {
+ _, err := gui.eth.AccountManager().NewAccount("hurr")
+ if err != nil {
+ // TODO: UI feedback?
+ }
}
func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
@@ -191,31 +183,11 @@ func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
return gui.win
}
-func (gui *Gui) ImportAndSetPrivKey(secret string) bool {
- err := gui.eth.KeyManager().InitFromString(gui.Session, 0, secret)
- if err != nil {
- guilogger.Errorln("unable to import: ", err)
- return false
- }
- guilogger.Errorln("successfully imported: ", err)
- return true
-}
-
-func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) {
- err := gui.eth.KeyManager().Init(gui.Session, 0, true)
- if err != nil {
- guilogger.Errorln("unable to create key: ", err)
- return "", "", "", ""
- }
- return gui.eth.KeyManager().KeyPair().AsStrings()
-}
-
func (gui *Gui) setInitialChain(ancientBlocks bool) {
sBlk := gui.eth.ChainManager().LastBlockHash()
blk := gui.eth.ChainManager().GetBlock(sBlk)
for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) {
sBlk = blk.ParentHash()
-
gui.processBlock(blk, true)
}
}
@@ -259,10 +231,8 @@ func (self *Gui) loadMergedMiningOptions() {
}
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
- addr := gui.address()
-
var inout string
- if bytes.Compare(tx.From(), addr) == 0 {
+ if gui.eth.AccountManager().HasAccount(tx.From()) {
inout = "send"
} else {
inout = "recv"
@@ -480,14 +450,6 @@ func (gui *Gui) setPeerInfo() {
}
}
-func (gui *Gui) privateKey() string {
- return ethutil.Bytes2Hex(gui.eth.KeyManager().PrivateKey())
-}
-
-func (gui *Gui) address() []byte {
- return gui.eth.KeyManager().Address()
-}
-
/*
func LoadExtension(path string) (uintptr, error) {
lib, err := ffi.NewLibrary(path)
diff --git a/cmd/mist/main.go b/cmd/mist/main.go
index 7d78c9c025..9a773e33a8 100644
--- a/cmd/mist/main.go
+++ b/cmd/mist/main.go
@@ -26,10 +26,10 @@ import (
"runtime"
"time"
+ "github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/ui/qt/webengine"
"github.com/obscuren/qml"
)
@@ -39,56 +39,32 @@ const (
Version = "0.9.0"
)
-var ethereum *eth.Ethereum
-var mainlogger = logger.NewLogger("MAIN")
-
-func run() error {
- webengine.Initialize()
-
- // precedence: code-internal flag default < config file < environment variables < command line
- Init() // parsing command line
-
- tstart := time.Now()
- config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
-
- ethereum, err := eth.New(ð.Config{
- Name: p2p.MakeName(ClientIdentifier, Version),
- KeyStore: KeyStore,
- DataDir: Datadir,
- LogFile: LogFile,
- LogLevel: LogLevel,
- MaxPeers: MaxPeer,
- Port: OutboundPort,
- NAT: NAT,
- Shh: true,
- BootNodes: BootNodes,
- NodeKey: NodeKey,
- KeyRing: KeyRing,
- Dial: true,
- MinerThreads: MinerThreads,
- })
- if err != nil {
- mainlogger.Fatalln(err)
+var (
+ app = utils.NewApp(Version, "the ether browser")
+ assetPathFlag = cli.StringFlag{
+ Name: "asset_path",
+ Usage: "absolute path to GUI assets directory",
+ Value: ethutil.DefaultAssetPath(),
}
- utils.KeyTasks(ethereum.KeyManager(), KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
+)
- if StartRpc {
- utils.StartRpc(ethereum, RpcListenAddress, RpcPort)
+func init() {
+ app.Action = run
+ app.Flags = []cli.Flag{
+ assetPathFlag,
+
+ utils.BootnodesFlag,
+ utils.DataDirFlag,
+ utils.ListenPortFlag,
+ utils.LogFileFlag,
+ utils.LogLevelFlag,
+ utils.MaxPeersFlag,
+ utils.MinerThreadsFlag,
+ utils.NATFlag,
+ utils.NodeKeyFileFlag,
+ utils.RPCListenAddrFlag,
+ utils.RPCPortFlag,
}
-
- gui := NewWindow(ethereum, config, KeyRing, LogLevel)
-
- utils.RegisterInterrupt(func(os.Signal) {
- gui.Stop()
- })
- go utils.StartEthereum(ethereum)
-
- fmt.Println("ETH stack took", time.Since(tstart))
-
- // gui blocks the main thread
- gui.Start(AssetPath)
-
- return nil
}
func main() {
@@ -97,15 +73,16 @@ func main() {
// This is a bit of a cheat, but ey!
os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999")
- qml.Run(run)
-
var interrupted = false
utils.RegisterInterrupt(func(os.Signal) {
interrupted = true
})
-
utils.HandleInterrupt()
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintln(os.Stderr, "Error: ", err)
+ }
+
// we need to run the interrupt callbacks in case gui is closed
// this skips if we got here by actual interrupt stopping the GUI
if !interrupted {
@@ -113,3 +90,26 @@ func main() {
}
logger.Flush()
}
+
+func run(ctx *cli.Context) {
+ tstart := time.Now()
+
+ // TODO: show qml popup instead of exiting if initialization fails.
+ ethereum, err := utils.GetEthereum(ClientIdentifier, Version, ctx)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+ utils.StartRPC(ethereum, ctx)
+ go utils.StartEthereum(ethereum)
+ fmt.Println("initializing eth stack took", time.Since(tstart))
+
+ // Open the window
+ qml.Run(func() error {
+ webengine.Initialize()
+ gui := NewWindow(ethereum)
+ utils.RegisterInterrupt(func(os.Signal) { gui.Stop() })
+ // gui blocks the main thread
+ gui.Start(ctx.GlobalString(assetPathFlag.Name))
+ return nil
+ })
+}
diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go
index b202432c41..4198c6316d 100644
--- a/cmd/mist/ui_lib.go
+++ b/cmd/mist/ui_lib.go
@@ -128,6 +128,7 @@ func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
object := mapToTxParams(params)
return self.XEth.Transact(
+ object["from"],
object["to"],
object["value"],
object["gas"],
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index a7e609af77..6b99b841f9 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -29,13 +29,10 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/rlp"
- rpchttp "github.com/ethereum/go-ethereum/rpc/http"
- "github.com/ethereum/go-ethereum/xeth"
)
var clilogger = logger.NewLogger("CLI")
@@ -97,18 +94,10 @@ func initDataDir(Datadir string) {
}
}
-func InitConfig(vmType int, ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager {
- initDataDir(Datadir)
- cfg := ethutil.ReadConfig(ConfigFile, Datadir, EnvPrefix)
- cfg.VmType = vmType
-
- return cfg
-}
-
func exit(err error) {
status := 0
if err != nil {
- fmt.Fprintln(os.Stderr, "Fatal: ", err)
+ fmt.Fprintln(os.Stderr, "Fatal:", err)
status = 1
}
logger.Flush()
@@ -142,47 +131,6 @@ func StartEthereumForTest(ethereum *eth.Ethereum) {
})
}
-func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) {
- var err error
- switch {
- case GenAddr:
- if NonInteractive || confirm("This action overwrites your old private key.") {
- err = keyManager.Init(KeyRing, 0, true)
- }
- exit(err)
- case len(SecretFile) > 0:
- SecretFile = ethutil.ExpandHomePath(SecretFile)
-
- if NonInteractive || confirm("This action overwrites your old private key.") {
- err = keyManager.InitFromSecretsFile(KeyRing, 0, SecretFile)
- }
- exit(err)
- case len(ExportDir) > 0:
- err = keyManager.Init(KeyRing, 0, false)
- if err == nil {
- err = keyManager.Export(ExportDir)
- }
- exit(err)
- default:
- // Creates a keypair if none exists
- err = keyManager.Init(KeyRing, 0, false)
- if err != nil {
- exit(err)
- }
- }
- clilogger.Infof("Main address %x\n", keyManager.Address())
-}
-
-func StartRpc(ethereum *eth.Ethereum, RpcListenAddress string, RpcPort int) {
- var err error
- ethereum.RpcServer, err = rpchttp.NewRpcHttpServer(xeth.New(ethereum, nil), RpcListenAddress, RpcPort)
- if err != nil {
- clilogger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err)
- } else {
- go ethereum.RpcServer.Start()
- }
-}
-
func FormatTransactionData(data string) []byte {
d := ethutil.StringToByteFunc(data, func(s string) (ret []byte) {
slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000)
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 88ff3558d9..2c7d37942a 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -2,10 +2,15 @@ package utils
import (
"crypto/ecdsa"
+ "fmt"
+ "net"
+ "net/http"
+ "os"
"path"
"runtime"
"github.com/codegangsta/cli"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -15,8 +20,48 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/nat"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/ethereum/go-ethereum/xeth"
)
+func init() {
+ cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]
+
+VERSION:
+ {{.Version}}
+
+COMMANDS:
+ {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
+ {{end}}{{if .Flags}}
+GLOBAL OPTIONS:
+ {{range .Flags}}{{.}}
+ {{end}}{{end}}
+`
+
+ cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
+{{if .Description}}{{.Description}}
+{{end}}{{if .Subcommands}}
+SUBCOMMANDS:
+ {{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
+ {{end}}{{end}}{{if .Flags}}
+OPTIONS:
+ {{range .Flags}}{{.}}
+ {{end}}{{end}}
+`
+}
+
+// NewApp creates an app with sane defaults.
+func NewApp(version, usage string) *cli.App {
+ app := cli.NewApp()
+ app.Name = path.Base(os.Args[0])
+ app.Author = ""
+ app.Authors = nil
+ app.Email = ""
+ app.Version = version
+ app.Usage = usage
+ return app
+}
+
// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
@@ -32,20 +77,14 @@ var (
Usage: "Virtual Machine type: 0 is standard VM, 1 is debug VM",
}
*/
+ UnlockedAccountFlag = cli.StringFlag{
+ Name: "unlock",
+ Usage: "Unlock a given account untill this programs exits (address:password)",
+ }
VMDebugFlag = cli.BoolFlag{
Name: "vmdebug",
Usage: "Virtual Machine debug output",
}
- KeyRingFlag = cli.StringFlag{
- Name: "keyring",
- Usage: "Name of keyring to be used",
- Value: "",
- }
- KeyStoreFlag = cli.StringFlag{
- Name: "keystore",
- Usage: `Where to store keyrings: "db" or "file"`,
- Value: "db",
- }
DataDirFlag = cli.StringFlag{
Name: "datadir",
Usage: "Data directory to be used",
@@ -149,30 +188,24 @@ func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
return key
}
-func GetEthereum(clientID, version string, ctx *cli.Context) *eth.Ethereum {
- ethereum, err := eth.New(ð.Config{
- Name: p2p.MakeName(clientID, version),
- KeyStore: ctx.GlobalString(KeyStoreFlag.Name),
- DataDir: ctx.GlobalString(DataDirFlag.Name),
- LogFile: ctx.GlobalString(LogFileFlag.Name),
- LogLevel: ctx.GlobalInt(LogLevelFlag.Name),
- LogFormat: ctx.GlobalString(LogFormatFlag.Name),
- MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
- VmDebug: ctx.GlobalBool(VMDebugFlag.Name),
-
- MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
- Port: ctx.GlobalString(ListenPortFlag.Name),
- NAT: GetNAT(ctx),
- NodeKey: GetNodeKey(ctx),
- KeyRing: ctx.GlobalString(KeyRingFlag.Name),
- Shh: true,
- Dial: true,
- BootNodes: ctx.GlobalString(BootnodesFlag.Name),
+func GetEthereum(clientID, version string, ctx *cli.Context) (*eth.Ethereum, error) {
+ return eth.New(ð.Config{
+ Name: p2p.MakeName(clientID, version),
+ DataDir: ctx.GlobalString(DataDirFlag.Name),
+ LogFile: ctx.GlobalString(LogFileFlag.Name),
+ LogLevel: ctx.GlobalInt(LogLevelFlag.Name),
+ LogFormat: ctx.GlobalString(LogFormatFlag.Name),
+ MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
+ AccountManager: GetAccountManager(ctx),
+ VmDebug: ctx.GlobalBool(VMDebugFlag.Name),
+ MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
+ Port: ctx.GlobalString(ListenPortFlag.Name),
+ NAT: GetNAT(ctx),
+ NodeKey: GetNodeKey(ctx),
+ Shh: true,
+ Dial: true,
+ BootNodes: ctx.GlobalString(BootnodesFlag.Name),
})
- if err != nil {
- exit(err)
- }
- return ethereum
}
func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database, ethutil.Database) {
@@ -188,3 +221,27 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database, ethutil.D
}
return core.NewChainManager(blockDb, stateDb, new(event.TypeMux)), blockDb, stateDb
}
+
+// Global account manager
+var km *accounts.Manager
+
+func GetAccountManager(ctx *cli.Context) *accounts.Manager {
+ dataDir := ctx.GlobalString(DataDirFlag.Name)
+ if km == nil {
+ ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
+ km = accounts.NewManager(ks)
+ }
+ return km
+}
+
+func StartRPC(eth *eth.Ethereum, ctx *cli.Context) {
+ addr := ctx.GlobalString(RPCListenAddrFlag.Name)
+ port := ctx.GlobalInt(RPCPortFlag.Name)
+ dataDir := ctx.GlobalString(DataDirFlag.Name)
+
+ l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
+ if err != nil {
+ Fatalf("Can't listen on %s:%d: %v", addr, port, err)
+ }
+ go http.Serve(l, rpc.JSONRPC(xeth.New(eth, nil), dataDir))
+}
diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go
index e78c2e980e..91822f9e20 100644
--- a/core/chain_manager_test.go
+++ b/core/chain_manager_test.go
@@ -12,14 +12,12 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rlp"
)
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
- ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
}
// Test fork of length N starting from block i
diff --git a/core/helper_test.go b/core/helper_test.go
index 473576e3f1..36d9e70a83 100644
--- a/core/helper_test.go
+++ b/core/helper_test.go
@@ -62,8 +62,6 @@ func (tm *TestManager) Db() ethutil.Database {
}
func NewTestManager() *TestManager {
- ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "ETH")
-
db, err := ethdb.NewMemDatabase()
if err != nil {
fmt.Println("Could not create mem-db, failing")
diff --git a/core/manager.go b/core/manager.go
index 803069377a..c4052cc058 100644
--- a/core/manager.go
+++ b/core/manager.go
@@ -1,7 +1,6 @@
package core
import (
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
@@ -14,7 +13,6 @@ type Backend interface {
PeerCount() int
IsListening() bool
Peers() []*p2p.Peer
- KeyManager() *crypto.KeyManager
BlockDb() ethutil.Database
StateDb() ethutil.Database
EventMux() *event.TypeMux
diff --git a/core/state_transition.go b/core/state_transition.go
index f54acd6eec..9b67de149f 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -45,8 +45,6 @@ type StateTransition struct {
}
type Message interface {
- Hash() []byte
-
From() []byte
To() []byte
@@ -153,7 +151,7 @@ func (self *StateTransition) preCheck() (err error) {
}
func (self *StateTransition) TransitionState() (ret []byte, err error) {
- statelogger.Debugf("(~) %x\n", self.msg.Hash())
+ // statelogger.Debugf("(~) %x\n", self.msg.Hash())
// XXX Transactions after this point are considered valid.
if err = self.preCheck(); err != nil {
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 7a1d6104e4..7d34c86f47 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -129,6 +129,7 @@ func (tx *Transaction) sender() []byte {
return crypto.Sha3(pubkey[1:])[12:]
}
+// TODO: deprecate after new accounts & key stores are integrated
func (tx *Transaction) Sign(privk []byte) error {
sig := tx.Signature(privk)
@@ -140,6 +141,13 @@ func (tx *Transaction) Sign(privk []byte) error {
return nil
}
+func (tx *Transaction) SetSignatureValues(sig []byte) error {
+ tx.R = sig[:32]
+ tx.S = sig[32:64]
+ tx.V = uint64(sig[64] + 27)
+ return nil
+}
+
func (tx *Transaction) SignECDSA(key *ecdsa.PrivateKey) error {
return tx.Sign(crypto.FromECDSA(key))
}
diff --git a/crypto/key_manager.go b/crypto/key_manager.go
deleted file mode 100644
index 326e559e07..0000000000
--- a/crypto/key_manager.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package crypto
-
-import (
- "fmt"
- "sync"
-
- "github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/logger"
-)
-
-var keylogger = logger.NewLogger("KEY")
-
-type KeyManager struct {
- keyRing *KeyRing
- session string
- keyStore KeyStore // interface
- keyRings map[string]*KeyRing // cache
- keyPair *KeyPair
-}
-
-func NewDBKeyManager(db ethutil.Database) *KeyManager {
- return &KeyManager{keyStore: &DBKeyStore{db: db}, keyRings: make(map[string]*KeyRing)}
-}
-
-func NewFileKeyManager(basedir string) *KeyManager {
- return &KeyManager{keyStore: &FileKeyStore{basedir: basedir}, keyRings: make(map[string]*KeyRing)}
-}
-
-func (k *KeyManager) KeyPair() *KeyPair {
- return k.keyPair
-}
-
-func (k *KeyManager) KeyRing() *KeyPair {
- return k.keyPair
-}
-
-func (k *KeyManager) PrivateKey() []byte {
- return k.keyPair.PrivateKey
-}
-
-func (k *KeyManager) PublicKey() []byte {
- return k.keyPair.PublicKey
-}
-
-func (k *KeyManager) Address() []byte {
- return k.keyPair.Address()
-}
-
-func (k *KeyManager) save(session string, keyRing *KeyRing) error {
- err := k.keyStore.Save(session, keyRing)
- if err != nil {
- return err
- }
- k.keyRings[session] = keyRing
- return nil
-}
-
-func (k *KeyManager) load(session string) (*KeyRing, error) {
- keyRing, found := k.keyRings[session]
- if !found {
- var err error
- keyRing, err = k.keyStore.Load(session)
- if err != nil {
- return nil, err
- }
- }
- return keyRing, nil
-}
-
-func cursorError(cursor int, len int) error {
- return fmt.Errorf("cursor %d out of range (0..%d)", cursor, len)
-}
-
-func (k *KeyManager) reset(session string, cursor int, keyRing *KeyRing) error {
- if cursor >= keyRing.Len() {
- return cursorError(cursor, keyRing.Len())
- }
- lock := &sync.Mutex{}
- lock.Lock()
- defer lock.Unlock()
- err := k.save(session, keyRing)
- if err != nil {
- return err
- }
- k.session = session
- k.keyRing = keyRing
- k.keyPair = keyRing.GetKeyPair(cursor)
- return nil
-}
-
-func (k *KeyManager) SetCursor(cursor int) error {
- if cursor >= k.keyRing.Len() {
- return cursorError(cursor, k.keyRing.Len())
- }
- k.keyPair = k.keyRing.GetKeyPair(cursor)
- return nil
-}
-
-func (k *KeyManager) Init(session string, cursor int, force bool) error {
- var keyRing *KeyRing
- if !force {
- var err error
- keyRing, err = k.load(session)
- if err != nil {
- return err
- }
- }
- if keyRing == nil {
- keyRing = NewGeneratedKeyRing(1)
- keylogger.Infof("Created keypair. Private key: %x\n", keyRing.keys[0].PrivateKey)
- }
- return k.reset(session, cursor, keyRing)
-}
-
-func (k *KeyManager) InitFromSecretsFile(session string, cursor int, secretsfile string) error {
- keyRing, err := NewKeyRingFromFile(secretsfile)
- if err != nil {
- return err
- }
- return k.reset(session, cursor, keyRing)
-}
-
-func (k *KeyManager) InitFromString(session string, cursor int, secrets string) error {
- keyRing, err := NewKeyRingFromString(secrets)
- if err != nil {
- return err
- }
- return k.reset(session, cursor, keyRing)
-}
-
-func (k *KeyManager) Export(dir string) error {
- fileKeyStore := FileKeyStore{dir}
- return fileKeyStore.Save(k.session, k.keyRing)
-}
diff --git a/crypto/key_store.go b/crypto/key_store.go
deleted file mode 100644
index 04560a04ed..0000000000
--- a/crypto/key_store.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package crypto
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path"
- "strings"
-
- "github.com/ethereum/go-ethereum/ethutil"
-)
-
-type KeyStore interface {
- Load(string) (*KeyRing, error)
- Save(string, *KeyRing) error
-}
-
-type DBKeyStore struct {
- db ethutil.Database
-}
-
-const dbKeyPrefix = "KeyRing"
-
-func (k *DBKeyStore) dbKey(session string) []byte {
- return []byte(fmt.Sprintf("%s%s", dbKeyPrefix, session))
-}
-
-func (k *DBKeyStore) Save(session string, keyRing *KeyRing) error {
- k.db.Put(k.dbKey(session), keyRing.RlpEncode())
- return nil
-}
-
-func (k *DBKeyStore) Load(session string) (*KeyRing, error) {
- data, err := k.db.Get(k.dbKey(session))
- if err != nil {
- return nil, nil
- }
- var keyRing *KeyRing
- keyRing, err = NewKeyRingFromBytes(data)
- if err != nil {
- return nil, err
- }
- // if empty keyRing is found we return nil, no error
- if keyRing.Len() == 0 {
- return nil, nil
- }
- return keyRing, nil
-}
-
-type FileKeyStore struct {
- basedir string
-}
-
-func (k *FileKeyStore) Save(session string, keyRing *KeyRing) error {
- var content []byte
- var err error
- var privateKeys []string
- var publicKeys []string
- var mnemonics []string
- var addresses []string
- keyRing.Each(func(keyPair *KeyPair) {
- privateKeys = append(privateKeys, ethutil.Bytes2Hex(keyPair.PrivateKey))
- publicKeys = append(publicKeys, ethutil.Bytes2Hex(keyPair.PublicKey))
- addresses = append(addresses, ethutil.Bytes2Hex(keyPair.Address()))
- mnemonics = append(mnemonics, keyPair.Mnemonic())
- })
-
- basename := session
- if session == "" {
- basename = "default"
- }
-
- path := path.Join(k.basedir, basename)
- content = []byte(strings.Join(privateKeys, "\n"))
- err = ioutil.WriteFile(path+".prv", content, 0600)
- if err != nil {
- return err
- }
-
- content = []byte(strings.Join(publicKeys, "\n"))
- err = ioutil.WriteFile(path+".pub", content, 0644)
- if err != nil {
- return err
- }
-
- content = []byte(strings.Join(addresses, "\n"))
- err = ioutil.WriteFile(path+".addr", content, 0644)
- if err != nil {
- return err
- }
-
- content = []byte(strings.Join(mnemonics, "\n"))
- err = ioutil.WriteFile(path+".mne", content, 0600)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (k *FileKeyStore) Load(session string) (*KeyRing, error) {
- basename := session
- if session == "" {
- basename = "default"
- }
- secfile := path.Join(k.basedir, basename+".prv")
- _, err := os.Stat(secfile)
- // if file is not found then we return nil, no error
- if err != nil {
- return nil, nil
- }
- return NewKeyRingFromFile(secfile)
-}
diff --git a/crypto/keyring.go b/crypto/keyring.go
deleted file mode 100644
index eab13dbc4b..0000000000
--- a/crypto/keyring.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package crypto
-
-import (
- "fmt"
- "io/ioutil"
- "strings"
-
- "github.com/ethereum/go-ethereum/ethutil"
-)
-
-type KeyRing struct {
- keys []*KeyPair
-}
-
-func NewKeyRing() *KeyRing {
- return &KeyRing{}
-}
-
-func (k *KeyRing) AddKeyPair(keyPair *KeyPair) {
- k.keys = append(k.keys, keyPair)
-}
-
-func (k *KeyRing) GetKeyPair(i int) *KeyPair {
- if len(k.keys) > i {
- return k.keys[i]
- }
-
- return nil
-}
-
-func (k *KeyRing) Empty() bool {
- return k.Len() == 0
-}
-
-func (k *KeyRing) Len() int {
- return len(k.keys)
-}
-
-func (k *KeyRing) Each(f func(*KeyPair)) {
- for _, keyPair := range k.keys {
- f(keyPair)
- }
-}
-
-func NewGeneratedKeyRing(len int) *KeyRing {
- keyRing := NewKeyRing()
- for i := 0; i < len; i++ {
- keyRing.AddKeyPair(GenerateNewKeyPair())
- }
- return keyRing
-}
-
-func NewKeyRingFromFile(secfile string) (*KeyRing, error) {
- var content []byte
- var err error
- content, err = ioutil.ReadFile(secfile)
- if err != nil {
- return nil, err
- }
- keyRing, err := NewKeyRingFromString(string(content))
- if err != nil {
- return nil, err
- }
- return keyRing, nil
-}
-
-func NewKeyRingFromString(content string) (*KeyRing, error) {
- secretStrings := strings.Split(content, "\n")
- var secrets [][]byte
- for _, secretString := range secretStrings {
- secret := secretString
- words := strings.Split(secretString, " ")
- if len(words) == 24 {
- secret = MnemonicDecode(words)
- } else if len(words) != 1 {
- return nil, fmt.Errorf("Unrecognised key format")
- }
-
- if len(secret) != 0 {
- secrets = append(secrets, ethutil.Hex2Bytes(secret))
- }
- }
-
- return NewKeyRingFromSecrets(secrets)
-}
-
-func NewKeyRingFromSecrets(secs [][]byte) (*KeyRing, error) {
- keyRing := NewKeyRing()
- for _, sec := range secs {
- keyPair, err := NewKeyPairFromSec(sec)
- if err != nil {
- return nil, err
- }
- keyRing.AddKeyPair(keyPair)
- }
- return keyRing, nil
-}
-
-func NewKeyRingFromBytes(data []byte) (*KeyRing, error) {
- var secrets [][]byte
- it := ethutil.NewValueFromBytes(data).NewIterator()
- for it.Next() {
- secret := it.Value().Bytes()
- secrets = append(secrets, secret)
- }
- keyRing, err := NewKeyRingFromSecrets(secrets)
- if err != nil {
- return nil, err
- }
- return keyRing, nil
-}
-
-func (k *KeyRing) RlpEncode() []byte {
- return k.RlpValue().Encode()
-}
-
-func (k *KeyRing) RlpValue() *ethutil.Value {
- v := ethutil.EmptyValue()
- k.Each(func(keyPair *KeyPair) {
- v.Append(keyPair.RlpValue())
- })
- return v
-}
diff --git a/crypto/keys_test.go b/crypto/keys_test.go
deleted file mode 100644
index 56e8519696..0000000000
--- a/crypto/keys_test.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package crypto
-
-// import (
-// "github.com/ethereum/go-ethereum/ethdb"
-// // "io/ioutil"
-// "fmt"
-// "os"
-// "path"
-// "testing"
-// )
-
-// // test if persistence layer works
-// func TestDBKeyManager(t *testing.T) {
-// memdb, _ := ethdb.NewMemDatabase()
-// keyManager0 := NewDBKeyManager(memdb)
-// err := keyManager0.Init("", 0, false)
-// if err != nil {
-// t.Error("Unexpected error: ", err)
-// }
-// keyManager1 := NewDBKeyManager(memdb)
-// err = keyManager1.Init("", 0, false)
-// if err != nil {
-// t.Error("Unexpected error: ", err)
-// }
-// if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) {
-// t.Error("Expected private keys %x, %x, to be identical via db persistence", keyManager0.PrivateKey(), keyManager1.PrivateKey())
-// }
-// err = keyManager1.Init("", 0, true)
-// if err != nil {
-// t.Error("Unexpected error: ", err)
-// }
-// if string(keyManager0.PrivateKey()) == string(keyManager1.PrivateKey()) {
-// t.Error("Expected private keys %x, %x, to be be different despite db persistence if force generate", keyManager0.PrivateKey(), keyManager1.PrivateKey())
-// }
-// }
-
-// func TestFileKeyManager(t *testing.T) {
-// basedir0 := "/tmp/ethtest0"
-// os.RemoveAll(basedir0)
-// os.Mkdir(basedir0, 0777)
-
-// keyManager0 := NewFileKeyManager(basedir0)
-// err := keyManager0.Init("", 0, false)
-// if err != nil {
-// t.Error("Unexpected error: ", err)
-// }
-
-// keyManager1 := NewFileKeyManager(basedir0)
-
-// err = keyManager1.Init("", 0, false)
-// if err != nil {
-// t.Error("Unexpected error: ", err)
-// }
-// if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) {
-// t.Error("Expected private keys %x, %x, to be identical via db persistence", keyManager0.PrivateKey(), keyManager1.PrivateKey())
-// }
-
-// err = keyManager1.Init("", 0, true)
-// if err != nil {
-// t.Error("Unexpected error: ", err)
-// }
-// if string(keyManager0.PrivateKey()) == string(keyManager1.PrivateKey()) {
-// t.Error("Expected private keys %x, %x, to be be different despite db persistence if force generate", keyManager0.PrivateKey(), keyManager1.PrivateKey())
-// }
-// }
-
-// // cursor errors
-// func TestCursorErrors(t *testing.T) {
-// memdb, _ := ethdb.NewMemDatabase()
-// keyManager0 := NewDBKeyManager(memdb)
-// err := keyManager0.Init("", 0, false)
-// err = keyManager0.Init("", 1, false)
-// if err == nil {
-// t.Error("Expected cursor error")
-// }
-// err = keyManager0.SetCursor(1)
-// if err == nil {
-// t.Error("Expected cursor error")
-// }
-// }
-
-// func TestExportImport(t *testing.T) {
-// memdb, _ := ethdb.NewMemDatabase()
-// keyManager0 := NewDBKeyManager(memdb)
-// err := keyManager0.Init("", 0, false)
-// basedir0 := "/tmp/ethtest0"
-// os.RemoveAll(basedir0)
-// os.Mkdir(basedir0, 0777)
-// keyManager0.Export(basedir0)
-
-// keyManager1 := NewFileKeyManager(basedir0)
-// err = keyManager1.Init("", 0, false)
-// if err != nil {
-// t.Error("Unexpected error: ", err)
-// }
-// fmt.Printf("keyRing: %v\n", keyManager0.KeyPair())
-// fmt.Printf("keyRing: %v\n", keyManager1.KeyPair())
-// if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) {
-// t.Error("Expected private keys %x, %x, to be identical via export to filestore basedir", keyManager0.PrivateKey(), keyManager1.PrivateKey())
-// }
-// path.Join("")
-
-// // memdb, _ = ethdb.NewMemDatabase()
-// // keyManager2 := NewDBKeyManager(memdb)
-// // err = keyManager2.InitFromSecretsFile("", 0, path.Join(basedir0, "default.prv"))
-// // if err != nil {
-// // t.Error("Unexpected error: ", err)
-// // }
-// // if string(keyManager0.PrivateKey()) != string(keyManager2.PrivateKey()) {
-// // t.Error("Expected private keys %s, %s, to be identical via export/import prv", keyManager0.PrivateKey(), keyManager1.PrivateKey())
-// // }
-
-// // memdb, _ = ethdb.NewMemDatabase()
-// // keyManager3 := NewDBKeyManager(memdb)
-// // err = keyManager3.InitFromSecretsFile("", 0, path.Join(basedir0, "default.mne"))
-// // if err != nil {
-// // t.Error("Unexpected error: ", err)
-// // }
-// // if string(keyManager0.PrivateKey()) != string(keyManager3.PrivateKey()) {
-// // t.Error("Expected private keys %s, %s, to be identical via export/import mnemonic file", keyManager0.PrivateKey(), keyManager1.PrivateKey())
-// // }
-// }
diff --git a/eth/backend.go b/eth/backend.go
index f42ceda693..9e365161dd 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -8,6 +8,7 @@ import (
"strings"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/blockpool"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
@@ -19,13 +20,12 @@ import (
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/nat"
- "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/vm"
"github.com/ethereum/go-ethereum/whisper"
)
var (
- ethlogger = logger.NewLogger("SERV")
+ servlogger = logger.NewLogger("SERV")
jsonlogger = logger.NewJsonLogger()
defaultBootNodes = []*discover.Node{
@@ -38,11 +38,9 @@ var (
type Config struct {
Name string
- KeyStore string
DataDir string
LogFile string
LogLevel int
- KeyRing string
LogFormat string
VmDebug bool
@@ -61,9 +59,8 @@ type Config struct {
Shh bool
Dial bool
- MinerThreads int
-
- KeyManager *crypto.KeyManager
+ MinerThreads int
+ AccountManager *accounts.Manager
}
func (cfg *Config) parseBootNodes() []*discover.Node {
@@ -77,7 +74,7 @@ func (cfg *Config) parseBootNodes() []*discover.Node {
}
n, err := discover.ParseNode(url)
if err != nil {
- ethlogger.Errorf("Bootstrap URL %s: %v\n", url, err)
+ servlogger.Errorf("Bootstrap URL %s: %v\n", url, err)
continue
}
ns = append(ns, n)
@@ -101,7 +98,7 @@ func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) {
return nil, fmt.Errorf("could not generate server key: %v", err)
}
if err := ioutil.WriteFile(keyfile, crypto.FromECDSA(key), 0600); err != nil {
- ethlogger.Errorln("could not persist nodekey: ", err)
+ servlogger.Errorln("could not persist nodekey: ", err)
}
return key, nil
}
@@ -120,6 +117,7 @@ type Ethereum struct {
txPool *core.TxPool
chainManager *core.ChainManager
blockPool *blockpool.BlockPool
+ accountManager *accounts.Manager
whisper *whisper.Whisper
net *p2p.Server
@@ -128,9 +126,6 @@ type Ethereum struct {
blockSub event.Subscription
miner *miner.Miner
- RpcServer rpc.RpcServer
- keyManager *crypto.KeyManager
-
logger logger.LogSystem
Mining bool
@@ -139,7 +134,7 @@ type Ethereum struct {
func New(config *Config) (*Ethereum, error) {
// Boostrap database
- ethlogger := logger.New(config.DataDir, config.LogFile, config.LogLevel, config.LogFormat)
+ servlogger := logger.New(config.DataDir, config.LogFile, config.LogLevel, config.LogFormat)
blockDb, err := ethdb.NewLDBDatabase(path.Join(config.DataDir, "blockchain"))
if err != nil {
@@ -158,40 +153,31 @@ func New(config *Config) (*Ethereum, error) {
return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, ProtocolVersion, path)
}
- // Create new keymanager
- var keyManager *crypto.KeyManager
- switch config.KeyStore {
- case "db":
- keyManager = crypto.NewDBKeyManager(blockDb)
- case "file":
- keyManager = crypto.NewFileKeyManager(config.DataDir)
- default:
- return nil, fmt.Errorf("unknown keystore type: %s", config.KeyStore)
- }
- // Initialise the keyring
- keyManager.Init(config.KeyRing, 0, false)
-
saveProtocolVersion(blockDb)
//ethutil.Config.Db = db
eth := &Ethereum{
- shutdownChan: make(chan bool),
- blockDb: blockDb,
- stateDb: stateDb,
- keyManager: keyManager,
- eventMux: &event.TypeMux{},
- logger: ethlogger,
- DataDir: config.DataDir,
+ shutdownChan: make(chan bool),
+ blockDb: blockDb,
+ stateDb: stateDb,
+ eventMux: &event.TypeMux{},
+ logger: servlogger,
+ accountManager: config.AccountManager,
+ DataDir: config.DataDir,
+ }
+
+ cb, err := eth.accountManager.Coinbase()
+ if err != nil {
+ return nil, err
}
eth.chainManager = core.NewChainManager(blockDb, stateDb, eth.EventMux())
pow := ethash.New(eth.chainManager)
-
eth.txPool = core.NewTxPool(eth.EventMux())
eth.blockProcessor = core.NewBlockProcessor(stateDb, pow, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor)
eth.whisper = whisper.New()
- eth.miner = miner.New(keyManager.Address(), eth, pow, config.MinerThreads)
+ eth.miner = miner.New(cb, eth, pow, config.MinerThreads)
hasBlock := eth.chainManager.HasBlock
insertChain := eth.chainManager.InsertChain
@@ -225,9 +211,9 @@ func New(config *Config) (*Ethereum, error) {
return eth, nil
}
-func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager }
func (s *Ethereum) Logger() logger.LogSystem { return s.logger }
func (s *Ethereum) Name() string { return s.net.Name }
+func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager }
func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
@@ -241,7 +227,6 @@ func (s *Ethereum) IsListening() bool { return true } // Alwa
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
-func (s *Ethereum) Coinbase() []byte { return nil } // TODO
// Start the ethereum
func (s *Ethereum) Start() error {
@@ -271,7 +256,7 @@ func (s *Ethereum) Start() error {
s.blockSub = s.eventMux.Subscribe(core.NewMinedBlockEvent{})
go s.blockBroadcastLoop()
- ethlogger.Infoln("Server started")
+ servlogger.Infoln("Server started")
return nil
}
@@ -303,10 +288,6 @@ func (s *Ethereum) Stop() {
s.txSub.Unsubscribe() // quits txBroadcastLoop
s.blockSub.Unsubscribe() // quits blockBroadcastLoop
- if s.RpcServer != nil {
- s.RpcServer.Stop()
- }
-
s.txPool.Stop()
s.eventMux.Stop()
s.blockPool.Stop()
@@ -314,7 +295,7 @@ func (s *Ethereum) Stop() {
s.whisper.Stop()
}
- ethlogger.Infoln("Server stopped")
+ servlogger.Infoln("Server stopped")
close(s.shutdownChan)
}
diff --git a/ethdb/database_test.go b/ethdb/database_test.go
index 2cbaf58e0d..7de30fd814 100644
--- a/ethdb/database_test.go
+++ b/ethdb/database_test.go
@@ -7,8 +7,6 @@ import (
)
func TestCompression(t *testing.T) {
- ethutil.ReadConfig("", "/tmp", "")
-
db, err := NewLDBDatabase("testdb")
if err != nil {
t.Fatal(err)
diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go
index 36b14a057d..0a137f72a0 100644
--- a/javascript/javascript_runtime.go
+++ b/javascript/javascript_runtime.go
@@ -6,6 +6,7 @@ import (
"os"
"path"
"path/filepath"
+
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/xeth"
"github.com/obscuren/otto"
diff --git a/javascript/types.go b/javascript/types.go
index 3921f48431..5f47c17356 100644
--- a/javascript/types.go
+++ b/javascript/types.go
@@ -70,8 +70,8 @@ func (self *JSEthereum) GetStateObject(addr string) otto.Value {
return self.toVal(&JSStateObject{self.XEth.State().SafeGet(addr), self})
}
-func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
- r, err := self.XEth.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr)
+func (self *JSEthereum) Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
+ r, err := self.XEth.Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr)
if err != nil {
fmt.Println(err)
diff --git a/rpc/api.go b/rpc/api.go
index 38f02a4735..04492f27c6 100644
--- a/rpc/api.go
+++ b/rpc/api.go
@@ -2,7 +2,9 @@ package rpc
import (
"encoding/json"
+ "fmt"
"math/big"
+ "path"
"strings"
"sync"
"time"
@@ -46,8 +48,8 @@ type EthereumApi struct {
// defaultBlockAge int64
}
-func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
- db, _ := ethdb.NewLDBDatabase("dapps")
+func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi {
+ db, _ := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps"))
api := &EthereumApi{
eth: eth,
mux: eth.Backend().EventMux(),
@@ -232,15 +234,7 @@ func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error
return nil
}
-func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error {
- if args.Gas == ethutil.Big0 {
- args.Gas = defaultGas
- }
-
- if args.GasPrice == ethutil.Big0 {
- args.GasPrice = defaultGasPrice
- }
-
+func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) (err error) {
// TODO if no_private_key then
//if _, exists := p.register[args.From]; exists {
// p.register[args.From] = append(p.register[args.From], args)
@@ -262,18 +256,28 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error {
p.register[ags.From] = append(p.register[args.From], args)
}
*/
- result, err := p.xeth().Transact( /* TODO specify account */ args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+ // TODO: align default values to have the same type, e.g. not depend on
+ // ethutil.Value conversions later on
+ fmt.Println("gas", args.Gas)
+ if args.Gas.Cmp(big.NewInt(0)) == 0 {
+ args.Gas = defaultGas
+ }
+
+ if args.GasPrice.Cmp(big.NewInt(0)) == 0 {
+ args.GasPrice = defaultGasPrice
+ }
+
+ *reply, err = p.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
if err != nil {
+ fmt.Println("err:", err)
return err
}
- *reply = result
- //}
return nil
}
func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error {
- result, err := p.xeth().Call( /* TODO specify account */ args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+ result, err := p.xeth().Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
if err != nil {
return err
}
diff --git a/rpc/http.go b/rpc/http.go
new file mode 100644
index 0000000000..857cf32210
--- /dev/null
+++ b/rpc/http.go
@@ -0,0 +1,52 @@
+package rpc
+
+import (
+ "net/http"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/xeth"
+)
+
+var rpchttplogger = logger.NewLogger("RPC-HTTP")
+
+const (
+ jsonrpcver = "2.0"
+ maxSizeReqLength = 1024 * 1024 // 1MB
+)
+
+// JSONRPC returns a handler that implements the Ethereum JSON-RPC API.
+func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler {
+ var json JsonWrapper
+ api := NewEthereumApi(pipe, dataDir)
+
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+
+ rpchttplogger.DebugDetailln("Handling request")
+
+ if req.ContentLength > maxSizeReqLength {
+ jsonerr := &RpcErrorObject{-32700, "Error: Request too large"}
+ json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr})
+ return
+ }
+
+ reqParsed, reqerr := json.ParseRequestBody(req)
+ if reqerr != nil {
+ jsonerr := &RpcErrorObject{-32700, "Error: Could not parse request"}
+ json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr})
+ return
+ }
+
+ var response interface{}
+ reserr := api.GetRequestReply(&reqParsed, &response)
+ if reserr != nil {
+ rpchttplogger.Warnln(reserr)
+ jsonerr := &RpcErrorObject{-32603, reserr.Error()}
+ json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr})
+ return
+ }
+
+ rpchttplogger.DebugDetailf("Generated response: %T %s", response, response)
+ json.Send(w, &RpcSuccessResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Result: response})
+ })
+}
diff --git a/rpc/http/server.go b/rpc/http/server.go
deleted file mode 100644
index d8f0157f11..0000000000
--- a/rpc/http/server.go
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- This file is part of go-ethereum
-
- go-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- go-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with go-ethereum. If not, see .
-*/
-package rpchttp
-
-import (
- "fmt"
- "net"
- "net/http"
-
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/xeth"
-)
-
-var rpchttplogger = logger.NewLogger("RPC-HTTP")
-var JSON rpc.JsonWrapper
-
-const maxSizeReqLength = 1024 * 1024 // 1MB
-
-func NewRpcHttpServer(pipe *xeth.XEth, address string, port int) (*RpcHttpServer, error) {
- sport := fmt.Sprintf("%s:%d", address, port)
- l, err := net.Listen("tcp", sport)
- if err != nil {
- return nil, err
- }
-
- return &RpcHttpServer{
- listener: l,
- quit: make(chan bool),
- pipe: pipe,
- port: port,
- addr: address,
- }, nil
-}
-
-type RpcHttpServer struct {
- quit chan bool
- listener net.Listener
- pipe *xeth.XEth
- port int
- addr string
-}
-
-func (s *RpcHttpServer) exitHandler() {
-out:
- for {
- select {
- case <-s.quit:
- s.listener.Close()
- break out
- }
- }
-
- rpchttplogger.Infoln("Shutdown RPC-HTTP server")
-}
-
-func (s *RpcHttpServer) Stop() {
- close(s.quit)
-}
-
-func (s *RpcHttpServer) Start() {
- rpchttplogger.Infof("Starting RPC-HTTP server on %s:%d", s.addr, s.port)
- go s.exitHandler()
-
- api := rpc.NewEthereumApi(s.pipe)
- h := s.apiHandler(api)
- http.Handle("/", h)
-
- err := http.Serve(s.listener, nil)
- // FIX Complains on shutdown due to listner already being closed
- if err != nil {
- rpchttplogger.Errorln("Error on RPC-HTTP interface:", err)
- }
-}
-
-func (s *RpcHttpServer) apiHandler(api *rpc.EthereumApi) http.Handler {
- var jsonrpcver string = "2.0"
- fn := func(w http.ResponseWriter, req *http.Request) {
- w.Header().Set("Access-Control-Allow-Origin", "*")
-
- rpchttplogger.DebugDetailln("Handling request")
-
- if req.ContentLength > maxSizeReqLength {
- jsonerr := &rpc.RpcErrorObject{-32700, "Error: Request too large"}
- JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr})
- return
- }
-
- reqParsed, reqerr := JSON.ParseRequestBody(req)
- if reqerr != nil {
- jsonerr := &rpc.RpcErrorObject{-32700, "Error: Could not parse request"}
- JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr})
- return
- }
-
- var response interface{}
- reserr := api.GetRequestReply(&reqParsed, &response)
- if reserr != nil {
- rpchttplogger.Warnln(reserr)
- jsonerr := &rpc.RpcErrorObject{-32603, reserr.Error()}
- JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr})
- return
- }
-
- rpchttplogger.DebugDetailf("Generated response: %T %s", response, response)
- JSON.Send(w, &rpc.RpcSuccessResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Result: response})
- }
-
- return http.HandlerFunc(fn)
-}
diff --git a/state/state_test.go b/state/state_test.go
index ee1cf92866..07e35f7e28 100644
--- a/state/state_test.go
+++ b/state/state_test.go
@@ -33,16 +33,16 @@ func (s *StateSuite) TestDump(c *checker.C) {
// check that dump contains the state objects that are in trie
got := string(s.state.Dump())
want := `{
- "root": "4e3a59299745ba6752247c8b91d0f716dac9ec235861c91f5ac1894a361d87ba",
+ "root": "6e277ae8357d013e50f74eedb66a991f6922f93ae03714de58b3d0c5e9eee53f",
"accounts": {
- "0000000000000000000000000000000000000001": {
+ "1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d": {
"balance": "22",
"nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"storage": {}
},
- "0000000000000000000000000000000000000102": {
+ "a17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1": {
"balance": "0",
"nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
@@ -57,7 +57,6 @@ func (s *StateSuite) TestDump(c *checker.C) {
}
func (s *StateSuite) SetUpTest(c *checker.C) {
- ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
db, _ := ethdb.NewMemDatabase()
s.state = New(nil, db)
}
diff --git a/tests/helper/init.go b/tests/helper/init.go
index df98b9e422..e0892d8f69 100644
--- a/tests/helper/init.go
+++ b/tests/helper/init.go
@@ -4,7 +4,6 @@ import (
"log"
"os"
- "github.com/ethereum/go-ethereum/ethutil"
logpkg "github.com/ethereum/go-ethereum/logger"
)
@@ -14,6 +13,4 @@ var Log = logpkg.NewLogger("TEST")
func init() {
Logger = logpkg.NewStdLogSystem(os.Stdout, log.LstdFlags, logpkg.InfoLevel)
logpkg.AddLogSystem(Logger)
-
- ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
}
diff --git a/ui/frontend.go b/ui/frontend.go
deleted file mode 100644
index 413a242597..0000000000
--- a/ui/frontend.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package ui
-
-import "github.com/ethereum/go-ethereum/core/types"
-
-type Interface interface {
- UnlockAccount(address []byte) bool
- ConfirmTransaction(tx *types.Transaction) bool
-}
diff --git a/whisper/peer.go b/whisper/peer.go
index 66cfec88c4..4bd9428f5f 100644
--- a/whisper/peer.go
+++ b/whisper/peer.go
@@ -38,11 +38,11 @@ func (self *peer) init() error {
func (self *peer) start() {
go self.update()
- self.peer.Infoln("whisper started")
+ self.peer.Debugln("whisper started")
}
func (self *peer) stop() {
- self.peer.Infoln("whisper stopped")
+ self.peer.Debugln("whisper stopped")
close(self.quit)
}
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 67bb3c622d..e73cd70c9c 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -1,14 +1,13 @@
+// eXtended ETHereum
package xeth
-/*
- * eXtended ETHereum
- */
-
import (
"bytes"
"encoding/json"
"fmt"
+ "math/big"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@@ -18,7 +17,6 @@ import (
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/state"
- "github.com/ethereum/go-ethereum/ui"
"github.com/ethereum/go-ethereum/whisper"
)
@@ -28,11 +26,11 @@ var pipelogger = logger.NewLogger("XETH")
type Backend interface {
BlockProcessor() *core.BlockProcessor
ChainManager() *core.ChainManager
+ AccountManager() *accounts.Manager
TxPool() *core.TxPool
PeerCount() int
IsListening() bool
Peers() []*p2p.Peer
- KeyManager() *crypto.KeyManager
BlockDb() ethutil.Database
StateDb() ethutil.Database
EventMux() *event.TypeMux
@@ -40,37 +38,62 @@ type Backend interface {
Miner() *miner.Miner
}
+// Frontend should be implemented by users of XEth. Its methods are
+// called whenever XEth makes a decision that requires user input.
+type Frontend interface {
+ // UnlockAccount is called when a transaction needs to be signed
+ // but the key corresponding to the transaction's sender is
+ // locked.
+ //
+ // It should unlock the account with the given address and return
+ // true if unlocking succeeded.
+ UnlockAccount(address []byte) bool
+
+ // This is called for all transactions inititated through
+ // Transact. It should prompt the user to confirm the transaction
+ // and return true if the transaction was acknowledged.
+ //
+ // ConfirmTransaction is not used for Call transactions
+ // because they cannot change any state.
+ ConfirmTransaction(tx *types.Transaction) bool
+}
+
type XEth struct {
eth Backend
blockProcessor *core.BlockProcessor
chainManager *core.ChainManager
+ accountManager *accounts.Manager
state *State
whisper *Whisper
miner *miner.Miner
- frontend ui.Interface
+ frontend Frontend
}
-type TmpFrontend struct{}
+// dummyFrontend is a non-interactive frontend that allows all
+// transactions but cannot not unlock any keys.
+type dummyFrontend struct{}
-func (TmpFrontend) UnlockAccount([]byte) bool { panic("UNLOCK ACCOUNT") }
-func (TmpFrontend) ConfirmTransaction(*types.Transaction) bool { panic("CONFIRM TRANSACTION") }
+func (dummyFrontend) UnlockAccount([]byte) bool { return false }
+func (dummyFrontend) ConfirmTransaction(*types.Transaction) bool { return true }
-func New(eth Backend, frontend ui.Interface) *XEth {
+// New creates an XEth that uses the given frontend.
+// If a nil Frontend is provided, a default frontend which
+// confirms all transactions will be used.
+func New(eth Backend, frontend Frontend) *XEth {
xeth := &XEth{
eth: eth,
blockProcessor: eth.BlockProcessor(),
chainManager: eth.ChainManager(),
+ accountManager: eth.AccountManager(),
whisper: NewWhisper(eth.Whisper()),
miner: eth.Miner(),
+ frontend: frontend,
}
-
if frontend == nil {
- xeth.frontend = TmpFrontend{}
+ xeth.frontend = dummyFrontend{}
}
-
xeth.state = NewState(xeth, xeth.chainManager.TransState())
-
return xeth
}
@@ -135,7 +158,13 @@ func (self *XEth) Block(v interface{}) *Block {
}
func (self *XEth) Accounts() []string {
- return []string{toHex(self.eth.KeyManager().Address())}
+ // TODO: check err?
+ accounts, _ := self.eth.AccountManager().Accounts()
+ accountAddresses := make([]string, len(accounts))
+ for i, ac := range accounts {
+ accountAddresses[i] = toHex(ac.Address)
+ }
+ return accountAddresses
}
func (self *XEth) PeerCount() int {
@@ -162,7 +191,8 @@ func (self *XEth) IsListening() bool {
}
func (self *XEth) Coinbase() string {
- return toHex(self.eth.KeyManager().Address())
+ cb, _ := self.eth.AccountManager().Coinbase()
+ return toHex(cb)
}
func (self *XEth) NumberToHuman(balance string) string {
@@ -263,7 +293,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
return toHex(tx.Hash()), nil
}
-func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
+func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
if len(gasStr) == 0 {
gasStr = "100000"
}
@@ -271,41 +301,34 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st
gasPriceStr = "1"
}
- var (
- statedb = self.State().State() //self.chainManager.TransState()
- key = self.eth.KeyManager().KeyPair()
- from = statedb.GetOrNewStateObject(key.Address())
- block = self.chainManager.CurrentBlock()
- to = statedb.GetOrNewStateObject(fromHex(toStr))
- data = fromHex(dataStr)
- gas = ethutil.Big(gasStr)
- price = ethutil.Big(gasPriceStr)
- value = ethutil.Big(valueStr)
- )
-
- msg := types.NewTransactionMessage(fromHex(toStr), value, gas, price, data)
- msg.Sign(key.PrivateKey)
+ statedb := self.State().State() //self.chainManager.TransState()
+ msg := callmsg{
+ from: statedb.GetOrNewStateObject(fromHex(fromStr)),
+ to: fromHex(toStr),
+ gas: ethutil.Big(gasStr),
+ gasPrice: ethutil.Big(gasPriceStr),
+ value: ethutil.Big(valueStr),
+ data: fromHex(dataStr),
+ }
+ block := self.chainManager.CurrentBlock()
vmenv := core.NewEnv(statedb, self.chainManager, msg, block)
- res, err := vmenv.Call(from, to.Address(), data, gas, price, value)
- if err != nil {
- return "", err
- }
-
- return toHex(res), nil
+ res, err := vmenv.Call(msg.from, msg.to, msg.data, msg.gas, msg.gasPrice, msg.value)
+ return toHex(res), err
}
-func (self *XEth) Transact(toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
+func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
var (
+ from []byte
to []byte
value = ethutil.NewValue(valueStr)
gas = ethutil.NewValue(gasStr)
price = ethutil.NewValue(gasPriceStr)
data []byte
- key = self.eth.KeyManager().KeyPair()
contractCreation bool
)
+ from = fromHex(fromStr)
data = fromHex(codeStr)
to = fromHex(toStr)
if len(to) == 0 {
@@ -319,25 +342,61 @@ func (self *XEth) Transact(toStr, valueStr, gasStr, gasPriceStr, codeStr string)
tx = types.NewTransactionMessage(to, value.BigInt(), gas.BigInt(), price.BigInt(), data)
}
- var err error
- state := self.eth.ChainManager().TxState()
- if balance := state.GetBalance(key.Address()); balance.Cmp(tx.Value()) < 0 {
- return "", fmt.Errorf("insufficient balance. balance=%v tx=%v", balance, tx.Value())
- }
- nonce := state.GetNonce(key.Address())
-
+ state := self.chainManager.TxState()
+ nonce := state.GetNonce(from)
tx.SetNonce(nonce)
- tx.Sign(key.PrivateKey)
- err = self.eth.TxPool().Add(tx)
- if err != nil {
+ if err := self.sign(tx, from, false); err != nil {
return "", err
}
- state.SetNonce(key.Address(), nonce+1)
+ if err := self.eth.TxPool().Add(tx); err != nil {
+ return "", err
+ }
+ state.SetNonce(from, nonce+1)
+
+ if contractCreation {
+ addr := core.AddressFromMessage(tx)
+ pipelogger.Infof("Contract addr %x\n", addr)
+ }
if types.IsContractAddr(to) {
return toHex(core.AddressFromMessage(tx)), nil
}
-
return toHex(tx.Hash()), nil
}
+
+func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error {
+ sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash())
+ if err == accounts.ErrLocked {
+ if didUnlock {
+ return fmt.Errorf("sender account still locked after successful unlock")
+ }
+ if !self.frontend.UnlockAccount(from) {
+ return fmt.Errorf("could not unlock sender account")
+ }
+ // retry signing, the account should now be unlocked.
+ return self.sign(tx, from, true)
+ } else if err != nil {
+ return err
+ }
+ tx.SetSignatureValues(sig)
+ return nil
+}
+
+// callmsg is the message type used for call transations.
+type callmsg struct {
+ from *state.StateObject
+ to []byte
+ gas, gasPrice *big.Int
+ value *big.Int
+ data []byte
+}
+
+// accessor boilerplate to implement core.Message
+func (m callmsg) From() []byte { return m.from.Address() }
+func (m callmsg) Nonce() uint64 { return m.from.Nonce() }
+func (m callmsg) To() []byte { return m.to }
+func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
+func (m callmsg) Gas() *big.Int { return m.gas }
+func (m callmsg) Value() *big.Int { return m.value }
+func (m callmsg) Data() []byte { return m.data }