This commit is contained in:
Taylor Gerring 2015-03-11 16:36:38 +00:00
commit 18f351eea1
59 changed files with 4039 additions and 3916 deletions

4
Godeps/Godeps.json generated
View file

@ -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",

View file

@ -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)
}

View file

@ -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

View file

@ -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 {

View file

@ -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)

View file

@ -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()
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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,103 +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(&eth.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.StartRpc(ethereum, RpcListenAddress, RpcPort)
utils.StartEthereum(ethereum)
ethereum.ChainManager().ResetWithGenesisBlock(blocks[0])
// fmt.Println("HURR: ", hex.EncodeToString(ethutil.Encode(blocks[0].RlpData())))
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) {
@ -207,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
}
@ -254,6 +187,9 @@ func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) {
}
gb := types.NewBlockWithHeader(gbh)
//gb.uncles = *new([]*types.Header)
//gb.transactions = *new(types.Transactions)
gb.Td = new(big.Int)
gb.Reward = new(big.Int)
testBlock := new(types.Block)
@ -272,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"))
}

View file

@ -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()

View file

@ -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 := eth.AccountManager()
// 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
}

View file

@ -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"))

View file

@ -35,7 +35,7 @@
var web3 = require('web3');
var eth = web3.eth;
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8545'));
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));
var desc = [{
"name": "balance(address)",
"type": "function",
@ -72,12 +72,13 @@
// 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;
var contract = web3.eth.contract(address, desc);
var Contract = web3.eth.contract(desc);
contract = new Contract(address);
contract.Changed({from: eth.coinbase}).changed(function() {
refresh();
});
@ -88,7 +89,7 @@
var table = document.querySelector("#table_body");
table.innerHTML = ""; // clear
var storage = eth.storageAt(address);
var storage = eth.getStorage(address);
table.innerHTML = "";
for( var item in storage ) {
table.innerHTML += "<tr><td>"+item+"</td><td>"+web3.toDecimal(storage[item])+"</td></tr>";

File diff suppressed because it is too large Load diff

View file

@ -190,17 +190,15 @@ ApplicationWindow {
}
}
MenuItem {
text: "Generate key"
shortcut: "Ctrl+k"
onTriggered: gui.generateKey()
}
}
Menu {
title: "Developer"
MenuItem {
iconSource: "../icecream.png"
text: "Debugger"
shortcut: "Ctrl+d"
onTriggered: eth.startDebugger()
}
MenuItem {
text: "Import Tx"
onTriggered: {
@ -756,24 +754,6 @@ ApplicationWindow {
}
}
Rectangle {
height: 55
color: "transparent"
visible: true
Text {
text: "DEBUG"
font.family: sourceSansPro.name
font.weight: Font.DemiBold
anchors {
left: parent.left
top: parent.verticalCenter
leftMargin: 16
}
color: "#AAA0A0"
}
}
ColumnLayout {
id: menuLegacy
visible: true

View file

@ -178,7 +178,6 @@ Rectangle {
}
function showContractData(tx) {
txDetailsDebugButton.tx = tx
if(tx.createsContract) {
contractData.text = tx.data
contractLabel.text = "<h4> Transaction created contract " + tx.address + "</h4>"
@ -202,22 +201,6 @@ Rectangle {
id: contractLabel
anchors.leftMargin: 10
}
Button {
property var tx
id: txDetailsDebugButton
anchors.right: parent.right
anchors.rightMargin: 10
anchors.top: parent.top
anchors.topMargin: 10
text: "Debug contract"
onClicked: {
if(tx && tx.createsContract){
eth.startDbWithCode(tx.rawData)
}else {
eth.startDbWithContractAndData(tx.address, tx.rawData)
}
}
}
TextArea {
id: contractData
text: "Contract"

View file

@ -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 {

View file

@ -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

View file

@ -1,367 +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 (
"fmt"
"math/big"
"strconv"
"strings"
"unicode"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/vm"
"github.com/obscuren/qml"
)
type DebuggerWindow struct {
win *qml.Window
engine *qml.Engine
lib *UiLib
vm *vm.Vm
Db *Debugger
state *state.StateDB
}
func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
engine := qml.NewEngine()
component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml"))
if err != nil {
fmt.Println(err)
return nil
}
win := component.CreateWindow(nil)
w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.Vm{}}
w.Db = NewDebugger(w)
return w
}
func (self *DebuggerWindow) Show() {
context := self.engine.Context()
context.SetVar("dbg", self)
go func() {
self.win.Show()
self.win.Wait()
}()
}
func (self *DebuggerWindow) SetCode(code string) {
self.win.Set("codeText", code)
}
func (self *DebuggerWindow) SetData(data string) {
self.win.Set("dataText", data)
}
func (self *DebuggerWindow) SetAsm(data []byte) {
self.win.Root().Call("clearAsm")
dis := core.Disassemble(data)
for _, str := range dis {
self.win.Root().Call("setAsm", str)
}
}
func (self *DebuggerWindow) Compile(code string) {
var err error
script := ethutil.StringToByteFunc(code, func(s string) (ret []byte) {
ret, err = ethutil.Compile(s, true)
return
})
if err == nil {
self.SetAsm(script)
}
}
// Used by QML
func (self *DebuggerWindow) AutoComp(code string) {
if self.Db.done {
self.Compile(code)
}
}
func (self *DebuggerWindow) ClearLog() {
self.win.Root().Call("clearLog")
}
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
self.Stop()
defer func() {
if r := recover(); r != nil {
self.Logf("compile FAULT: %v", r)
}
}()
data := utils.FormatTransactionData(dataStr)
var err error
script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
ret, err = ethutil.Compile(s, false)
return
})
if err != nil {
self.Logln(err)
return
}
var (
gas = ethutil.Big(gasStr)
gasPrice = ethutil.Big(gasPriceStr)
value = ethutil.Big(valueStr)
// Contract addr as test address
keyPair = self.lib.eth.KeyManager().KeyPair()
)
statedb := self.lib.eth.ChainManager().TransState()
account := self.lib.eth.ChainManager().TransState().GetAccount(keyPair.Address())
contract := statedb.NewStateObject([]byte{0})
contract.SetCode(script)
contract.SetBalance(value)
self.SetAsm(script)
block := self.lib.eth.ChainManager().CurrentBlock()
msg := types.NewTransactionMessage(nil, value, gas, gasPrice, data)
env := core.NewEnv(statedb, self.lib.eth.ChainManager(), msg, block)
self.Logf("callsize %d", len(script))
go func() {
pgas := new(big.Int).Set(gas)
ret, err := env.Call(account, contract.Address(), data, gas, gasPrice, ethutil.Big0)
rgas := new(big.Int).Sub(pgas, gas)
tot := new(big.Int).Mul(rgas, gasPrice)
self.Logf("gas usage %v total price = %v (%v)", rgas, tot, ethutil.CurrencyToString(tot))
if err != nil {
self.Logln("exited with errors:", err)
} else {
if len(ret) > 0 {
self.Logf("exited: % x", ret)
} else {
self.Logf("exited: nil")
}
}
statedb.Reset()
if !self.Db.interrupt {
self.Db.done = true
} else {
self.Db.interrupt = false
}
}()
}
func (self *DebuggerWindow) Logf(format string, v ...interface{}) {
self.win.Root().Call("setLog", fmt.Sprintf(format, v...))
}
func (self *DebuggerWindow) Logln(v ...interface{}) {
str := fmt.Sprintln(v...)
self.Logf("%s", str[:len(str)-1])
}
func (self *DebuggerWindow) Next() {
self.Db.Next()
}
func (self *DebuggerWindow) Continue() {
self.vm.Stepping = false
self.Next()
}
func (self *DebuggerWindow) Stop() {
if !self.Db.done {
self.Db.Q <- true
}
}
func (self *DebuggerWindow) ExecCommand(command string) {
if len(command) > 0 {
cmd := strings.Split(command, " ")
switch cmd[0] {
case "help":
self.Logln("Debugger commands:")
self.Logln("break, bp Set breakpoint on instruction")
self.Logln("clear [log, break, bp] Clears previous set sub-command(s)")
case "break", "bp":
if len(cmd) > 1 {
lineNo, err := strconv.Atoi(cmd[1])
if err != nil {
self.Logln(err)
break
}
self.Db.breakPoints = append(self.Db.breakPoints, int64(lineNo))
self.Logf("break point set on instruction %d", lineNo)
} else {
self.Logf("'%s' requires line number", cmd[0])
}
case "clear":
if len(cmd) > 1 {
switch cmd[1] {
case "break", "bp":
self.Db.breakPoints = nil
self.Logln("Breakpoints cleared")
case "log":
self.ClearLog()
default:
self.Logf("clear '%s' is not valid", cmd[1])
}
} else {
self.Logln("'clear' requires sub command")
}
default:
self.Logf("Unknown command %s", cmd[0])
}
}
}
type Debugger struct {
N chan bool
Q chan bool
done, interrupt bool
breakPoints []int64
main *DebuggerWindow
win *qml.Window
}
func NewDebugger(main *DebuggerWindow) *Debugger {
db := &Debugger{make(chan bool), make(chan bool), true, false, nil, main, main.win}
return db
}
type storeVal struct {
Key, Value string
}
func (self *Debugger) Step(evm vm.VirtualMachine, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, context *vm.Context) {
}
func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
self.main.Logln("break on instr:", pc)
return self.halting(pc, op, mem, stack, stateObject)
}
func (self *Debugger) StepHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
return self.halting(pc, op, mem, stack, stateObject)
}
func (self *Debugger) SetCode(byteCode []byte) {
self.main.SetAsm(byteCode)
}
func (self *Debugger) BreakPoints() []int64 {
return self.breakPoints
}
func (d *Debugger) halting(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
d.win.Root().Call("setInstruction", pc)
d.win.Root().Call("clearMem")
d.win.Root().Call("clearStack")
d.win.Root().Call("clearStorage")
addr := 0
for i := 0; i+16 <= mem.Len(); i += 16 {
dat := mem.Data()[i : i+16]
var str string
for _, d := range dat {
if unicode.IsGraphic(rune(d)) {
str += string(d)
} else {
str += "?"
}
}
d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s % x", str, dat)})
addr += 16
}
for _, val := range stack.Data() {
d.win.Root().Call("setStack", val.String())
}
it := stateObject.Trie().Iterator()
for it.Next() {
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", it.Key), fmt.Sprintf("% x", it.Value)})
}
stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32))
psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64())
d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`<b>stack ptr</b>: %v`, stackFrameAt))
d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`<b>stack size</b>: %d`, psize))
d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`<b>mem size</b>: %v`, mem.Len()))
out:
for {
select {
case <-d.N:
break out
case <-d.Q:
d.interrupt = true
d.clearBuffers()
return false
}
}
return true
}
func (d *Debugger) clearBuffers() {
out:
// drain
for {
select {
case <-d.N:
case <-d.Q:
default:
break out
}
}
}
func (d *Debugger) Next() {
if !d.done {
d.N <- true
}
}

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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(&eth.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
})
}

View file

@ -48,8 +48,6 @@ type UiLib struct {
assetPath string
// The main application window
win *qml.Window
Db *Debugger
DbWindow *DebuggerWindow
jsEngine *javascript.JSRE
@ -126,33 +124,11 @@ func (ui *UiLib) AssetPath(p string) string {
return path.Join(ui.assetPath, p)
}
func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
dbWindow := NewDebuggerWindow(self)
object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
if len(object.Code()) > 0 {
dbWindow.SetCode(ethutil.Bytes2Hex(object.Code()))
}
dbWindow.SetData(data)
dbWindow.Show()
}
func (self *UiLib) StartDbWithCode(code string) {
dbWindow := NewDebuggerWindow(self)
dbWindow.SetCode(code)
dbWindow.Show()
}
func (self *UiLib) StartDebugger() {
dbWindow := NewDebuggerWindow(self)
dbWindow.Show()
}
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"],

View file

@ -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 {
@ -133,45 +122,13 @@ func StartEthereum(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 StartEthereumForTest(ethereum *eth.Ethereum) {
clilogger.Infoln("Starting ", ethereum.Name())
ethereum.StartForTest()
RegisterInterrupt(func(sig os.Signal) {
ethereum.Stop()
logger.Flush()
})
}
func FormatTransactionData(data string) []byte {

View file

@ -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(&eth.Config{
func GetEthereum(clientID, version string, ctx *cli.Context) (*eth.Ethereum, error) {
return eth.New(&eth.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,22 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database, ethutil.D
}
return core.NewChainManager(blockDb, stateDb, new(event.TypeMux)), blockDb, stateDb
}
func GetAccountManager(ctx *cli.Context) *accounts.Manager {
dataDir := ctx.GlobalString(DataDirFlag.Name)
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))
}

View file

@ -80,7 +80,7 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated
cb := statedb.GetStateObject(coinbase.Address())
st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb)
_, err := st.TransitionState()
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) {
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
return nil, nil, err
}
@ -120,17 +120,12 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state
for _, tx := range txs {
receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess)
if err != nil {
switch {
case IsNonceErr(err):
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
return nil, nil, nil, nil, err
case state.IsGasLimitErr(err):
return nil, nil, nil, nil, err
default:
statelogger.Infoln(err)
erroneous = append(erroneous, tx)
err = nil
}
if err != nil {
statelogger.Infoln("TX err:", err)
}
receipts = append(receipts, receipt)
handled = append(handled, tx)
@ -261,7 +256,7 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
}
if block.Time <= parent.Time {
return ValidationError("Block timestamp not after or equal to prev block (%v - %v)", block.Time, parent.Time)
return ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time)
}
if int64(block.Time) > time.Now().Unix() {

View file

@ -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}

View file

@ -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

View file

@ -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")

View file

@ -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

View file

@ -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 {
@ -186,6 +184,7 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) {
}
}
if err = self.UseGas(big.NewInt(dgas)); err != nil {
println("2")
return nil, InvalidTxError(err)
}
@ -200,48 +199,18 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) {
dataGas.Mul(dataGas, vm.GasCreateByte)
if err := self.UseGas(dataGas); err == nil {
ref.SetCode(ret)
} else {
statelogger.Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas)
}
}
/*
if vmenv, ok := vmenv.(*VMEnv); ok && tryJit {
statelogger.Infof("CREATE: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4])
// re-run using the JIT (validation for the JIT)
goodState := vmenv.State().Copy()
vmenv.state = stateCopy
vmenv.SetVmType(vm.JitVmTy)
vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4])
self.state.Set(goodState)
}
*/
} else {
ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
/*
if vmenv, ok := vmenv.(*VMEnv); ok && tryJit {
statelogger.Infof("CALL: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4])
// re-run using the JIT (validation for the JIT)
goodState := vmenv.State().Copy()
vmenv.state = stateCopy
vmenv.SetVmType(vm.JitVmTy)
vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4])
self.state.Set(goodState)
}
*/
}
if err != nil && IsValueTransferErr(err) {
return nil, InvalidTxError(err)
}
/*
if err != nil {
self.UseGas(self.gas)
}
*/
return
}

View file

@ -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))
}

View file

@ -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{

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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())
// // }
// }

View file

@ -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,10 +256,21 @@ 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
}
func (s *Ethereum) StartForTest() {
jsonlogger.LogJson(&logger.LogStarting{
ClientString: s.net.Name,
ProtocolVersion: ProtocolVersion,
})
// Start services
s.txPool.Start()
s.blockPool.Start()
}
func (self *Ethereum) SuggestPeer(nodeURL string) error {
n, err := discover.ParseNode(nodeURL)
if err != nil {
@ -292,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()
@ -303,7 +295,7 @@ func (s *Ethereum) Stop() {
s.whisper.Stop()
}
ethlogger.Infoln("Server stopped")
servlogger.Infoln("Server stopped")
close(s.shutdownChan)
}

View file

@ -7,8 +7,6 @@ import (
)
func TestCompression(t *testing.T) {
ethutil.ReadConfig("", "/tmp", "")
db, err := NewLDBDatabase("testdb")
if err != nil {
t.Fatal(err)

View file

@ -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"

View file

@ -58,7 +58,7 @@ type JSEthereum struct {
func (self *JSEthereum) Block(v interface{}) otto.Value {
if number, ok := v.(int64); ok {
return self.toVal(&JSBlock{self.XEth.BlockByNumber(int32(number)), self})
return self.toVal(&JSBlock{self.XEth.BlockByNumber(number), self})
} else if hash, ok := v.(string); ok {
return self.toVal(&JSBlock{self.XEth.BlockByHash(hash), self})
}
@ -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)

View file

@ -209,6 +209,8 @@ gasLimit:
err := self.commitTransaction(tx)
switch {
case core.IsNonceErr(err):
fallthrough
case core.IsInvalidTxErr(err):
// Remove invalid transactions
remove = append(remove, tx)
case state.IsGasLimitErr(err):
@ -222,7 +224,7 @@ gasLimit:
}
self.eth.TxPool().RemoveSet(remove)
self.current.coinbase.AddBalance(core.BlockReward)
self.current.state.AddBalance(self.coinbase, core.BlockReward)
self.current.state.Update(ethutil.Big0)
self.push()
@ -258,9 +260,11 @@ func (self *worker) commitUncle(uncle *types.Header) error {
}
func (self *worker) commitTransaction(tx *types.Transaction) error {
snap := self.current.state.Copy()
//fmt.Printf("proc %x %v\n", tx.Hash()[:3], tx.Nonce())
receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true)
if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err)) {
if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err) || core.IsInvalidTxErr(err)) {
self.current.state.Set(snap)
return err
}

View file

@ -1,15 +1,10 @@
/*
For each request type, define the following:
1. RpcRequest "To" method [message.go], which does basic validation and conversion to "Args" type via json.Decoder()
2. json.Decoder() calls "UnmarshalON" defined on each "Args" struct
3. EthereumApi method, taking the "Args" type and replying with an interface to be marshalled to ON
*/
package rpc
import (
"encoding/json"
"fmt"
"math/big"
"path"
"strings"
"sync"
"time"
@ -26,8 +21,8 @@ import (
)
var (
defaultGasPrice = big.NewInt(10000000000000)
defaultGas = big.NewInt(10000)
defaultGasPrice = big.NewInt(150000000000)
defaultGas = big.NewInt(500000)
filterTickerTime = 15 * time.Second
)
@ -49,12 +44,10 @@ type EthereumApi struct {
register map[string][]*NewTxArgs
db ethutil.Database
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(),
@ -63,7 +56,6 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
logs: make(map[int]*logFilter),
messages: make(map[int]*whisperFilter),
db: db,
defaultBlockAge: -1,
}
go api.filterManager.Start()
go api.start()
@ -71,36 +63,33 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
return api
}
func (self *EthereumApi) setStateByBlockNumber(num int64) {
func (self *EthereumApi) xethWithStateNum(num int64) *xeth.XEth {
chain := self.xeth().Backend().ChainManager()
var block *types.Block
if self.defaultBlockAge < 0 {
if num < 0 {
num = chain.CurrentBlock().Number().Int64() + num + 1
}
block = chain.GetBlockByNumber(uint64(num))
var st *state.StateDB
if block != nil {
self.useState(state.New(block.Root(), self.xeth().Backend().StateDb()))
st = state.New(block.Root(), self.xeth().Backend().StateDb())
} else {
self.useState(chain.State())
st = chain.State()
}
return self.xeth().WithState(st)
}
func (self *EthereumApi) getStateWithNum(num int64) *xeth.State {
return self.xethWithStateNum(num).State()
}
func (self *EthereumApi) start() {
timer := time.NewTicker(filterTickerTime)
events := self.mux.Subscribe(core.ChainEvent{})
done:
for {
select {
case ev := <-events.Chan():
switch ev.(type) {
case core.ChainEvent:
if self.defaultBlockAge < 0 {
self.setStateByBlockNumber(self.defaultBlockAge)
}
}
case <-timer.C:
self.logMut.Lock()
self.messagesMut.Lock()
@ -129,35 +118,35 @@ func (self *EthereumApi) stop() {
close(self.quit)
}
func (self *EthereumApi) Register(args string, reply *interface{}) error {
self.regmut.Lock()
defer self.regmut.Unlock()
// func (self *EthereumApi) Register(args string, reply *interface{}) error {
// self.regmut.Lock()
// defer self.regmut.Unlock()
if _, ok := self.register[args]; ok {
self.register[args] = nil // register with empty
}
return nil
}
// if _, ok := self.register[args]; ok {
// self.register[args] = nil // register with empty
// }
// return nil
// }
func (self *EthereumApi) Unregister(args string, reply *interface{}) error {
self.regmut.Lock()
defer self.regmut.Unlock()
// func (self *EthereumApi) Unregister(args string, reply *interface{}) error {
// self.regmut.Lock()
// defer self.regmut.Unlock()
delete(self.register, args)
// delete(self.register, args)
return nil
}
// return nil
// }
func (self *EthereumApi) WatchTx(args string, reply *interface{}) error {
self.regmut.Lock()
defer self.regmut.Unlock()
// func (self *EthereumApi) WatchTx(args string, reply *interface{}) error {
// self.regmut.Lock()
// defer self.regmut.Unlock()
txs := self.register[args]
self.register[args] = nil
// txs := self.register[args]
// self.register[args] = nil
*reply = txs
return nil
}
// *reply = txs
// return nil
// }
func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error {
var id int
@ -172,7 +161,7 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro
id = self.filterManager.InstallFilter(filter)
self.logs[id] = &logFilter{timeout: time.Now()}
*reply = id
*reply = i2hex(id)
return nil
}
@ -202,7 +191,7 @@ func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error
id = self.filterManager.InstallFilter(filter)
self.logs[id] = &logFilter{timeout: time.Now()}
*reply = id
*reply = i2hex(id)
return nil
}
@ -239,25 +228,7 @@ func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error
return nil
}
func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error {
// This seems a bit precarious Maybe worth splitting to discrete functions
if len(args.Hash) > 0 {
*reply = p.xeth().BlockByHash(args.Hash)
} else {
*reply = p.xeth().BlockByNumber(args.BlockNumber)
}
return nil
}
func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error {
if len(args.Gas) == 0 {
args.Gas = defaultGas.String()
}
if len(args.GasPrice) == 0 {
args.GasPrice = defaultGasPrice.String()
}
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)
@ -279,15 +250,27 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error {
p.register[ags.From] = append(p.register[args.From], args)
}
*/
result, _ := p.xeth().Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data)
*reply = result
//}
// TODO: align default values to have the same type, e.g. not depend on
// ethutil.Value conversions later on
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
}
return nil
}
func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error {
result, err := p.xeth().Call( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data)
result, err := p.xethWithStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
if err != nil {
return err
}
@ -296,23 +279,28 @@ func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error {
return nil
}
func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error {
err := args.requirementsPushTx()
if err != nil {
func (p *EthereumApi) GetBalance(args *GetBalanceArgs, reply *interface{}) error {
if err := args.requirements(); err != nil {
return err
}
result, _ := p.xeth().PushTx(args.Tx)
*reply = result
state := p.getStateWithNum(args.BlockNumber).SafeGet(args.Address)
*reply = toHex(state.Balance().Bytes())
return nil
}
func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error {
err := args.requirements()
if err != nil {
func (p *EthereumApi) GetStorage(args *GetStorageArgs, reply *interface{}) error {
if err := args.requirements(); err != nil {
return err
}
*reply = p.getStateWithNum(args.BlockNumber).SafeGet(args.Address).Storage()
return nil
}
state := p.xeth().State().SafeGet(args.Address)
func (p *EthereumApi) GetStorageAt(args *GetStorageAtArgs, reply *interface{}) error {
if err := args.requirements(); err != nil {
return err
}
state := p.getStateWithNum(args.BlockNumber).SafeGet(args.Address)
value := state.StorageString(args.Key)
var hx string
@ -328,115 +316,31 @@ func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error {
return nil
}
func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error {
err := args.requirements()
if err != nil {
return err
}
*reply = p.xeth().State().SafeGet(args.Address).Storage()
return nil
}
func (p *EthereumApi) GetPeerCount(reply *interface{}) error {
*reply = p.xeth().PeerCount()
return nil
}
func (p *EthereumApi) GetIsListening(reply *interface{}) error {
*reply = p.xeth().IsListening()
return nil
}
func (p *EthereumApi) GetCoinbase(reply *interface{}) error {
*reply = p.xeth().Coinbase()
return nil
}
func (p *EthereumApi) Accounts(reply *interface{}) error {
*reply = p.xeth().Accounts()
return nil
}
func (p *EthereumApi) GetIsMining(reply *interface{}) error {
*reply = p.xeth().IsMining()
return nil
}
func (p *EthereumApi) SetMining(shouldmine bool, reply *interface{}) error {
*reply = p.xeth().SetMining(shouldmine)
return nil
}
func (p *EthereumApi) GetDefaultBlockAge(reply *interface{}) error {
*reply = p.defaultBlockAge
return nil
}
func (p *EthereumApi) SetDefaultBlockAge(defaultBlockAge int64, reply *interface{}) error {
p.defaultBlockAge = defaultBlockAge
p.setStateByBlockNumber(p.defaultBlockAge)
*reply = true
return nil
}
func (p *EthereumApi) BlockNumber(reply *interface{}) error {
*reply = p.xeth().Backend().ChainManager().CurrentBlock().Number()
return nil
}
func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) error {
err := args.requirements()
if err != nil {
return err
}
*reply = p.xeth().TxCountAt(args.Address)
*reply = p.xethWithStateNum(args.BlockNumber).TxCountAt(args.Address)
return nil
}
func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *interface{}) error {
err := args.requirements()
if err != nil {
func (p *EthereumApi) GetData(args *GetDataArgs, reply *interface{}) error {
if err := args.requirements(); err != nil {
return err
}
state := p.xeth().State().SafeGet(args.Address)
*reply = toHex(state.Balance().Bytes())
return nil
}
func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error {
err := args.requirements()
if err != nil {
return err
}
*reply = p.xeth().CodeAt(args.Address)
*reply = p.xethWithStateNum(args.BlockNumber).CodeAt(args.Address)
return nil
}
func (p *EthereumApi) GetCompilers(reply *interface{}) error {
c := []string{"serpent"}
c := []string{""}
*reply = c
return nil
}
func (p *EthereumApi) CompileSerpent(script string, reply *interface{}) error {
res, err := ethutil.Compile(script, false)
if err != nil {
return err
}
*reply = res
return nil
}
func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error {
*reply = toHex(crypto.Sha3(fromHex(args.Data)))
return nil
}
func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error {
err := args.requirements()
if err != nil {
if err := args.requirements(); err != nil {
return err
}
@ -446,8 +350,7 @@ func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error {
}
func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error {
err := args.requirements()
if err != nil {
if err := args.requirements(); err != nil {
return err
}
@ -461,14 +364,18 @@ func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error {
return nil
}
func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) error {
func (p *EthereumApi) NewWhisperFilter(args *WhisperFilterArgs, reply *interface{}) error {
var id int
args.Fn = func(msg xeth.WhisperMessage) {
opts := new(xeth.Options)
opts.From = args.From
opts.To = args.To
opts.Topics = args.Topics
opts.Fn = func(msg xeth.WhisperMessage) {
p.messagesMut.Lock()
defer p.messagesMut.Unlock()
p.messages[id].add(msg) // = append(p.messages[id], msg)
}
id = p.xeth().Whisper().Watch(args)
id = p.xeth().Whisper().Watch(opts)
p.messages[id] = &whisperFilter{timeout: time.Now()}
*reply = id
return nil
@ -486,7 +393,7 @@ func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error {
}
func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error {
err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topic, args.Priority, args.Ttl)
err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
if err != nil {
return err
}
@ -505,199 +412,358 @@ func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error {
return nil
}
func (p *EthereumApi) GetBlockByHash(blockhash string, includetx bool) (*BlockRes, error) {
block := p.xeth().EthBlockByHash(blockhash)
br := NewBlockRes(block)
br.fullTx = includetx
return br, nil
}
func (p *EthereumApi) GetBlockByNumber(blocknum int64, includetx bool) (*BlockRes, error) {
block := p.xeth().EthBlockByNumber(blocknum)
br := NewBlockRes(block)
br.fullTx = includetx
return br, nil
}
func (p *EthereumApi) GetBlockTransactionCountByHash(blockhash string) (int64, error) {
block := p.xeth().EthBlockByHash(blockhash)
br := NewBlockRes(block)
return int64(len(br.Transactions)), nil
}
func (p *EthereumApi) GetBlockTransactionCountByNumber(blocknum int64) (int64, error) {
block := p.xeth().EthBlockByNumber(blocknum)
br := NewBlockRes(block)
return int64(len(br.Transactions)), nil
}
func (p *EthereumApi) GetBlockUncleCountByHash(blockhash string) (int64, error) {
block := p.xeth().EthBlockByHash(blockhash)
br := NewBlockRes(block)
return int64(len(br.Uncles)), nil
}
func (p *EthereumApi) GetBlockUncleCountByNumber(blocknum int64) (int64, error) {
block := p.xeth().EthBlockByNumber(blocknum)
br := NewBlockRes(block)
return int64(len(br.Uncles)), nil
}
func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
// Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC
rpclogger.DebugDetailf("%T %s", req.Params, req.Params)
rpclogger.Debugf("%s %s", req.Method, req.Params)
switch req.Method {
case "web3_sha3":
args := new(Sha3Args)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
*reply = toHex(crypto.Sha3(fromHex(args.Data)))
case "net_listening":
*reply = p.xeth().IsListening()
case "net_peerCount":
*reply = toHex(big.NewInt(int64(p.xeth().PeerCount())).Bytes())
case "eth_coinbase":
return p.GetCoinbase(reply)
case "eth_listening":
return p.GetIsListening(reply)
*reply = p.xeth().Coinbase()
case "eth_mining":
return p.GetIsMining(reply)
case "eth_setMining":
args, err := req.ToBoolArgs()
if err != nil {
return err
}
return p.SetMining(args, reply)
case "eth_defaultBlock":
return p.GetDefaultBlockAge(reply)
case "eth_setDefaultBlock":
args, err := req.ToIntArgs()
if err != nil {
return err
}
return p.SetDefaultBlockAge(int64(args), reply)
case "eth_peerCount":
return p.GetPeerCount(reply)
case "eth_number":
return p.BlockNumber(reply)
*reply = p.xeth().IsMining()
case "eth_gasPrice":
*reply = toHex(defaultGasPrice.Bytes())
case "eth_accounts":
return p.Accounts(reply)
case "eth_countAt":
args, err := req.ToGetTxCountArgs()
if err != nil {
*reply = p.xeth().Accounts()
case "eth_blockNumber":
*reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes())
case "eth_getBalance":
args := new(GetBalanceArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.GetTxCountAt(args, reply)
case "eth_codeAt":
args, err := req.ToGetCodeAtArgs()
if err != nil {
return p.GetBalance(args, reply)
case "eth_getStorage", "eth_storageAt":
args := new(GetStorageArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.GetCodeAt(args, reply)
case "eth_balanceAt":
args, err := req.ToGetBalanceArgs()
if err != nil {
return err
}
return p.GetBalanceAt(args, reply)
case "eth_stateAt":
args, err := req.ToGetStateArgs()
if err != nil {
return err
}
return p.GetStateAt(args, reply)
case "eth_storageAt":
args, err := req.ToStorageAtArgs()
if err != nil {
return p.GetStorage(args, reply)
case "eth_getStorageAt":
args := new(GetStorageAtArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.GetStorageAt(args, reply)
case "eth_blockByNumber", "eth_blockByHash":
args, err := req.ToGetBlockArgs()
case "eth_getTransactionCount":
args := new(GetTxCountArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.GetTxCountAt(args, reply)
case "eth_getBlockTransactionCountByHash":
args := new(GetBlockByHashArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockTransactionCountByHash(args.BlockHash)
if err != nil {
return err
}
return p.GetBlock(args, reply)
case "eth_transact":
args, err := req.ToNewTxArgs()
*reply = toHex(big.NewInt(v).Bytes())
case "eth_getBlockTransactionCountByNumber":
args := new(GetBlockByNumberArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockTransactionCountByNumber(args.BlockNumber)
if err != nil {
return err
}
*reply = toHex(big.NewInt(v).Bytes())
case "eth_getUncleCountByBlockHash":
args := new(GetBlockByHashArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockUncleCountByHash(args.BlockHash)
if err != nil {
return err
}
*reply = toHex(big.NewInt(v).Bytes())
case "eth_getUncleCountByBlockNumber":
args := new(GetBlockByNumberArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockUncleCountByNumber(args.BlockNumber)
if err != nil {
return err
}
*reply = toHex(big.NewInt(v).Bytes())
case "eth_getData":
args := new(GetDataArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.GetData(args, reply)
case "eth_sendTransaction", "eth_transact":
args := new(NewTxArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.Transact(args, reply)
case "eth_call":
args, err := req.ToNewTxArgs()
if err != nil {
args := new(NewTxArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.Call(args, reply)
case "eth_newFilter":
args, err := req.ToFilterArgs()
case "eth_flush":
return errNotImplemented
case "eth_getBlockByHash":
args := new(GetBlockByHashArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockByHash(args.BlockHash, args.Transactions)
if err != nil {
return err
}
*reply = v
case "eth_getBlockByNumber":
args := new(GetBlockByNumberArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockByNumber(args.BlockNumber, args.Transactions)
if err != nil {
return err
}
*reply = v
case "eth_getTransactionByHash":
return errNotImplemented
case "eth_getTransactionByBlockHashAndIndex":
args := new(HashIndexArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockByHash(args.BlockHash, true)
if err != nil {
return err
}
if args.Index > int64(len(v.Transactions)) || args.Index < 0 {
return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist")
}
*reply = v.Transactions[args.Index]
case "eth_getTransactionByBlockNumberAndIndex":
args := new(BlockNumIndexArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockByNumber(args.BlockNumber, true)
if err != nil {
return err
}
if args.Index > int64(len(v.Transactions)) || args.Index < 0 {
return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist")
}
*reply = v.Transactions[args.Index]
case "eth_getUncleByBlockHashAndIndex":
args := new(HashIndexArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockByHash(args.BlockHash, false)
if err != nil {
return err
}
if args.Index > int64(len(v.Uncles)) || args.Index < 0 {
return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist")
}
uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false)
if err != nil {
return err
}
*reply = uncle
case "eth_getUncleByBlockNumberAndIndex":
args := new(BlockNumIndexArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
v, err := p.GetBlockByNumber(args.BlockNumber, true)
if err != nil {
return err
}
if args.Index > int64(len(v.Uncles)) || args.Index < 0 {
return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist")
}
uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false)
if err != nil {
return err
}
*reply = uncle
case "eth_getCompilers":
return p.GetCompilers(reply)
case "eth_compileSolidity":
case "eth_compileLLL":
case "eth_compileSerpent":
return errNotImplemented
case "eth_newFilter":
args := new(FilterOptions)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.NewFilter(args, reply)
case "eth_newFilterString":
args, err := req.ToFilterStringArgs()
if err != nil {
case "eth_newBlockFilter":
args := new(FilterStringArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.NewFilterString(args, reply)
return p.NewFilterString(args.Word, reply)
case "eth_uninstallFilter":
args, err := req.ToUninstallFilterArgs()
if err != nil {
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.UninstallFilter(args, reply)
case "eth_changed":
args, err := req.ToIdArgs()
if err != nil {
return p.UninstallFilter(args.Id, reply)
case "eth_getFilterChanges":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.FilterChanged(args, reply)
case "eth_filterLogs":
args, err := req.ToIdArgs()
if err != nil {
return p.FilterChanged(args.Id, reply)
case "eth_getFilterLogs":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.Logs(args, reply)
case "eth_logs":
args, err := req.ToFilterArgs()
if err != nil {
return p.Logs(args.Id, reply)
case "eth_getLogs":
args := new(FilterOptions)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.AllLogs(args, reply)
case "eth_gasPrice":
*reply = toHex(defaultGasPrice.Bytes())
return nil
case "eth_register":
args, err := req.ToRegisterArgs()
if err != nil {
return err
}
return p.Register(args, reply)
case "eth_unregister":
args, err := req.ToRegisterArgs()
if err != nil {
return err
}
return p.Unregister(args, reply)
case "eth_watchTx":
args, err := req.ToWatchTxArgs()
if err != nil {
return err
}
return p.WatchTx(args, reply)
case "eth_compilers":
return p.GetCompilers(reply)
case "eth_serpent":
args, err := req.ToCompileArgs()
if err != nil {
return err
}
return p.CompileSerpent(args, reply)
case "web3_sha3":
args, err := req.ToSha3Args()
if err != nil {
return err
}
return p.Sha3(args, reply)
case "eth_getWork":
case "eth_submitWork":
return errNotImplemented
case "db_put":
args, err := req.ToDbPutArgs()
if err != nil {
args := new(DbArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.DbPut(args, reply)
case "db_get":
args, err := req.ToDbGetArgs()
if err != nil {
args := new(DbArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.DbGet(args, reply)
case "shh_newIdentity":
return p.NewWhisperIdentity(reply)
case "shh_newFilter":
args, err := req.ToWhisperFilterArgs()
if err != nil {
return err
}
return p.NewWhisperFilter(args, reply)
case "shh_changed":
args, err := req.ToIdArgs()
if err != nil {
return err
}
return p.MessagesChanged(args, reply)
case "shh_post":
args, err := req.ToWhisperPostArgs()
if err != nil {
args := new(WhisperMessageArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.WhisperPost(args, reply)
case "shh_haveIdentity":
args, err := req.ToWhisperHasIdentityArgs()
if err != nil {
case "shh_newIdentity":
return p.NewWhisperIdentity(reply)
case "shh_hasIdentity":
args := new(WhisperIdentityArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.HasWhisperIdentity(args, reply)
return p.HasWhisperIdentity(args.Identity, reply)
case "shh_newGroup":
case "shh_addToGroup":
return errNotImplemented
case "shh_newFilter":
args := new(WhisperFilterArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.NewWhisperFilter(args, reply)
case "shh_uninstallFilter":
return errNotImplemented
case "shh_getFilterChanges":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.MessagesChanged(args.Id, reply)
case "shh_getMessages":
args, err := req.ToIdArgs()
if err != nil {
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
return p.WhisperMessages(args, reply)
return p.WhisperMessages(args.Id, reply)
// case "eth_register":
// args, err := req.ToRegisterArgs()
// if err != nil {
// return err
// }
// return p.Register(args, reply)
// case "eth_unregister":
// args, err := req.ToRegisterArgs()
// if err != nil {
// return err
// }
// return p.Unregister(args, reply)
// case "eth_watchTx":
// args, err := req.ToWatchTxArgs()
// if err != nil {
// return err
// }
// return p.WatchTx(args, reply)
default:
return NewErrorWithMessage(errNotImplemented, req.Method)
}
@ -713,9 +779,38 @@ func (self *EthereumApi) xeth() *xeth.XEth {
return self.eth
}
func (self *EthereumApi) useState(statedb *state.StateDB) {
self.xethMu.Lock()
defer self.xethMu.Unlock()
func toFilterOptions(options *FilterOptions) core.FilterOptions {
var opts core.FilterOptions
self.eth = self.eth.UseState(statedb)
// Convert optional address slice/string to byte slice
if str, ok := options.Address.(string); ok {
opts.Address = [][]byte{fromHex(str)}
} else if slice, ok := options.Address.([]interface{}); ok {
bslice := make([][]byte, len(slice))
for i, addr := range slice {
if saddr, ok := addr.(string); ok {
bslice[i] = fromHex(saddr)
}
}
opts.Address = bslice
}
opts.Earliest = options.Earliest
opts.Latest = options.Latest
topics := make([][][]byte, len(options.Topics))
for i, topicDat := range options.Topics {
if slice, ok := topicDat.([]interface{}); ok {
topics[i] = make([][]byte, len(slice))
for j, topic := range slice {
topics[i][j] = fromHex(topic.(string))
}
} else if str, ok := topicDat.(string); ok {
topics[i] = make([][]byte, 1)
topics[i][0] = fromHex(str)
}
}
opts.Topics = topics
return opts
}

View file

@ -1,11 +1,29 @@
package rpc
import (
"encoding/json"
"sync"
"testing"
"time"
)
func TestWeb3Sha3(t *testing.T) {
jsonstr := `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`
expected := "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"
api := &EthereumApi{}
var req RpcRequest
json.Unmarshal([]byte(jsonstr), &req)
var response interface{}
_ = api.GetRequestReply(&req, &response)
if response.(string) != expected {
t.Errorf("Expected %s got %s", expected, response)
}
}
func TestFilterClose(t *testing.T) {
t.Skip()
api := &EthereumApi{

View file

@ -1,246 +1,331 @@
package rpc
import "encoding/json"
import (
"bytes"
"encoding/json"
"math/big"
import "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/ethutil"
)
type GetBlockArgs struct {
BlockNumber int32
Hash string
}
func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) {
argint, argstr := int32(0), ""
if err = json.Unmarshal(b, &argint); err == nil {
obj.BlockNumber = argint
return
}
if err = json.Unmarshal(b, &argstr); err == nil {
obj.Hash = argstr
return
}
func blockNumber(raw json.RawMessage, number *int64) (err error) {
var str string
if err = json.Unmarshal(raw, &str); err != nil {
return errDecodeArgs
}
switch str {
case "latest":
*number = -1
case "pending":
*number = 0
default:
*number = ethutil.String2Big(str).Int64()
}
return nil
}
type GetBlockByHashArgs struct {
BlockHash string
Transactions bool
}
func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return errDecodeArgs
}
if len(obj) < 1 {
return errArguments
}
argstr, ok := obj[0].(string)
if !ok {
return errDecodeArgs
}
args.BlockHash = argstr
if len(obj) > 1 {
args.Transactions = obj[1].(bool)
}
return nil
}
type GetBlockByNumberArgs struct {
BlockNumber int64
Transactions bool
}
func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return errDecodeArgs
}
if len(obj) < 1 {
return errArguments
}
if v, ok := obj[0].(float64); ok {
args.BlockNumber = int64(v)
} else {
args.BlockNumber = ethutil.Big(obj[0].(string)).Int64()
}
if len(obj) > 1 {
args.Transactions = obj[1].(bool)
}
return nil
}
type NewTxArgs struct {
From string `json:"from"`
To string `json:"to"`
Value string `json:"value"`
Gas string `json:"gas"`
GasPrice string `json:"gasPrice"`
Data string `json:"data"`
}
func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
// Data can be either specified as "data" or "code" :-/
var ext struct {
From string
To string
Value string
Gas string
GasPrice string
Value *big.Int
Gas *big.Int
GasPrice *big.Int
Data string
Code string
BlockNumber int64
}
if err = json.Unmarshal(b, &ext); err == nil {
if len(ext.Data) == 0 {
ext.Data = ext.Code
}
obj.From = ext.From
obj.To = ext.To
obj.Value = ext.Value
obj.Gas = ext.Gas
obj.GasPrice = ext.GasPrice
obj.Data = ext.Data
return
func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
var obj struct{ From, To, Value, Gas, GasPrice, Data string }
if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil {
return err
}
return errDecodeArgs
}
args.From = obj.From
args.To = obj.To
args.Value = ethutil.Big(obj.Value)
args.Gas = ethutil.Big(obj.Gas)
args.GasPrice = ethutil.Big(obj.GasPrice)
args.Data = obj.Data
type PushTxArgs struct {
Tx string `json:"tx"`
}
func (obj *PushTxArgs) UnmarshalJSON(b []byte) (err error) {
arg0 := ""
if err = json.Unmarshal(b, &arg0); err == nil {
obj.Tx = arg0
return
}
return errDecodeArgs
}
func (a *PushTxArgs) requirementsPushTx() error {
if a.Tx == "" {
return NewErrorWithMessage(errArguments, "PushTx requires a 'tx' as argument")
}
return nil
}
type GetStorageArgs struct {
Address string
BlockNumber int64
}
func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
if err = json.Unmarshal(b, &obj.Address); err != nil {
func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
return errDecodeArgs
}
return
return nil
}
func (a *GetStorageArgs) requirements() error {
if len(a.Address) == 0 {
return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument")
func (args *GetStorageArgs) requirements() error {
if len(args.Address) == 0 {
return NewErrorWithMessage(errArguments, "Address cannot be blank")
}
return nil
}
type GetStateArgs struct {
type GetStorageAtArgs struct {
Address string
Key string
BlockNumber int64
}
func (obj *GetStateArgs) UnmarshalJSON(b []byte) (err error) {
arg0 := ""
if err = json.Unmarshal(b, &arg0); err == nil {
obj.Address = arg0
return
func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) {
var obj []string
if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil {
return errDecodeArgs
}
if len(obj) < 2 {
return errDecodeArgs
}
func (a *GetStateArgs) requirements() error {
if a.Address == "" {
return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument")
args.Address = obj[0]
args.Key = obj[1]
return nil
}
if a.Key == "" {
return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'key' value as argument")
func (args *GetStorageAtArgs) requirements() error {
if len(args.Address) == 0 {
return NewErrorWithMessage(errArguments, "Address cannot be blank")
}
if len(args.Key) == 0 {
return NewErrorWithMessage(errArguments, "Key cannot be blank")
}
return nil
}
type GetTxCountArgs struct {
Address string `json:"address"`
Address string
BlockNumber int64
}
func (obj *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
arg0 := ""
if err = json.Unmarshal(b, &arg0); err == nil {
obj.Address = arg0
return
}
func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
return errDecodeArgs
}
func (a *GetTxCountArgs) requirements() error {
if a.Address == "" {
return NewErrorWithMessage(errArguments, "GetTxCountAt requires an 'address' value as argument")
return nil
}
func (args *GetTxCountArgs) requirements() error {
if len(args.Address) == 0 {
return NewErrorWithMessage(errArguments, "Address cannot be blank")
}
return nil
}
type GetBalanceArgs struct {
Address string
BlockNumber int64
}
func (obj *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
arg0 := ""
if err = json.Unmarshal(b, &arg0); err == nil {
obj.Address = arg0
return
}
func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
return errDecodeArgs
}
func (a *GetBalanceArgs) requirements() error {
if a.Address == "" {
return NewErrorWithMessage(errArguments, "GetBalanceAt requires an 'address' value as argument")
return nil
}
func (args *GetBalanceArgs) requirements() error {
if len(args.Address) == 0 {
return NewErrorWithMessage(errArguments, "Address cannot be blank")
}
return nil
}
type GetCodeAtArgs struct {
type GetDataArgs struct {
Address string
BlockNumber int64
}
func (obj *GetCodeAtArgs) UnmarshalJSON(b []byte) (err error) {
arg0 := ""
if err = json.Unmarshal(b, &arg0); err == nil {
obj.Address = arg0
return
}
func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil {
return errDecodeArgs
}
func (a *GetCodeAtArgs) requirements() error {
if a.Address == "" {
return NewErrorWithMessage(errArguments, "GetCodeAt requires an 'address' value as argument")
return nil
}
func (args *GetDataArgs) requirements() error {
if len(args.Address) == 0 {
return NewErrorWithMessage(errArguments, "Address cannot be blank")
}
return nil
}
type BlockNumIndexArgs struct {
BlockNumber int64
Index int64
}
type HashIndexArgs struct {
BlockHash string
Index int64
}
type Sha3Args struct {
Data string
}
func (obj *Sha3Args) UnmarshalJSON(b []byte) (err error) {
if err = json.Unmarshal(b, &obj.Data); err != nil {
return errDecodeArgs
func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewErrorWithMessage(errDecodeArgs, err.Error())
}
return
if len(obj) < 1 {
return errArguments
}
args.Data = obj[0].(string)
return nil
}
// type FilterArgs struct {
// FromBlock uint64
// ToBlock uint64
// Limit uint64
// Offset uint64
// Address string
// Topics []string
// }
// func (args *FilterArgs) UnmarshalJSON(b []byte) (err error) {
// var obj []struct {
// FromBlock string `json:"fromBlock"`
// ToBlock string `json:"toBlock"`
// Limit string `json:"limit"`
// Offset string `json:"offset"`
// Address string `json:"address"`
// Topics []string `json:"topics"`
// }
// if err = json.Unmarshal(b, &obj); err != nil {
// return errDecodeArgs
// }
// if len(obj) < 1 {
// return errArguments
// }
// args.FromBlock = uint64(ethutil.Big(obj[0].FromBlock).Int64())
// args.ToBlock = uint64(ethutil.Big(obj[0].ToBlock).Int64())
// args.Limit = uint64(ethutil.Big(obj[0].Limit).Int64())
// args.Offset = uint64(ethutil.Big(obj[0].Offset).Int64())
// args.Address = obj[0].Address
// args.Topics = obj[0].Topics
// return nil
// }
type FilterOptions struct {
Earliest int64
Latest int64
Address interface{}
Topic []interface{}
Topics []interface{}
Skip int
Max int
}
func toFilterOptions(options *FilterOptions) core.FilterOptions {
var opts core.FilterOptions
// Convert optional address slice/string to byte slice
if str, ok := options.Address.(string); ok {
opts.Address = [][]byte{fromHex(str)}
} else if slice, ok := options.Address.([]interface{}); ok {
bslice := make([][]byte, len(slice))
for i, addr := range slice {
if saddr, ok := addr.(string); ok {
bslice[i] = fromHex(saddr)
}
}
opts.Address = bslice
func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) {
var obj []struct {
FromBlock string `json:"fromBlock"`
ToBlock string `json:"toBlock"`
Limit string `json:"limit"`
Offset string `json:"offset"`
Address string `json:"address"`
Topics []interface{} `json:"topics"`
}
opts.Earliest = options.Earliest
opts.Latest = options.Latest
topics := make([][][]byte, len(options.Topic))
for i, topicDat := range options.Topic {
if slice, ok := topicDat.([]interface{}); ok {
topics[i] = make([][]byte, len(slice))
for j, topic := range slice {
topics[i][j] = fromHex(topic.(string))
}
} else if str, ok := topicDat.(string); ok {
topics[i] = make([][]byte, 1)
topics[i][0] = fromHex(str)
}
}
opts.Topics = topics
return opts
if err = json.Unmarshal(b, &obj); err != nil {
return errDecodeArgs
}
type FilterChangedArgs struct {
n int
if len(obj) < 1 {
return errArguments
}
args.Earliest = int64(ethutil.Big(obj[0].FromBlock).Int64())
args.Latest = int64(ethutil.Big(obj[0].ToBlock).Int64())
args.Max = int(ethutil.Big(obj[0].Limit).Int64())
args.Skip = int(ethutil.Big(obj[0].Offset).Int64())
args.Address = obj[0].Address
args.Topics = obj[0].Topics
return nil
}
// type FilterChangedArgs struct {
// n int
// }
type DbArgs struct {
Database string
@ -248,12 +333,32 @@ type DbArgs struct {
Value string
}
func (args *DbArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return errDecodeArgs
}
if len(obj) < 2 {
return errArguments
}
args.Database = obj[0].(string)
args.Key = obj[1].(string)
if len(obj) > 2 {
args.Value = obj[2].(string)
}
return nil
}
func (a *DbArgs) requirements() error {
if len(a.Database) == 0 {
return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Database' value as argument")
return NewErrorWithMessage(errArguments, "Database cannot be blank")
}
if len(a.Key) == 0 {
return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Key' value as argument")
return NewErrorWithMessage(errArguments, "Key cannot be blank")
}
return nil
}
@ -262,7 +367,140 @@ type WhisperMessageArgs struct {
Payload string
To string
From string
Topic []string
Topics []string
Priority uint32
Ttl uint32
}
func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) {
var obj []struct {
Payload string
To string
From string
Topics []string
Priority string
Ttl string
}
if err = json.Unmarshal(b, &obj); err != nil {
return errDecodeArgs
}
if len(obj) < 1 {
return errArguments
}
args.Payload = obj[0].Payload
args.To = obj[0].To
args.From = obj[0].From
args.Topics = obj[0].Topics
args.Priority = uint32(ethutil.Big(obj[0].Priority).Int64())
args.Ttl = uint32(ethutil.Big(obj[0].Ttl).Int64())
return nil
}
type CompileArgs struct {
Source string
}
func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return errDecodeArgs
}
if len(obj) > 0 {
args.Source = obj[0].(string)
}
return nil
}
type FilterStringArgs struct {
Word string
}
func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
var obj []string
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return errDecodeArgs
}
if len(obj) < 1 {
return errDecodeArgs
}
args.Word = obj[0]
return nil
}
type FilterIdArgs struct {
Id int
}
func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) {
var obj []string
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return errDecodeArgs
}
if len(obj) < 1 {
return errDecodeArgs
}
args.Id = int(ethutil.Big(obj[0]).Int64())
return nil
}
type WhisperIdentityArgs struct {
Identity string
}
func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) {
var obj []string
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return errDecodeArgs
}
if len(obj) < 1 {
return errDecodeArgs
}
args.Identity = obj[0]
return nil
}
type WhisperFilterArgs struct {
To string `json:"to"`
From string
Topics []string
}
func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
var obj []struct {
To string
From string
Topics []string
}
if err = json.Unmarshal(b, &obj); err != nil {
return errDecodeArgs
}
if len(obj) < 1 {
return errArguments
}
args.To = obj[0].To
args.From = obj[0].From
args.Topics = obj[0].Topics
return nil
}

434
rpc/args_test.go Normal file
View file

@ -0,0 +1,434 @@
package rpc
import (
"bytes"
"encoding/json"
"math/big"
"testing"
)
func TestSha3(t *testing.T) {
input := `["0x68656c6c6f20776f726c64"]`
expected := "0x68656c6c6f20776f726c64"
args := new(Sha3Args)
json.Unmarshal([]byte(input), &args)
if args.Data != expected {
t.Error("got %s expected %s", input, expected)
}
}
func TestGetBalanceArgs(t *testing.T) {
input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x1f"]`
expected := new(GetBalanceArgs)
expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
expected.BlockNumber = 31
args := new(GetBalanceArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if err := args.requirements(); err != nil {
t.Error(err)
}
if args.Address != expected.Address {
t.Errorf("Address should be %v but is %v", expected.Address, args.Address)
}
if args.BlockNumber != expected.BlockNumber {
t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber)
}
}
func TestGetBlockByHashArgs(t *testing.T) {
input := `["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true]`
expected := new(GetBlockByHashArgs)
expected.BlockHash = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
expected.Transactions = true
args := new(GetBlockByHashArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if args.BlockHash != expected.BlockHash {
t.Errorf("BlockHash should be %v but is %v", expected.BlockHash, args.BlockHash)
}
if args.Transactions != expected.Transactions {
t.Errorf("Transactions should be %v but is %v", expected.Transactions, args.Transactions)
}
}
func TestGetBlockByNumberArgs(t *testing.T) {
input := `["0x1b4", false]`
expected := new(GetBlockByNumberArgs)
expected.BlockNumber = 436
expected.Transactions = false
args := new(GetBlockByNumberArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if args.BlockNumber != expected.BlockNumber {
t.Errorf("BlockHash should be %v but is %v", expected.BlockNumber, args.BlockNumber)
}
if args.Transactions != expected.Transactions {
t.Errorf("Transactions should be %v but is %v", expected.Transactions, args.Transactions)
}
}
func TestNewTxArgs(t *testing.T) {
input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a000",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}]`
expected := new(NewTxArgs)
expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155"
expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675"
expected.Gas = big.NewInt(30400)
expected.GasPrice = big.NewInt(10000000000000)
expected.Value = big.NewInt(10000000000000)
expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
args := new(NewTxArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.From != args.From {
t.Errorf("From shoud be %#v but is %#v", expected.From, args.From)
}
if expected.To != args.To {
t.Errorf("To shoud be %#v but is %#v", expected.To, args.To)
}
if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 {
t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes())
}
if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 {
t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice)
}
if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 {
t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value)
}
if expected.Data != args.Data {
t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data)
}
}
func TestGetStorageArgs(t *testing.T) {
input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]`
expected := new(GetStorageArgs)
expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
expected.BlockNumber = -1
args := new(GetStorageArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if err := args.requirements(); err != nil {
t.Error(err)
}
if expected.Address != args.Address {
t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address)
}
if expected.BlockNumber != args.BlockNumber {
t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber)
}
}
func TestGetStorageAtArgs(t *testing.T) {
input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x0", "0x2"]`
expected := new(GetStorageAtArgs)
expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
expected.Key = "0x0"
expected.BlockNumber = 2
args := new(GetStorageAtArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if err := args.requirements(); err != nil {
t.Error(err)
}
if expected.Address != args.Address {
t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address)
}
if expected.Key != args.Key {
t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address)
}
if expected.BlockNumber != args.BlockNumber {
t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber)
}
}
func TestGetTxCountArgs(t *testing.T) {
input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]`
expected := new(GetTxCountArgs)
expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
expected.BlockNumber = -1
args := new(GetTxCountArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if err := args.requirements(); err != nil {
t.Error(err)
}
if expected.Address != args.Address {
t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address)
}
if expected.BlockNumber != args.BlockNumber {
t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber)
}
}
func TestGetDataArgs(t *testing.T) {
input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", "latest"]`
expected := new(GetDataArgs)
expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"
expected.BlockNumber = -1
args := new(GetDataArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if err := args.requirements(); err != nil {
t.Error(err)
}
if expected.Address != args.Address {
t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address)
}
if expected.BlockNumber != args.BlockNumber {
t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber)
}
}
func TestFilterOptions(t *testing.T) {
input := `[{
"fromBlock": "0x1",
"toBlock": "0x2",
"limit": "0x3",
"offset": "0x0",
"address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8",
"topics": ["0x12341234"]}]`
expected := new(FilterOptions)
expected.Earliest = 1
expected.Latest = 2
expected.Max = 3
expected.Skip = 0
expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"
// expected.Topics = []string{"0x12341234"}
args := new(FilterOptions)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.Earliest != args.Earliest {
t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest)
}
if expected.Latest != args.Latest {
t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest)
}
if expected.Max != args.Max {
t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max)
}
if expected.Skip != args.Skip {
t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip)
}
if expected.Address != args.Address {
t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address)
}
// if expected.Topics != args.Topics {
// t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic)
// }
}
func TestDbArgs(t *testing.T) {
input := `["0x74657374","0x6b6579","0x6d79537472696e67"]`
expected := new(DbArgs)
expected.Database = "0x74657374"
expected.Key = "0x6b6579"
expected.Value = "0x6d79537472696e67"
args := new(DbArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if err := args.requirements(); err != nil {
t.Error(err)
}
if expected.Database != args.Database {
t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database)
}
if expected.Key != args.Key {
t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key)
}
if expected.Value != args.Value {
t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value)
}
}
func TestWhisperMessageArgs(t *testing.T) {
input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2",
"topics": ["0x68656c6c6f20776f726c64"],
"payload":"0x68656c6c6f20776f726c64",
"ttl": "0x64",
"priority": "0x64"}]`
expected := new(WhisperMessageArgs)
expected.From = "0xc931d93e97ab07fe42d923478ba2465f2"
expected.To = ""
expected.Payload = "0x68656c6c6f20776f726c64"
expected.Priority = 100
expected.Ttl = 100
expected.Topics = []string{"0x68656c6c6f20776f726c64"}
args := new(WhisperMessageArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.From != args.From {
t.Errorf("From shoud be %#v but is %#v", expected.From, args.From)
}
if expected.To != args.To {
t.Errorf("To shoud be %#v but is %#v", expected.To, args.To)
}
if expected.Payload != args.Payload {
t.Errorf("Value shoud be %#v but is %#v", expected.Payload, args.Payload)
}
if expected.Ttl != args.Ttl {
t.Errorf("Ttl shoud be %#v but is %#v", expected.Ttl, args.Ttl)
}
if expected.Priority != args.Priority {
t.Errorf("Priority shoud be %#v but is %#v", expected.Priority, args.Priority)
}
// if expected.Topics != args.Topics {
// t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic)
// }
}
func TestFilterIdArgs(t *testing.T) {
input := `["0x7"]`
expected := new(FilterIdArgs)
expected.Id = 7
args := new(FilterIdArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.Id != args.Id {
t.Errorf("Id shoud be %#v but is %#v", expected.Id, args.Id)
}
}
func TestWhsiperFilterArgs(t *testing.T) {
input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": "0x34ag445g3455b34"}]`
expected := new(WhisperFilterArgs)
expected.From = ""
expected.To = "0x34ag445g3455b34"
expected.Topics = []string{"0x68656c6c6f20776f726c64"}
args := new(WhisperFilterArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.From != args.From {
t.Errorf("From shoud be %#v but is %#v", expected.From, args.From)
}
if expected.To != args.To {
t.Errorf("To shoud be %#v but is %#v", expected.To, args.To)
}
// if expected.Topics != args.Topics {
// t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics)
// }
}
func TestCompileArgs(t *testing.T) {
input := `["contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"]`
expected := new(CompileArgs)
expected.Source = `contract test { function multiply(uint a) returns(uint d) { return a * 7; } }`
args := new(CompileArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.Source != args.Source {
t.Errorf("Source shoud be %#v but is %#v", expected.Source, args.Source)
}
}
func TestFilterStringArgs(t *testing.T) {
input := `["pending"]`
expected := new(FilterStringArgs)
expected.Word = "pending"
args := new(FilterStringArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.Word != args.Word {
t.Errorf("Word shoud be %#v but is %#v", expected.Word, args.Word)
}
}
func TestWhisperIdentityArgs(t *testing.T) {
input := `["0xc931d93e97ab07fe42d923478ba2465f283"]`
expected := new(WhisperIdentityArgs)
expected.Identity = "0xc931d93e97ab07fe42d923478ba2465f283"
args := new(WhisperIdentityArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
if expected.Identity != args.Identity {
t.Errorf("Identity shoud be %#v but is %#v", expected.Identity, args.Identity)
}
}

52
rpc/http.go Normal file
View 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})
})
}

View file

@ -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)
}

View file

@ -17,12 +17,9 @@
package rpc
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/xeth"
)
var (
@ -36,7 +33,7 @@ type RpcRequest struct {
ID interface{} `json:"id"`
JsonRpc string `json:"jsonrpc"`
Method string `json:"method"`
Params []json.RawMessage `json:"params"`
Params json.RawMessage `json:"params"`
}
type RpcSuccessResponse struct {
@ -61,359 +58,30 @@ func NewErrorWithMessage(err error, msg string) error {
return fmt.Errorf("%s: %s", err.Error(), msg)
}
func (req *RpcRequest) ToSha3Args() (*Sha3Args, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(Sha3Args)
r := bytes.NewReader(req.Params[0])
if err := json.NewDecoder(r).Decode(args); err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToGetBlockArgs() (*GetBlockArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(GetBlockArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToNewTxArgs() (*NewTxArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(NewTxArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, NewErrorWithMessage(errDecodeArgs, err.Error())
}
return args, nil
}
func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(PushTxArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToGetStateArgs() (*GetStateArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(GetStateArgs)
// TODO need to pass both arguments
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToStorageAtArgs() (*GetStorageArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(GetStorageArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToGetTxCountArgs() (*GetTxCountArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(GetTxCountArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToGetBalanceArgs() (*GetBalanceArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(GetBalanceArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToGetCodeAtArgs() (*GetCodeAtArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(GetCodeAtArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToBoolArgs() (bool, error) {
if len(req.Params) < 1 {
return false, errArguments
}
var args bool
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return false, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToIntArgs() (int, error) {
if len(req.Params) < 1 {
return 0, errArguments
}
var args int
if err := json.Unmarshal(req.Params[0], &args); err != nil {
return 0, errArguments
}
return args, nil
}
func (req *RpcRequest) ToCompileArgs() (string, error) {
if len(req.Params) < 1 {
return "", errArguments
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToFilterArgs() (*FilterOptions, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
args := new(FilterOptions)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToFilterStringArgs() (string, error) {
if len(req.Params) < 1 {
return "", errArguments
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToUninstallFilterArgs() (int, error) {
if len(req.Params) < 1 {
return 0, errArguments
}
var args int
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return 0, errDecodeArgs
}
return args, nil
}
func (req *RpcRequest) ToFilterChangedArgs() (int, error) {
if len(req.Params) < 1 {
return 0, errArguments
}
var id int
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(&id)
if err != nil {
return 0, errDecodeArgs
}
return id, nil
}
func (req *RpcRequest) ToDbPutArgs() (*DbArgs, error) {
if len(req.Params) < 3 {
return nil, errArguments
}
var args DbArgs
err := json.Unmarshal(req.Params[0], &args.Database)
if err != nil {
return nil, NewErrorWithMessage(errDecodeArgs, err.Error())
}
err = json.Unmarshal(req.Params[1], &args.Key)
if err != nil {
return nil, NewErrorWithMessage(errDecodeArgs, err.Error())
}
err = json.Unmarshal(req.Params[2], &args.Value)
if err != nil {
return nil, NewErrorWithMessage(errDecodeArgs, err.Error())
}
return &args, nil
}
func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) {
if len(req.Params) < 2 {
return nil, errArguments
}
var args DbArgs
err := json.Unmarshal(req.Params[0], &args.Database)
if err != nil {
return nil, NewErrorWithMessage(errDecodeArgs, err.Error())
}
err = json.Unmarshal(req.Params[1], &args.Key)
if err != nil {
return nil, NewErrorWithMessage(errDecodeArgs, err.Error())
}
return &args, nil
}
func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
var args xeth.Options
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return nil, NewErrorWithMessage(errDecodeArgs, err.Error())
}
return &args, nil
}
func (req *RpcRequest) ToIdArgs() (int, error) {
if len(req.Params) < 1 {
return 0, errArguments
}
var id int
err := json.Unmarshal(req.Params[0], &id)
if err != nil {
return 0, errDecodeArgs
}
return id, nil
}
func (req *RpcRequest) ToWhisperPostArgs() (*WhisperMessageArgs, error) {
if len(req.Params) < 1 {
return nil, errArguments
}
var args WhisperMessageArgs
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return nil, err
}
return &args, nil
}
func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) {
if len(req.Params) < 1 {
return "", errArguments
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", err
}
return args, nil
}
func (req *RpcRequest) ToRegisterArgs() (string, error) {
if len(req.Params) < 1 {
return "", errArguments
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", err
}
return args, nil
}
func (req *RpcRequest) ToWatchTxArgs() (string, error) {
if len(req.Params) < 1 {
return "", errArguments
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", err
}
return args, nil
}
// func (req *RpcRequest) ToRegisterArgs() (string, error) {
// if len(req.Params) < 1 {
// return "", errArguments
// }
// var args string
// err := json.Unmarshal(req.Params, &args)
// if err != nil {
// return "", err
// }
// return args, nil
// }
// func (req *RpcRequest) ToWatchTxArgs() (string, error) {
// if len(req.Params) < 1 {
// return "", errArguments
// }
// var args string
// err := json.Unmarshal(req.Params, &args)
// if err != nil {
// return "", err
// }
// return args, nil
// }

212
rpc/responses.go Normal file
View file

@ -0,0 +1,212 @@
package rpc
import (
"encoding/json"
// "fmt"
"math/big"
"github.com/ethereum/go-ethereum/core/types"
)
type BlockRes struct {
fullTx bool
BlockNumber int64 `json:"number"`
BlockHash []byte `json:"hash"`
ParentHash []byte `json:"parentHash"`
Nonce []byte `json:"nonce"`
Sha3Uncles []byte `json:"sha3Uncles"`
LogsBloom []byte `json:"logsBloom"`
TransactionRoot []byte `json:"transactionRoot"`
StateRoot []byte `json:"stateRoot"`
Miner []byte `json:"miner"`
Difficulty int64 `json:"difficulty"`
TotalDifficulty int64 `json:"totalDifficulty"`
Size int64 `json:"size"`
ExtraData []byte `json:"extraData"`
GasLimit int64 `json:"gasLimit"`
MinGasPrice int64 `json:"minGasPrice"`
GasUsed int64 `json:"gasUsed"`
UnixTimestamp int64 `json:"timestamp"`
Transactions []*TransactionRes `json:"transactions"`
Uncles [][]byte `json:"uncles"`
}
func (b *BlockRes) MarshalJSON() ([]byte, error) {
var ext struct {
BlockNumber string `json:"number"`
BlockHash string `json:"hash"`
ParentHash string `json:"parentHash"`
Nonce string `json:"nonce"`
Sha3Uncles string `json:"sha3Uncles"`
LogsBloom string `json:"logsBloom"`
TransactionRoot string `json:"transactionRoot"`
StateRoot string `json:"stateRoot"`
Miner string `json:"miner"`
Difficulty string `json:"difficulty"`
TotalDifficulty string `json:"totalDifficulty"`
Size string `json:"size"`
ExtraData string `json:"extraData"`
GasLimit string `json:"gasLimit"`
MinGasPrice string `json:"minGasPrice"`
GasUsed string `json:"gasUsed"`
UnixTimestamp string `json:"timestamp"`
Transactions []interface{} `json:"transactions"`
Uncles []string `json:"uncles"`
}
// convert strict types to hexified strings
ext.BlockNumber = toHex(big.NewInt(b.BlockNumber).Bytes())
ext.BlockHash = toHex(b.BlockHash)
ext.ParentHash = toHex(b.ParentHash)
ext.Nonce = toHex(b.Nonce)
ext.Sha3Uncles = toHex(b.Sha3Uncles)
ext.LogsBloom = toHex(b.LogsBloom)
ext.TransactionRoot = toHex(b.TransactionRoot)
ext.StateRoot = toHex(b.StateRoot)
ext.Miner = toHex(b.Miner)
ext.Difficulty = toHex(big.NewInt(b.Difficulty).Bytes())
ext.TotalDifficulty = toHex(big.NewInt(b.TotalDifficulty).Bytes())
ext.Size = toHex(big.NewInt(b.Size).Bytes())
// ext.ExtraData = toHex(b.ExtraData)
ext.GasLimit = toHex(big.NewInt(b.GasLimit).Bytes())
// ext.MinGasPrice = toHex(big.NewInt(b.MinGasPrice).Bytes())
ext.GasUsed = toHex(big.NewInt(b.GasUsed).Bytes())
ext.UnixTimestamp = toHex(big.NewInt(b.UnixTimestamp).Bytes())
ext.Transactions = make([]interface{}, len(b.Transactions))
if b.fullTx {
for i, tx := range b.Transactions {
ext.Transactions[i] = tx
}
} else {
for i, tx := range b.Transactions {
ext.Transactions[i] = toHex(tx.Hash)
}
}
ext.Uncles = make([]string, len(b.Uncles))
for i, v := range b.Uncles {
ext.Uncles[i] = toHex(v)
}
return json.Marshal(ext)
}
func NewBlockRes(block *types.Block) *BlockRes {
if block == nil {
return &BlockRes{}
}
res := new(BlockRes)
res.BlockNumber = block.Number().Int64()
res.BlockHash = block.Hash()
res.ParentHash = block.ParentHash()
res.Nonce = block.Header().Nonce
res.Sha3Uncles = block.Header().UncleHash
res.LogsBloom = block.Bloom()
res.TransactionRoot = block.Header().TxHash
res.StateRoot = block.Root()
res.Miner = block.Header().Coinbase
res.Difficulty = block.Difficulty().Int64()
if block.Td != nil {
res.TotalDifficulty = block.Td.Int64()
}
res.Size = int64(block.Size())
// res.ExtraData =
res.GasLimit = block.GasLimit().Int64()
// res.MinGasPrice =
res.GasUsed = block.GasUsed().Int64()
res.UnixTimestamp = block.Time()
res.Transactions = make([]*TransactionRes, len(block.Transactions()))
for i, tx := range block.Transactions() {
v := NewTransactionRes(tx)
v.BlockHash = block.Hash()
v.BlockNumber = block.Number().Int64()
v.TxIndex = int64(i)
res.Transactions[i] = v
}
res.Uncles = make([][]byte, len(block.Uncles()))
for i, uncle := range block.Uncles() {
res.Uncles[i] = uncle.Hash()
}
return res
}
type TransactionRes struct {
Hash []byte `json:"hash"`
Nonce int64 `json:"nonce"`
BlockHash []byte `json:"blockHash,omitempty"`
BlockNumber int64 `json:"blockNumber,omitempty"`
TxIndex int64 `json:"transactionIndex,omitempty"`
From []byte `json:"from"`
To []byte `json:"to"`
Value int64 `json:"value"`
Gas int64 `json:"gas"`
GasPrice int64 `json:"gasPrice"`
Input []byte `json:"input"`
}
func (t *TransactionRes) MarshalJSON() ([]byte, error) {
var ext struct {
Hash string `json:"hash"`
Nonce string `json:"nonce"`
BlockHash string `json:"blockHash,omitempty"`
BlockNumber string `json:"blockNumber,omitempty"`
TxIndex string `json:"transactionIndex,omitempty"`
From string `json:"from"`
To string `json:"to"`
Value string `json:"value"`
Gas string `json:"gas"`
GasPrice string `json:"gasPrice"`
Input string `json:"input"`
}
ext.Hash = toHex(t.Hash)
ext.Nonce = toHex(big.NewInt(t.Nonce).Bytes())
ext.BlockHash = toHex(t.BlockHash)
ext.BlockNumber = toHex(big.NewInt(t.BlockNumber).Bytes())
ext.TxIndex = toHex(big.NewInt(t.TxIndex).Bytes())
ext.From = toHex(t.From)
ext.To = toHex(t.To)
ext.Value = toHex(big.NewInt(t.Value).Bytes())
ext.Gas = toHex(big.NewInt(t.Gas).Bytes())
ext.GasPrice = toHex(big.NewInt(t.GasPrice).Bytes())
ext.Input = toHex(t.Input)
return json.Marshal(ext)
}
func NewTransactionRes(tx *types.Transaction) *TransactionRes {
var v = new(TransactionRes)
v.Hash = tx.Hash()
v.Nonce = int64(tx.Nonce())
v.From = tx.From()
v.To = tx.To()
v.Value = tx.Value().Int64()
v.Gas = tx.Gas().Int64()
v.GasPrice = tx.GasPrice().Int64()
v.Input = tx.Data()
return v
}
type FilterLogRes struct {
Hash string `json:"hash"`
Address string `json:"address"`
Data string `json:"data"`
BlockNumber string `json:"blockNumber"`
TransactionHash string `json:"transactionHash"`
BlockHash string `json:"blockHash"`
TransactionIndex string `json:"transactionIndex"`
LogIndex string `json:"logIndex"`
}
type FilterWhisperRes struct {
Hash string `json:"hash"`
From string `json:"from"`
To string `json:"to"`
Expiry string `json:"expiry"`
Sent string `json:"sent"`
Ttl string `json:"ttl"`
Topics string `json:"topics"`
Payload string `json:"payload"`
WorkProved string `json:"workProved"`
}

View file

@ -18,8 +18,11 @@ package rpc
import (
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"reflect"
"time"
"github.com/ethereum/go-ethereum/ethutil"
@ -32,9 +35,63 @@ var rpclogger = logger.NewLogger("RPC")
type JsonWrapper struct{}
// Unmarshal state is a helper method which has the ability to decode messsages
// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter)
// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction
// message and the second one refers to the block height (or state) to which to apply this `call`.
func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error) {
var data []json.RawMessage
if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 {
return errDecodeArgs
}
// Number index determines the index in the array for a possible block number
numberIndex := 0
value := reflect.ValueOf(iface)
rvalue := reflect.Indirect(value)
switch rvalue.Kind() {
case reflect.Slice:
// This is a bit of a cheat, but `data` is expected to be larger than 2 if iface is a slice
if number != nil {
numberIndex = len(data) - 1
} else {
numberIndex = len(data)
}
slice := reflect.MakeSlice(rvalue.Type(), numberIndex, numberIndex)
for i, raw := range data[0:numberIndex] {
v := slice.Index(i).Interface()
if err = json.Unmarshal(raw, &v); err != nil {
fmt.Println(err, v)
return err
}
slice.Index(i).Set(reflect.ValueOf(v))
}
reflect.Indirect(rvalue).Set(slice) //value.Set(slice)
case reflect.Struct:
fallthrough
default:
if err = json.Unmarshal(data[0], iface); err != nil {
return errDecodeArgs
}
numberIndex = 1
}
// <0 index means out of bound for block number
if numberIndex >= 0 && len(data) > numberIndex {
if err = blockNumber(data[numberIndex], number); err != nil {
return errDecodeArgs
}
}
return nil
}
func (self JsonWrapper) Send(writer io.Writer, v interface{}) (n int, err error) {
var payload []byte
payload, err = json.Marshal(v)
payload, err = json.MarshalIndent(v, "", "\t")
if err != nil {
rpclogger.Fatalln("Error marshalling JSON", err)
return 0, err
@ -63,18 +120,31 @@ func (self JsonWrapper) ParseRequestBody(req *http.Request) (RpcRequest, error)
}
func toHex(b []byte) string {
return "0x" + ethutil.Bytes2Hex(b)
hex := ethutil.Bytes2Hex(b)
// Prefer output of "0x0" instead of "0x"
if len(hex) == 0 {
hex = "0"
}
return "0x" + hex
}
func fromHex(s string) []byte {
if len(s) > 1 {
if s[0:2] == "0x" {
s = s[2:]
}
if len(s)%2 == 1 {
s = "0" + s
}
return ethutil.Hex2Bytes(s)
}
return nil
}
func i2hex(n int) string {
return toHex(big.NewInt(int64(n)).Bytes())
}
type RpcServer interface {
Start()
Stop()

25
rpc/util_test.go Normal file
View file

@ -0,0 +1,25 @@
package rpc
import (
"bytes"
"testing"
)
//fromHex
func TestFromHex(t *testing.T) {
input := "0x01"
expected := []byte{1}
result := fromHex(input)
if bytes.Compare(expected, result) != 0 {
t.Errorf("Expected % x got % x", expected, result)
}
}
func TestFromHexOddLength(t *testing.T) {
input := "0x1"
expected := []byte{1}
result := fromHex(input)
if bytes.Compare(expected, result) != 0 {
t.Errorf("Expected % x got % x", expected, result)
}
}

View file

@ -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)
}

View file

@ -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", "")
}

View file

@ -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
}

View file

@ -1 +0,0 @@
package vm

View file

@ -37,7 +37,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I
context := NewContext(caller, me, code, gas, price)
vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData)
self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData).Endl()
if self.Recoverable {
// Recover from any require exception
@ -696,7 +696,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I
if err != nil {
stack.push(ethutil.BigFalse)
vmlogger.Debugln(err)
self.Printf("%v").Endl()
} else {
stack.push(ethutil.BigTrue)
@ -726,7 +726,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I
return context.Return(nil), nil
default:
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl()
panic(fmt.Errorf("Invalid opcode %x", op))
}
@ -894,7 +894,7 @@ func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine {
func (self *Vm) Endl() VirtualMachine {
if self.debug {
if self.logTy == LogTyPretty {
vmlogger.Debugln(self.logStr)
vmlogger.Infoln(self.logStr)
self.logStr = ""
}
}

View file

@ -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)
}

View file

@ -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,42 +38,67 @@ 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
}
func (self *XEth) Backend() Backend { return self.eth }
func (self *XEth) UseState(statedb *state.StateDB) *XEth {
func (self *XEth) WithState(statedb *state.StateDB) *XEth {
xeth := &XEth{
eth: self.eth,
blockProcessor: self.blockProcessor,
@ -99,7 +122,14 @@ func (self *XEth) BlockByHash(strHash string) *Block {
return NewBlock(block)
}
func (self *XEth) BlockByNumber(num int32) *Block {
func (self *XEth) EthBlockByHash(strHash string) *types.Block {
hash := fromHex(strHash)
block := self.chainManager.GetBlock(hash)
return block
}
func (self *XEth) BlockByNumber(num int64) *Block {
if num == -1 {
return NewBlock(self.chainManager.CurrentBlock())
}
@ -107,20 +137,34 @@ func (self *XEth) BlockByNumber(num int32) *Block {
return NewBlock(self.chainManager.GetBlockByNumber(uint64(num)))
}
func (self *XEth) EthBlockByNumber(num int64) *types.Block {
if num == -1 {
return self.chainManager.CurrentBlock()
}
return self.chainManager.GetBlockByNumber(uint64(num))
}
func (self *XEth) Block(v interface{}) *Block {
if n, ok := v.(int32); ok {
return self.BlockByNumber(n)
return self.BlockByNumber(int64(n))
} else if str, ok := v.(string); ok {
return self.BlockByHash(str)
} else if f, ok := v.(float64); ok { // Don't ask ...
return self.BlockByNumber(int32(f))
return self.BlockByNumber(int64(f))
}
return nil
}
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 {
@ -147,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 {
@ -248,49 +293,48 @@ 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) {
if len(gasStr) == 0 {
gasStr = "100000"
}
if len(gasPriceStr) == 0 {
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)
defaultGasPrice = big.NewInt(10000000000000)
defaultGas = big.NewInt(90000)
)
msg := types.NewTransactionMessage(fromHex(toStr), value, gas, price, data)
msg.Sign(key.PrivateKey)
func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
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),
}
if msg.gas.Cmp(big.NewInt(0)) == 0 {
msg.gas = defaultGas
}
if msg.gasPrice.Cmp(big.NewInt(0)) == 0 {
msg.gasPrice = defaultGasPrice
}
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 {
@ -304,25 +348,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 }