Merge branch 'rpcfrontier' of github.com:ethereum/go-ethereum into rpcfrontier
This commit is contained in:
		
						commit
						6bca40274f
					
				
							
								
								
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @ -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", | ||||
|  | ||||
							
								
								
									
										31
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -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) | ||||
| } | ||||
|  | ||||
							
								
								
									
										3
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -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
 | ||||
|  | ||||
							
								
								
									
										2
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/command.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/command.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -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 { | ||||
|  | ||||
							
								
								
									
										13
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/help.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								Godeps/_workspace/src/github.com/codegangsta/cli/help.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -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) | ||||
|  | ||||
| @ -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 { | ||||
| type Manager struct { | ||||
| 	keyStore crypto.KeyStore2 | ||||
| 	unlockedKeys       map[string]crypto.Key | ||||
| 	unlockMilliseconds time.Duration | ||||
| 	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, | ||||
| 	} | ||||
| 	return *am | ||||
| type unlocked struct { | ||||
| 	*crypto.Key | ||||
| 	abort chan struct{} | ||||
| } | ||||
| 
 | ||||
| func (am AccountManager) DeleteAccount(address []byte, auth string) error { | ||||
| func NewManager(keyStore crypto.KeyStore2) *Manager { | ||||
| 	return &Manager{ | ||||
| 		keyStore: keyStore, | ||||
| 		unlocked: make(map[string]*unlocked), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
| 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 <-time.After(time.Millisecond * am.unlockMilliseconds): | ||||
| 	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() | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	am.Unlock(a1.Address, "") | ||||
| 
 | ||||
| 	// 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) | ||||
| 	_, err = am.Sign(a1, toSign) | ||||
| 	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) | ||||
| } | ||||
|  | ||||
| @ -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 <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Gustav Simonsson <gustav.simonsson@gmail.com> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	TestFile string | ||||
| ) | ||||
| 
 | ||||
| func Init() { | ||||
| 	flag.Usage = func() { | ||||
| 		fmt.Fprintf(os.Stderr, "%s <testfile>\n", os.Args[0]) | ||||
| 		flag.PrintDefaults() | ||||
| 	} | ||||
| 	flag.Parse() | ||||
| 
 | ||||
| 	TestFile = flag.Arg(0) | ||||
| } | ||||
| @ -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 <testfile>\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")) | ||||
| } | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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) | ||||
| 	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 { | ||||
| 		runREPL(eth) | ||||
| 		repl.interactive() | ||||
| 	} else { | ||||
| 		for _, file := range ctx.Args() { | ||||
| 			repl.exec(file) | ||||
| 		} | ||||
| 	} | ||||
| 	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.") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
|  | ||||
| @ -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")) | ||||
|  | ||||
| @ -72,7 +72,7 @@ | ||||
| 	// deploy if not exist | ||||
| 	if (address == null) { | ||||
| 		var code = "0x60056013565b61014f8061003a6000396000f35b620f42406000600033600160a060020a0316815260200190815260200160002081905550560060e060020a600035048063d0679d3414610020578063e3d670d71461003457005b61002e600435602435610049565b60006000f35b61003f600435610129565b8060005260206000f35b806000600033600160a060020a03168152602001908152602001600020541061007157610076565b610125565b806000600033600160a060020a03168152602001908152602001600020908154039081905550806000600084600160a060020a031681526020019081526020016000209081540190819055508033600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a38082600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a35b5050565b60006000600083600160a060020a0316815260200190815260200160002054905091905056"; | ||||
| 		address = web3.eth.transact({data: code}); | ||||
| 		address = web3.eth.transact({from: eth.coinbase, data: code}); | ||||
|         	localStorage.setItem("address", address); | ||||
| 	} | ||||
| 	document.querySelector("#contract_addr").innerHTML = address; | ||||
|  | ||||
| @ -190,6 +190,11 @@ ApplicationWindow { | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             MenuItem { | ||||
|                 text: "Generate key" | ||||
|                 shortcut: "Ctrl+k" | ||||
|                 onTriggered: gui.generateKey() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Menu { | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
| @ -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 <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| 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:<IP>)") | ||||
| 	) | ||||
| 	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) | ||||
| 	} | ||||
| } | ||||
| @ -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 | ||||
| 
 | ||||
| 	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) | ||||
|  | ||||
							
								
								
									
										104
									
								
								cmd/mist/main.go
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								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 | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @ -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"], | ||||
|  | ||||
| @ -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,14 +94,6 @@ 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 { | ||||
| @ -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) | ||||
|  | ||||
| @ -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{ | ||||
| func GetEthereum(clientID, version string, ctx *cli.Context) (*eth.Ethereum, error) { | ||||
| 	return 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), | ||||
| 		AccountManager: GetAccountManager(ctx), | ||||
| 		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), | ||||
| 	}) | ||||
| 	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)) | ||||
| } | ||||
|  | ||||
| @ -440,12 +440,14 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { | ||||
| 				self.setTotalDifficulty(td) | ||||
| 				self.insert(block) | ||||
| 
 | ||||
| 				/* XXX crashes | ||||
| 				jsonlogger.LogJson(&logger.EthChainNewHead{ | ||||
| 					BlockHash:     ethutil.Bytes2Hex(block.Hash()), | ||||
| 					BlockNumber:   block.Number(), | ||||
| 					ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()), | ||||
| 					BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()), | ||||
| 				}) | ||||
| 				*/ | ||||
| 
 | ||||
| 				self.setTransState(state.New(block.Root(), self.stateDb)) | ||||
| 				queue[i] = ChainEvent{block} | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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") | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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)) | ||||
| } | ||||
|  | ||||
| @ -26,7 +26,6 @@ package crypto | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 
 | ||||
| @ -87,18 +86,16 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { | ||||
| } | ||||
| 
 | ||||
| func NewKey(rand io.Reader) *Key { | ||||
| 	randBytes := make([]byte, 32) | ||||
| 	randBytes := make([]byte, 64) | ||||
| 	_, err := rand.Read(randBytes) | ||||
| 	if err != nil { | ||||
| 		panic("key generation: could not read from random source: " + err.Error()) | ||||
| 	} | ||||
| 	reader := bytes.NewReader(randBytes) | ||||
| 	_, x, y, err := elliptic.GenerateKey(S256(), reader) | ||||
| 	privateKeyECDSA, err := ecdsa.GenerateKey(S256(), reader) | ||||
| 	if err != nil { | ||||
| 		panic("key generation: elliptic.GenerateKey failed: " + err.Error()) | ||||
| 		panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) | ||||
| 	} | ||||
| 	privateKeyMarshalled := elliptic.Marshal(S256(), x, y) | ||||
| 	privateKeyECDSA := ToECDSA(privateKeyMarshalled) | ||||
| 
 | ||||
| 	id := uuid.NewRandom() | ||||
| 	key := &Key{ | ||||
|  | ||||
| @ -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) | ||||
| } | ||||
| @ -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) | ||||
| } | ||||
| @ -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 | ||||
| } | ||||
| @ -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())
 | ||||
| // 	// }
 | ||||
| // }
 | ||||
| @ -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 | ||||
| 
 | ||||
| @ -62,8 +60,7 @@ type Config struct { | ||||
| 	Dial bool | ||||
| 
 | ||||
| 	MinerThreads   int | ||||
| 
 | ||||
| 	KeyManager *crypto.KeyManager | ||||
| 	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,19 +153,6 @@ 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
 | ||||
| 
 | ||||
| @ -178,20 +160,24 @@ func New(config *Config) (*Ethereum, error) { | ||||
| 		shutdownChan:   make(chan bool), | ||||
| 		blockDb:        blockDb, | ||||
| 		stateDb:        stateDb, | ||||
| 		keyManager:   keyManager, | ||||
| 		eventMux:       &event.TypeMux{}, | ||||
| 		logger:       ethlogger, | ||||
| 		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) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -7,8 +7,6 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| func TestCompression(t *testing.T) { | ||||
| 	ethutil.ReadConfig("", "/tmp", "") | ||||
| 
 | ||||
| 	db, err := NewLDBDatabase("testdb") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										36
									
								
								rpc/api.go
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								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 | ||||
| 	} | ||||
| @ -556,7 +560,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error | ||||
| 			return err | ||||
| 		} | ||||
| 		return p.GetData(args, reply) | ||||
| 	case "eth_sendTransaction": | ||||
| 	case "eth_sendTransaction", "eth_transact": | ||||
| 		args := new(NewTxArgs) | ||||
| 		if err := json.Unmarshal(req.Params, &args); err != nil { | ||||
| 			return err | ||||
|  | ||||
							
								
								
									
										52
									
								
								rpc/http.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								rpc/http.go
									
									
									
									
									
										Normal file
									
								
							| @ -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}) | ||||
| 	}) | ||||
| } | ||||
| @ -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 <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 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) | ||||
| } | ||||
| @ -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) | ||||
| } | ||||
|  | ||||
| @ -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", "") | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
| @ -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) | ||||
| } | ||||
|  | ||||
							
								
								
									
										163
									
								
								xeth/xeth.go
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								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 | ||||
| 	res, err := vmenv.Call(msg.from, msg.to, msg.data, msg.gas, msg.gasPrice, msg.value) | ||||
| 	return toHex(res), err | ||||
| } | ||||
| 
 | ||||
| 	return toHex(res), nil | ||||
| } | ||||
| 
 | ||||
| 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 } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user