From e22bcb78a530d07ef63e8ba3e332199c7c860ac6 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 25 Mar 2015 16:50:30 +0100 Subject: [PATCH 01/25] Update response types + tests To coincide with recent type conversion --- rpc/responses.go | 137 ++++++++++++++++++++++++------------------ rpc/responses_test.go | 123 +++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 58 deletions(-) create mode 100644 rpc/responses_test.go diff --git a/rpc/responses.go b/rpc/responses.go index 993f467eaf..f5f3a33f3d 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -6,14 +6,14 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" ) type BlockRes struct { fullTx bool - BlockNumber int64 `json:"number"` + BlockNumber *big.Int `json:"number"` BlockHash common.Hash `json:"hash"` ParentHash common.Hash `json:"parentHash"` Nonce [8]byte `json:"nonce"` @@ -22,13 +22,13 @@ type BlockRes struct { TransactionRoot common.Hash `json:"transactionRoot"` StateRoot common.Hash `json:"stateRoot"` Miner common.Address `json:"miner"` - Difficulty int64 `json:"difficulty"` - TotalDifficulty int64 `json:"totalDifficulty"` - Size int64 `json:"size"` + Difficulty *big.Int `json:"difficulty"` + TotalDifficulty *big.Int `json:"totalDifficulty"` + Size *big.Int `json:"size"` ExtraData []byte `json:"extraData"` - GasLimit int64 `json:"gasLimit"` + GasLimit *big.Int `json:"gasLimit"` MinGasPrice int64 `json:"minGasPrice"` - GasUsed int64 `json:"gasUsed"` + GasUsed *big.Int `json:"gasUsed"` UnixTimestamp int64 `json:"timestamp"` Transactions []*TransactionRes `json:"transactions"` Uncles []common.Hash `json:"uncles"` @@ -58,7 +58,7 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { } // convert strict types to hexified strings - ext.BlockNumber = common.ToHex(big.NewInt(b.BlockNumber).Bytes()) + ext.BlockNumber = common.ToHex(b.BlockNumber.Bytes()) ext.BlockHash = b.BlockHash.Hex() ext.ParentHash = b.ParentHash.Hex() ext.Nonce = common.ToHex(b.Nonce[:]) @@ -67,13 +67,13 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { ext.TransactionRoot = b.TransactionRoot.Hex() ext.StateRoot = b.StateRoot.Hex() ext.Miner = b.Miner.Hex() - ext.Difficulty = common.ToHex(big.NewInt(b.Difficulty).Bytes()) - ext.TotalDifficulty = common.ToHex(big.NewInt(b.TotalDifficulty).Bytes()) - ext.Size = common.ToHex(big.NewInt(b.Size).Bytes()) + ext.Difficulty = common.ToHex(b.Difficulty.Bytes()) + ext.TotalDifficulty = common.ToHex(b.TotalDifficulty.Bytes()) + ext.Size = common.ToHex(b.Size.Bytes()) // ext.ExtraData = common.ToHex(b.ExtraData) - ext.GasLimit = common.ToHex(big.NewInt(b.GasLimit).Bytes()) + ext.GasLimit = common.ToHex(b.GasLimit.Bytes()) // ext.MinGasPrice = common.ToHex(big.NewInt(b.MinGasPrice).Bytes()) - ext.GasUsed = common.ToHex(big.NewInt(b.GasUsed).Bytes()) + ext.GasUsed = common.ToHex(b.GasUsed.Bytes()) ext.UnixTimestamp = common.ToHex(big.NewInt(b.UnixTimestamp).Bytes()) ext.Transactions = make([]interface{}, len(b.Transactions)) if b.fullTx { @@ -99,7 +99,7 @@ func NewBlockRes(block *types.Block) *BlockRes { } res := new(BlockRes) - res.BlockNumber = block.Number().Int64() + res.BlockNumber = block.Number() res.BlockHash = block.Hash() res.ParentHash = block.ParentHash() res.Nonce = block.Header().Nonce @@ -108,15 +108,13 @@ func NewBlockRes(block *types.Block) *BlockRes { 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.Difficulty = block.Difficulty() + res.TotalDifficulty = block.Td + res.Size = big.NewInt(int64(block.Size())) // res.ExtraData = - res.GasLimit = block.GasLimit().Int64() + res.GasLimit = block.GasLimit() // res.MinGasPrice = - res.GasUsed = block.GasUsed().Int64() + res.GasUsed = block.GasUsed() res.UnixTimestamp = block.Time() res.Transactions = make([]*TransactionRes, len(block.Transactions())) for i, tx := range block.Transactions() { @@ -135,47 +133,47 @@ func NewBlockRes(block *types.Block) *BlockRes { type TransactionRes struct { Hash common.Hash `json:"hash"` - Nonce int64 `json:"nonce"` + Nonce uint64 `json:"nonce"` BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber int64 `json:"blockNumber,omitempty"` TxIndex int64 `json:"transactionIndex,omitempty"` From common.Address `json:"from"` To *common.Address `json:"to"` - Value int64 `json:"value"` - Gas int64 `json:"gas"` - GasPrice int64 `json:"gasPrice"` + Value *big.Int `json:"value"` + Gas *big.Int `json:"gas"` + GasPrice *big.Int `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"` + 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 interface{} `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Input string `json:"input"` } ext.Hash = t.Hash.Hex() - ext.Nonce = common.ToHex(big.NewInt(t.Nonce).Bytes()) + ext.Nonce = common.ToHex(big.NewInt(int64(t.Nonce)).Bytes()) ext.BlockHash = t.BlockHash.Hex() ext.BlockNumber = common.ToHex(big.NewInt(t.BlockNumber).Bytes()) ext.TxIndex = common.ToHex(big.NewInt(t.TxIndex).Bytes()) ext.From = t.From.Hex() if t.To == nil { - ext.To = "0x00" + ext.To = nil } else { ext.To = t.To.Hex() } - ext.Value = common.ToHex(big.NewInt(t.Value).Bytes()) - ext.Gas = common.ToHex(big.NewInt(t.Gas).Bytes()) - ext.GasPrice = common.ToHex(big.NewInt(t.GasPrice).Bytes()) + ext.Value = common.ToHex(t.Value.Bytes()) + ext.Gas = common.ToHex(t.Gas.Bytes()) + ext.GasPrice = common.ToHex(t.GasPrice.Bytes()) ext.Input = common.ToHex(t.Input) return json.Marshal(ext) @@ -184,12 +182,12 @@ func (t *TransactionRes) MarshalJSON() ([]byte, error) { func NewTransactionRes(tx *types.Transaction) *TransactionRes { var v = new(TransactionRes) v.Hash = tx.Hash() - v.Nonce = int64(tx.Nonce()) + v.Nonce = 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.Value = tx.Value() + v.Gas = tx.Gas() + v.GasPrice = tx.GasPrice() v.Input = tx.Data() return v } @@ -218,25 +216,48 @@ type FilterWhisperRes struct { } type LogRes struct { - Address string `json:"address"` - Topics []string `json:"topics"` - Data string `json:"data"` - Number uint64 `json:"number"` + Address common.Address `json:"address"` + Topics []common.Hash `json:"topics"` + Data []byte `json:"data"` + Number uint64 `json:"number"` +} + +func NewLogRes(log state.Log) LogRes { + var l LogRes + l.Topics = make([]common.Hash, len(log.Topics())) + l.Address = log.Address() + l.Data = log.Data() + l.Number = log.Number() + for j, topic := range log.Topics() { + l.Topics[j] = topic + } + return l +} + +func (l *LogRes) MarshalJSON() ([]byte, error) { + var ext struct { + Address string `json:"address"` + Topics []string `json:"topics"` + Data string `json:"data"` + Number string `json:"number"` + } + + ext.Address = l.Address.Hex() + ext.Data = common.Bytes2Hex(l.Data) + ext.Number = common.Bytes2Hex(big.NewInt(int64(l.Number)).Bytes()) + ext.Topics = make([]string, len(l.Topics)) + for i, v := range l.Topics { + ext.Topics[i] = v.Hex() + } + + return json.Marshal(ext) } func NewLogsRes(logs state.Logs) (ls []LogRes) { ls = make([]LogRes, len(logs)) for i, log := range logs { - var l LogRes - l.Topics = make([]string, len(log.Topics())) - l.Address = log.Address().Hex() - l.Data = common.ToHex(log.Data()) - l.Number = log.Number() - for j, topic := range log.Topics() { - l.Topics[j] = topic.Hex() - } - ls[i] = l + ls[i] = NewLogRes(log) } return diff --git a/rpc/responses_test.go b/rpc/responses_test.go new file mode 100644 index 0000000000..2789398307 --- /dev/null +++ b/rpc/responses_test.go @@ -0,0 +1,123 @@ +package rpc + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" +) + +func TestNewBlockRes(t *testing.T) { + parentHash := common.HexToHash("0x01") + coinbase := common.HexToAddress("0x01") + root := common.HexToHash("0x01") + difficulty := common.Big1 + nonce := uint64(1) + extra := "" + block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, extra) + + _ = NewBlockRes(block) +} + +func TestBlockRes(t *testing.T) { + v := &BlockRes{ + BlockNumber: big.NewInt(0), + BlockHash: common.HexToHash("0x0"), + ParentHash: common.HexToHash("0x0"), + Nonce: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, + Sha3Uncles: common.HexToHash("0x0"), + LogsBloom: types.BytesToBloom([]byte{0}), + TransactionRoot: common.HexToHash("0x0"), + StateRoot: common.HexToHash("0x0"), + Miner: common.HexToAddress("0x0"), + Difficulty: big.NewInt(0), + TotalDifficulty: big.NewInt(0), + Size: big.NewInt(0), + ExtraData: []byte{}, + GasLimit: big.NewInt(0), + MinGasPrice: int64(0), + GasUsed: big.NewInt(0), + UnixTimestamp: int64(0), + // Transactions []*TransactionRes `json:"transactions"` + // Uncles []common.Hash `json:"uncles"` + } + + _, _ = json.Marshal(v) + + // fmt.Println(string(j)) + +} + +func TestTransactionRes(t *testing.T) { + a := common.HexToAddress("0x0") + v := &TransactionRes{ + Hash: common.HexToHash("0x0"), + Nonce: uint64(0), + BlockHash: common.HexToHash("0x0"), + BlockNumber: int64(0), + TxIndex: int64(0), + From: common.HexToAddress("0x0"), + To: &a, + Value: big.NewInt(0), + Gas: big.NewInt(0), + GasPrice: big.NewInt(0), + Input: []byte{0}, + } + + _, _ = json.Marshal(v) +} + +func TestNewTransactionRes(t *testing.T) { + to := common.HexToAddress("0x02") + amount := big.NewInt(1) + gasAmount := big.NewInt(1) + gasPrice := big.NewInt(1) + data := []byte{1, 2, 3} + tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data) + + _ = NewTransactionRes(tx) +} + +func TestLogRes(t *testing.T) { + topics := make([]common.Hash, 3) + topics = append(topics, common.HexToHash("0x00")) + topics = append(topics, common.HexToHash("0x10")) + topics = append(topics, common.HexToHash("0x20")) + + v := &LogRes{ + Topics: topics, + Address: common.HexToAddress("0x0"), + Data: []byte{1, 2, 3}, + Number: uint64(5), + } + + _, _ = json.Marshal(v) +} + +func MakeStateLog(num int) state.Log { + address := common.HexToAddress("0x0") + data := []byte{1, 2, 3} + number := uint64(num) + topics := make([]common.Hash, 3) + topics = append(topics, common.HexToHash("0x00")) + topics = append(topics, common.HexToHash("0x10")) + topics = append(topics, common.HexToHash("0x20")) + log := state.NewLog(address, topics, data, number) + return log +} + +func TestNewLogRes(t *testing.T) { + log := MakeStateLog(0) + _ = NewLogRes(log) +} + +func TestNewLogsRes(t *testing.T) { + logs := make([]state.Log, 3) + logs[0] = MakeStateLog(1) + logs[1] = MakeStateLog(2) + logs[2] = MakeStateLog(3) + _ = NewLogsRes(logs) +} From d36501a6e54e1c794af0c7109e937f7f7c74de79 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 17:45:03 +0100 Subject: [PATCH 02/25] Fixed miner * Miners could stall because the worker wasn't aware the miner was done --- miner/agent.go | 23 ++++++++++++++++------- miner/remote_agent.go | 1 + miner/worker.go | 26 ++++++++++++++++++-------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/miner/agent.go b/miner/agent.go index 5661d29824..c650fa2f30 100644 --- a/miner/agent.go +++ b/miner/agent.go @@ -1,12 +1,15 @@ package miner import ( + "sync" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/pow" ) type CpuMiner struct { + chMu sync.Mutex c chan *types.Block quit chan struct{} quitCurrentOp chan struct{} @@ -43,16 +46,13 @@ func (self *CpuMiner) Start() { } func (self *CpuMiner) update() { - justStarted := true out: for { select { case block := <-self.c: - if justStarted { - justStarted = true - } else { - self.quitCurrentOp <- struct{}{} - } + self.chMu.Lock() + self.quitCurrentOp <- struct{}{} + self.chMu.Unlock() go self.mine(block) case <-self.quit: @@ -60,6 +60,7 @@ out: } } + close(self.quitCurrentOp) done: // Empty channel for { @@ -75,12 +76,20 @@ done: func (self *CpuMiner) mine(block *types.Block) { minerlogger.Debugf("(re)started agent[%d]. mining...\n", self.index) + + // Reset the channel + self.chMu.Lock() + self.quitCurrentOp = make(chan struct{}, 1) + self.chMu.Unlock() + + // Mine nonce, mixDigest, _ := self.pow.Search(block, self.quitCurrentOp) if nonce != 0 { block.SetNonce(nonce) block.Header().MixDigest = common.BytesToHash(mixDigest) self.returnCh <- block - //self.returnCh <- Work{block.Number().Uint64(), nonce, mixDigest, seedHash} + } else { + self.returnCh <- nil } } diff --git a/miner/remote_agent.go b/miner/remote_agent.go index e92dd5963f..aa04a58aa9 100644 --- a/miner/remote_agent.go +++ b/miner/remote_agent.go @@ -50,6 +50,7 @@ out: break out case work := <-a.workCh: a.work = work + a.returnCh <- nil } } } diff --git a/miner/worker.go b/miner/worker.go index e0287ea8df..e3680dea3f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -5,6 +5,7 @@ import ( "math/big" "sort" "sync" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" @@ -58,13 +59,14 @@ type Agent interface { } type worker struct { - mu sync.Mutex + mu sync.Mutex + agents []Agent recv chan *types.Block mux *event.TypeMux quit chan struct{} pow pow.PoW - atWork int + atWork int64 eth core.Backend chain *core.ChainManager @@ -107,7 +109,7 @@ func (self *worker) start() { func (self *worker) stop() { self.mining = false - self.atWork = 0 + atomic.StoreInt64(&self.atWork, 0) close(self.quit) } @@ -135,9 +137,6 @@ out: self.uncleMu.Unlock() } - if self.atWork == 0 { - self.commitNewWork() - } case <-self.quit: // stop all agents for _, agent := range self.agents { @@ -146,6 +145,11 @@ out: break out case <-timer.C: minerlogger.Infoln("Hash rate:", self.HashRate(), "Khash") + + // XXX In case all mined a possible uncle + if atomic.LoadInt64(&self.atWork) == 0 { + self.commitNewWork() + } } } @@ -155,6 +159,12 @@ out: func (self *worker) wait() { for { for block := range self.recv { + atomic.AddInt64(&self.atWork, -1) + + if block == nil { + continue + } + if err := self.chain.InsertChain(types.Blocks{block}); err == nil { for _, uncle := range block.Uncles() { delete(self.possibleUncles, uncle.Hash()) @@ -170,7 +180,6 @@ func (self *worker) wait() { } else { self.commitNewWork() } - self.atWork-- } } } @@ -182,8 +191,9 @@ func (self *worker) push() { // push new work to agents for _, agent := range self.agents { + atomic.AddInt64(&self.atWork, 1) + agent.Work() <- self.current.block.Copy() - self.atWork++ } } } From c32bca45ad753da69a5530a1ee96ff069e937fc2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 17:45:09 +0100 Subject: [PATCH 03/25] Stack limit --- core/vm/stack.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/vm/stack.go b/core/vm/stack.go index c5c2774db0..1e093476bf 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -5,6 +5,8 @@ import ( "math/big" ) +const maxStack = 1024 + func newStack() *stack { return &stack{} } @@ -15,6 +17,10 @@ type stack struct { } func (st *stack) push(d *big.Int) { + if len(st.data) == maxStack { + panic(fmt.Sprintf("stack limit reached (%d)", maxStack)) + } + stackItem := new(big.Int).Set(d) if len(st.data) > st.ptr { st.data[st.ptr] = stackItem From 658204bafcba6332e979aee690dc5cff6e46fb42 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 17:55:30 +0100 Subject: [PATCH 04/25] bump --- cmd/ethereum/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 2cf81b9e75..2f417aacb7 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -42,7 +42,7 @@ import ( const ( ClientIdentifier = "Ethereum(G)" - Version = "0.9.3" + Version = "0.9.4" ) var ( From c4ea921876b0535022882c568b5cc6b0269db7d4 Mon Sep 17 00:00:00 2001 From: zelig Date: Mon, 23 Mar 2015 13:00:06 +0000 Subject: [PATCH 05/25] import/export accounts - cli: add passwordfile flag - cli: change unlock flag only takes account - cli: with unlock you are prompted for password or use passfile with password flag - cli: unlockAccount used in normal client start (run) and accountExport - cli: getPassword used in accountCreate and accountImport - accounts: Manager.Import, Manager.Export - crypto: SaveECDSA (to complement LoadECDSA) to save to file - crypto: NewKeyFromECDSA added (used in accountImport and New = generated constructor) --- accounts/account_manager.go | 20 ++++ cmd/ethereum/main.go | 178 +++++++++++++++++++++++++++++------- cmd/utils/flags.go | 8 +- crypto/crypto.go | 5 + crypto/key.go | 18 ++-- 5 files changed, 190 insertions(+), 39 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 646dc8376e..670d4337f3 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -208,3 +208,23 @@ func zeroKey(k *ecdsa.PrivateKey) { b[i] = 0 } } + +func (am *Manager) Export(path string, addr []byte, keyAuth string) error { + key, err := am.keyStore.GetKey(addr, keyAuth) + if err != nil { + return err + } + return crypto.SaveECDSA(path, key.PrivateKey) +} + +func (am *Manager) Import(path string, keyAuth string) (Account, error) { + privateKeyECDSA, err := crypto.LoadECDSA(path) + if err != nil { + return Account{}, err + } + key := crypto.NewKeyFromECDSA(privateKeyECDSA) + if err = am.keyStore.StoreKey(key, keyAuth); err != nil { + return Account{}, err + } + return Account{Address: key.Address}, nil +} diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 2f417aacb7..276480195e 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -26,11 +26,11 @@ import ( "os" "runtime" "strconv" - "strings" "time" "github.com/codegangsta/cli" "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" @@ -83,11 +83,62 @@ The output of this command is supposed to be machine-readable. Action: accountList, Name: "list", Usage: "print account addresses", + Description: ` + +`, }, { Action: accountCreate, Name: "new", Usage: "create a new account", + Description: ` + + ethereum account new + +Creates a new accountThe account is saved in encrypted format, you are prompted for a passphrase. +You must remember this passphrase to unlock your account in future. +For non-interactive use the passphrase can be specified with the --password flag: + + ethereum --password account new + + `, + }, + { + Action: accountImport, + Name: "import", + Usage: "import a private key into a new account", + Description: ` + + ethereum account import + +Imports a private key from and creates a new account with the address derived from the key. +The keyfile is assumed to contain an unencrypted private key in canonical EC format. + +The account is saved in encrypted format, you are prompted for a passphrase. +You must remember this passphrase to unlock your account in future. +For non-interactive use the passphrase can be specified with the --password flag: + + ethereum --password account import + + `, + }, + { + Action: accountExport, + Name: "export", + Usage: "export an account into key file", + Description: ` + + ethereum account export
+ +Exports the given account's private key into keyfile using the canonical EC format. +The account needs to be unlocked, if it is not the user is prompted for a passphrase to unlock it. +For non-interactive use, the password can be specified with the --unlock flag: + + ethereum --unlock account export
+ +Note: +Since you can directly copy your encrypted accounts to another ethereum instance, this import/export mechanism is not needed when you transfer an account between nodes. + `, }, }, }, @@ -130,6 +181,7 @@ The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP Ja } app.Flags = []cli.Flag{ utils.UnlockedAccountFlag, + utils.PasswordFileFlag, utils.BootnodesFlag, utils.DataDirFlag, utils.JSpathFlag, @@ -218,23 +270,43 @@ func execJSFiles(ctx *cli.Context) { ethereum.WaitForShutdown() } -func startEth(ctx *cli.Context, eth *eth.Ethereum) { - utils.StartEthereum(eth) - - // Load startup keys. XXX we are going to need a different format - account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) - if len(account) > 0 { - split := strings.Split(account, ":") - if len(split) != 2 { - utils.Fatalf("Illegal 'unlock' format (address:password)") - } - am := eth.AccountManager() +func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) { + if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { + var err error + // Load startup keys. XXX we are going to need a different format // Attempt to unlock the account - err := am.Unlock(common.FromHex(split[0]), split[1]) + passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) + if len(passfile) == 0 { + fmt.Println("Please enter a passphrase now.") + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + + passphrase = auth + + } else { + if passphrase, err = common.ReadAllFile(passfile); err != nil { + utils.Fatalf("Unable to read password file '%s': %v", passfile, err) + } + } + + err = am.Unlock(common.FromHex(account), passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) } } + return +} + +func startEth(ctx *cli.Context, eth *eth.Ethereum) { + utils.StartEthereum(eth) + am := eth.AccountManager() + + account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) + if len(account) > 0 { + unlockAccount(ctx, am, account) + } // Start auxiliary services if enabled. if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { utils.StartRPC(eth, ctx) @@ -255,30 +327,74 @@ func accountList(ctx *cli.Context) { } } +func getPassPhrase(ctx *cli.Context) (passphrase string) { + if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { + passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) + if len(passfile) == 0 { + 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.") + } + passphrase = auth + + } else { + var err error + if passphrase, err = common.ReadAllFile(passfile); err != nil { + utils.Fatalf("Unable to read password file '%s': %v", passfile, err) + } + } + } + return +} + func accountCreate(ctx *cli.Context) { am := utils.GetAccountManager(ctx) - passphrase := "" - if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { - 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.") - } - passphrase = auth - } + passphrase := getPassPhrase(ctx) acct, err := am.NewAccount(passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) } - fmt.Printf("Address: %x\n", acct.Address) + fmt.Printf("Address: %x\n", acct) +} + +func accountImport(ctx *cli.Context) { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx) + acct, err := am.Import(keyfile, passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %x\n", acct) +} + +func accountExport(ctx *cli.Context) { + account := ctx.Args().First() + if len(account) == 0 { + utils.Fatalf("account address must be given as first argument") + } + keyfile := ctx.Args().Get(1) + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as second argument") + } + am := utils.GetAccountManager(ctx) + auth := unlockAccount(ctx, am, account) + err := am.Export(keyfile, common.FromHex(account), auth) + if err != nil { + utils.Fatalf("Account export failed: %v", err) + } } func importchain(ctx *cli.Context) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 94b043d730..f94ec3a691 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -104,7 +104,13 @@ var ( } UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", - Usage: "Unlock a given account untill this programs exits (address:password)", + Usage: "unlock the account given until this program exits (prompts for password).", + Value: "", + } + PasswordFileFlag = cli.StringFlag{ + Name: "password", + Usage: "Password used when saving a new account and unlocking an existing account. If you create a new account make sure you remember this password.", + Value: "", } // logging and debug settings diff --git a/crypto/crypto.go b/crypto/crypto.go index c3d47b6293..2d26dd25ea 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -139,6 +139,11 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) { return ToECDSA(buf), nil } +// SaveECDSA saves a secp256k1 private key from the given file. +func SaveECDSA(file string, key *ecdsa.PrivateKey) error { + return common.WriteFile(file, FromECDSA(key)) +} + func GenerateKey() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(S256(), rand.Reader) } diff --git a/crypto/key.go b/crypto/key.go index 9dbf374675..0b84bfec16 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -85,6 +85,16 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { return err } +func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { + id := uuid.NewRandom() + key := &Key{ + Id: id, + Address: PubkeyToAddress(privateKeyECDSA.PublicKey), + PrivateKey: privateKeyECDSA, + } + return key +} + func NewKey(rand io.Reader) *Key { randBytes := make([]byte, 64) _, err := rand.Read(randBytes) @@ -97,11 +107,5 @@ func NewKey(rand io.Reader) *Key { panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } - id := uuid.NewRandom() - key := &Key{ - Id: id, - Address: PubkeyToAddress(privateKeyECDSA.PublicKey), - PrivateKey: privateKeyECDSA, - } - return key + return NewKeyFromECDSA(privateKeyECDSA) } From 859f1f08ca48de99408c825eba8d6ed4bfea3235 Mon Sep 17 00:00:00 2001 From: zelig Date: Mon, 23 Mar 2015 21:34:05 +0000 Subject: [PATCH 06/25] blockpool: wrap intermittent status test in a loop --- blockpool/status_test.go | 83 +++++++++++++++++++++++----------------- blockpool/test/util.go | 12 ++++-- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/blockpool/status_test.go b/blockpool/status_test.go index cbaa8bb559..a87b99d7c9 100644 --- a/blockpool/status_test.go +++ b/blockpool/status_test.go @@ -1,7 +1,7 @@ package blockpool import ( - // "fmt" + "fmt" "testing" "time" @@ -45,17 +45,15 @@ func getStatusValues(s *Status) []int { func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err error) { s := bp.Status() if s.Syncing != syncing { - t.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing) + err = fmt.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing) + return } got := getStatusValues(s) for i, v := range expected { - if i == 0 || i == 7 { - continue //hack - } err = test.CheckInt(statusFields[i], got[i], v, t) // fmt.Printf("%v: %v (%v)\n", statusFields[i], got[i], v) if err != nil { - return err + return } } return @@ -63,6 +61,25 @@ func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err func TestBlockPoolStatus(t *testing.T) { test.LogInit() + var err error + n := 3 + for n > 0 { + n-- + err = testBlockPoolStatus(t) + if err != nil { + t.Log(err) + continue + } else { + return + } + } + if err != nil { + t.Errorf("no pass out of 3: %v", err) + } +} + +func testBlockPoolStatus(t *testing.T) (err error) { + _, blockPool, blockPoolTester := newTestBlockPool(t) blockPoolTester.blockChain[0] = nil blockPoolTester.initRefBlockChain(12) @@ -70,6 +87,7 @@ func TestBlockPoolStatus(t *testing.T) { delete(blockPoolTester.refBlockChain, 6) blockPool.Start() + defer blockPool.Stop() blockPoolTester.tds = make(map[int]int) blockPoolTester.tds[9] = 1 blockPoolTester.tds[11] = 3 @@ -79,73 +97,67 @@ func TestBlockPoolStatus(t *testing.T) { peer2 := blockPoolTester.newPeer("peer2", 2, 6) peer3 := blockPoolTester.newPeer("peer3", 3, 11) peer4 := blockPoolTester.newPeer("peer4", 1, 9) - // peer1 := blockPoolTester.newPeer("peer1", 1, 9) - // peer2 := blockPoolTester.newPeer("peer2", 2, 6) - // peer3 := blockPoolTester.newPeer("peer3", 3, 11) - // peer4 := blockPoolTester.newPeer("peer4", 1, 9) peer2.blocksRequestsMap = peer1.blocksRequestsMap var expected []int - var err error expected = []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - err = checkStatus(t, blockPool, false, expected) + err = checkStatus(nil, blockPool, false, expected) if err != nil { return } peer1.AddPeer() expected = []int{0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(8, 9) - expected = []int{0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} - // err = checkStatus(t, blockPool, true, expected) + expected = []int{1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlockHashes(9, 8, 7, 3, 2) expected = []int{6, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} - // expected = []int{5, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(3, 7, 8) expected = []int{6, 5, 3, 3, 0, 1, 0, 0, 1, 1, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(2, 3) expected = []int{6, 5, 4, 4, 0, 1, 0, 0, 1, 1, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer4.AddPeer() expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer4.sendBlockHashes(12, 11) expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer2.AddPeer() expected = []int{6, 5, 4, 4, 0, 3, 0, 0, 3, 3, 1, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } @@ -153,76 +165,76 @@ func TestBlockPoolStatus(t *testing.T) { peer2.serveBlocks(5, 6) peer2.serveBlockHashes(6, 5, 4, 3, 2) expected = []int{10, 8, 5, 5, 0, 3, 1, 0, 3, 3, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer2.serveBlocks(2, 3, 4) expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 3, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } blockPool.RemovePeer("peer2") expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlockHashes(2, 1, 0) expected = []int{11, 9, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(1, 2) expected = []int{11, 9, 7, 7, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(4, 5) expected = []int{11, 9, 8, 8, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.AddPeer() expected = []int{11, 9, 8, 8, 0, 4, 1, 0, 4, 3, 2, 3, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.serveBlocks(10, 11) expected = []int{12, 9, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.serveBlockHashes(11, 10, 9) expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer4.sendBlocks(11, 12) expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 4, 3, 1} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.serveBlocks(9, 10) expected = []int{14, 11, 10, 10, 0, 4, 1, 0, 4, 3, 4, 3, 1} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } @@ -231,10 +243,9 @@ func TestBlockPoolStatus(t *testing.T) { blockPool.Wait(waitTimeout) time.Sleep(200 * time.Millisecond) expected = []int{14, 3, 11, 3, 8, 4, 1, 8, 4, 3, 4, 3, 1} - err = checkStatus(t, blockPool, false, expected) + err = checkStatus(nil, blockPool, false, expected) if err != nil { return } - - blockPool.Stop() + return nil } diff --git a/blockpool/test/util.go b/blockpool/test/util.go index 0349493c31..930601278b 100644 --- a/blockpool/test/util.go +++ b/blockpool/test/util.go @@ -10,16 +10,20 @@ import ( func CheckInt(name string, got int, expected int, t *testing.T) (err error) { if got != expected { - t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) - err = fmt.Errorf("") + err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) + if t != nil { + t.Error(err) + } } return } func CheckDuration(name string, got time.Duration, expected time.Duration, t *testing.T) (err error) { if got != expected { - t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) - err = fmt.Errorf("") + err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) + if t != nil { + t.Error(err) + } } return } From fd8d18ec280c3fe2c3d2651870c31c65b02039ba Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 12:37:00 +0000 Subject: [PATCH 07/25] unlocking coinbase - extract accounts.getKey method - if given empty address it retrieves coinbase (first account) - cli -unlock coinbase will unlock coinbase --- accounts/account_manager.go | 15 +++++++++++++-- cmd/ethereum/main.go | 5 ++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 670d4337f3..21ef469919 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -101,6 +101,17 @@ func (am *Manager) firstAddr() ([]byte, error) { return addrs[0], nil } +func (am *Manager) getKey(addr []byte, keyAuth string) (*crypto.Key, error) { + if len(addr) == 0 { + var err error + addr, err = am.firstAddr() + if err != nil { + return nil, err + } + } + return am.keyStore.GetKey(addr, keyAuth) +} + func (am *Manager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } @@ -119,7 +130,7 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) // 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) + key, err := am.getKey(addr, keyAuth) if err != nil { return err } @@ -132,7 +143,7 @@ func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duratio // 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) + key, err := am.getKey(addr, keyAuth) if err != nil { return err } diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 276480195e..fea3fbf61a 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -134,7 +134,7 @@ Exports the given account's private key into keyfile using the canonical EC form The account needs to be unlocked, if it is not the user is prompted for a passphrase to unlock it. For non-interactive use, the password can be specified with the --unlock flag: - ethereum --unlock account export
+ ethereum --password account export
Note: Since you can directly copy your encrypted accounts to another ethereum instance, this import/export mechanism is not needed when you transfer an account between nodes. @@ -305,6 +305,9 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) { account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) if len(account) > 0 { + if account == "coinbase" { + account = "" + } unlockAccount(ctx, am, account) } // Start auxiliary services if enabled. From 1c4c71dcff442e3ae30e510fef312d3c05341f30 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 14:09:06 +0000 Subject: [PATCH 08/25] cli: fix liner not closing (spuriously opened) in noninteractive jsre --- cmd/ethereum/js.go | 4 ++-- cmd/ethereum/main.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index 1f0033daa7..599af0a16c 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -67,14 +67,14 @@ type jsre struct { prompter } -func newJSRE(ethereum *eth.Ethereum, libPath string) *jsre { +func newJSRE(ethereum *eth.Ethereum, libPath string, interactive bool) *jsre { js := &jsre{ethereum: ethereum, ps1: "> "} js.xeth = xeth.New(ethereum, js) js.re = re.New(libPath) js.apiBindings() js.adminBindings() - if !liner.TerminalSupported() { + if !liner.TerminalSupported() || !interactive { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index fea3fbf61a..59c6ef485d 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -246,7 +246,7 @@ func console(ctx *cli.Context) { } startEth(ctx, ethereum) - repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), true) repl.interactive() ethereum.Stop() @@ -261,7 +261,7 @@ func execJSFiles(ctx *cli.Context) { } startEth(ctx, ethereum) - repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), false) for _, file := range ctx.Args() { repl.exec(file) } From 34d5a6c156a014ce000b4f850f2b0f11533387f0 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 16:05:27 +0000 Subject: [PATCH 09/25] cli: help formatting --- cmd/ethereum/main.go | 31 ++++++++++++++++++------------- cmd/utils/flags.go | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 59c6ef485d..39a0a9d7f9 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -83,9 +83,6 @@ The output of this command is supposed to be machine-readable. Action: accountList, Name: "list", Usage: "print account addresses", - Description: ` - -`, }, { Action: accountCreate, @@ -111,12 +108,14 @@ For non-interactive use the passphrase can be specified with the --password flag ethereum account import -Imports a private key from and creates a new account with the address derived from the key. -The keyfile is assumed to contain an unencrypted private key in canonical EC format. +Imports a private key from and creates a new account with the address +derived from the key. +The keyfile is assumed to contain an unencrypted private key in canonical EC +format. The account is saved in encrypted format, you are prompted for a passphrase. You must remember this passphrase to unlock your account in future. -For non-interactive use the passphrase can be specified with the --password flag: +For non-interactive use the passphrase can be specified with the -password flag: ethereum --password account import @@ -130,14 +129,18 @@ For non-interactive use the passphrase can be specified with the --password flag ethereum account export
-Exports the given account's private key into keyfile using the canonical EC format. -The account needs to be unlocked, if it is not the user is prompted for a passphrase to unlock it. -For non-interactive use, the password can be specified with the --unlock flag: +Exports the given account's private key into keyfile using the canonical EC +format. +The account needs to be unlocked, if it is not the user is prompted for a +passphrase to unlock it. +For non-interactive use, the passphrase can be specified with the --unlock flag: ethereum --password account export
Note: -Since you can directly copy your encrypted accounts to another ethereum instance, this import/export mechanism is not needed when you transfer an account between nodes. +As you can directly copy your encrypted accounts to another ethereum instance, +this import/export mechanism is not needed when you transfer an account between +nodes. `, }, }, @@ -156,16 +159,18 @@ Use "ethereum dump 0" to dump the genesis block. Name: "console", Usage: `Ethereum Console: interactive JavaScript environment`, Description: ` -Console is an interactive shell for the Ethereum JavaScript runtime environment which exposes a node admin interface as well as the DAPP JavaScript API. +Console is an interactive shell for the Ethereum JavaScript runtime environment +which exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console `, }, { Action: execJSFiles, Name: "js", - Usage: `executes the given JavaScript files in the Ethereum Frontier JavaScript VM`, + Usage: `executes the given JavaScript files in the Ethereum JavaScript VM`, Description: ` -The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console +The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP +JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console `, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f94ec3a691..dda4095023 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -109,7 +109,7 @@ var ( } PasswordFileFlag = cli.StringFlag{ Name: "password", - Usage: "Password used when saving a new account and unlocking an existing account. If you create a new account make sure you remember this password.", + Usage: "Path to password file for (un)locking an existing account.", Value: "", } From d1b52efdb581ca90613d2047b974d3a128f9bc58 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 16:19:11 +0000 Subject: [PATCH 10/25] cli: implement ethereum presale wallet import via cli --- accounts/account_manager.go | 12 ++++++ cmd/ethereum/main.go | 74 +++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 21ef469919..f063f8ca5e 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -239,3 +239,15 @@ func (am *Manager) Import(path string, keyAuth string) (Account, error) { } return Account{Address: key.Address}, nil } + +func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) { + var key *crypto.Key + key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password) + if err != nil { + return + } + if err = am.keyStore.StoreKey(key, password); err != nil { + return + } + return Account{Address: key.Address}, nil +} diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 39a0a9d7f9..57729b2060 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -23,6 +23,7 @@ package main import ( "bufio" "fmt" + "io/ioutil" "os" "runtime" "strconv" @@ -74,6 +75,19 @@ Regular users do not need to execute it. The output of this command is supposed to be machine-readable. `, }, + + { + Action: accountList, + Name: "wallet", + Usage: "ethereum presale wallet", + Subcommands: []cli.Command{ + { + Action: importWallet, + Name: "import", + Usage: "import ethereum presale wallet", + }, + }, + }, { Action: accountList, Name: "account", @@ -280,22 +294,7 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass var err error // Load startup keys. XXX we are going to need a different format // Attempt to unlock the account - passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) - if len(passfile) == 0 { - fmt.Println("Please enter a passphrase now.") - auth, err := readPassword("Passphrase: ", true) - if err != nil { - utils.Fatalf("%v", err) - } - - passphrase = auth - - } else { - if passphrase, err = common.ReadAllFile(passfile); err != nil { - utils.Fatalf("Unable to read password file '%s': %v", passfile, err) - } - } - + passphrase := getPassPhrase(ctx, "", false) err = am.Unlock(common.FromHex(account), passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) @@ -335,22 +334,23 @@ func accountList(ctx *cli.Context) { } } -func getPassPhrase(ctx *cli.Context) (passphrase string) { +func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) { if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) if len(passfile) == 0 { - fmt.Println("The new account will be encrypted with a passphrase.") - fmt.Println("Please enter a passphrase now.") + fmt.Println(desc) 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.") + if confirmation { + confirm, err := readPassword("Repeat Passphrase: ", false) + if err != nil { + utils.Fatalf("%v", err) + } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") + } } passphrase = auth @@ -366,7 +366,7 @@ func getPassPhrase(ctx *cli.Context) (passphrase string) { func accountCreate(ctx *cli.Context) { am := utils.GetAccountManager(ctx) - passphrase := getPassPhrase(ctx) + passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) acct, err := am.NewAccount(passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) @@ -374,13 +374,33 @@ func accountCreate(ctx *cli.Context) { fmt.Printf("Address: %x\n", acct) } +func importWallet(ctx *cli.Context) { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + keyJson, err := ioutil.ReadFile(keyfile) + if err != nil { + utils.Fatalf("Could not read wallet file: %v", err) + } + + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx, "", false) + + acct, err := am.ImportPreSaleKey(keyJson, passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %x\n", acct) +} + func accountImport(ctx *cli.Context) { keyfile := ctx.Args().First() if len(keyfile) == 0 { utils.Fatalf("keyfile must be given as argument") } am := utils.GetAccountManager(ctx) - passphrase := getPassPhrase(ctx) + passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) acct, err := am.Import(keyfile, passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) From fee224f07582a3f4c74f214347a89061ce75d2a1 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 21:53:46 +0000 Subject: [PATCH 11/25] cli test: fix test newJSRE interactive argument --- cmd/ethereum/js_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethereum/js_test.go b/cmd/ethereum/js_test.go index a6058b3184..580bc7a2b3 100644 --- a/cmd/ethereum/js_test.go +++ b/cmd/ethereum/js_test.go @@ -47,7 +47,7 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) { return } assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") - repl = newJSRE(ethereum, assetPath) + repl = newJSRE(ethereum, assetPath, false) return } From 23e41a57ad7e7cb4bc5a1cbad28bbf8d65907fdd Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 10:41:36 +0000 Subject: [PATCH 12/25] Applying: fix adming js test regression (maybe otto update?) --- cmd/ethereum/js.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index 599af0a16c..8e88a1c543 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -102,7 +102,7 @@ func (js *jsre) apiBindings() { jethObj := t.Object() jethObj.Set("send", jeth.Send) - err := js.re.Compile("bignum.js", re.BigNumber_JS) + err := js.re.Compile("bignumber.js", re.BigNumber_JS) if err != nil { utils.Fatalf("Error loading bignumber.js: %v", err) } From 4ec38e39320ee9abccd96da765a9c65fccd04151 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 14:58:52 +0000 Subject: [PATCH 13/25] common: remove WriteFile and ReadAllFile (use ioutil instead) --- cmd/ethereum/js_test.go | 6 +++--- cmd/ethereum/main.go | 5 +++-- cmd/mist/bindings.go | 7 +++--- cmd/mist/gui.go | 5 +++-- common/path.go | 30 -------------------------- common/path_test.go | 47 +---------------------------------------- crypto/crypto.go | 6 ++++-- jsre/jsre_test.go | 8 +++---- 8 files changed, 22 insertions(+), 92 deletions(-) diff --git a/cmd/ethereum/js_test.go b/cmd/ethereum/js_test.go index 580bc7a2b3..5b962f6219 100644 --- a/cmd/ethereum/js_test.go +++ b/cmd/ethereum/js_test.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io/ioutil" "os" "path" "testing" @@ -9,7 +10,6 @@ import ( "github.com/robertkrimen/otto" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" ) @@ -30,8 +30,8 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) { } // FIXME: this does not work ATM ks := crypto.NewKeyStorePlain("/tmp/eth/keys") - common.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", - []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`)) + ioutil.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", + []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm) port++ ethereum, err = eth.New(ð.Config{ diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 57729b2060..6bbe1044f0 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -355,10 +355,11 @@ func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase passphrase = auth } else { - var err error - if passphrase, err = common.ReadAllFile(passfile); err != nil { + passbytes, err := ioutil.ReadFile(passfile) + if err != nil { utils.Fatalf("Unable to read password file '%s': %v", passfile, err) } + passphrase = string(passbytes) } } return diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go index 8a9ec7cb17..e7ce50c352 100644 --- a/cmd/mist/bindings.go +++ b/cmd/mist/bindings.go @@ -22,13 +22,14 @@ package main import ( "encoding/json" + "io/ioutil" "os" "strconv" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" ) type plugin struct { @@ -46,14 +47,14 @@ func (self *Gui) AddPlugin(pluginPath string) { self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath} json, _ := json.MarshalIndent(self.plugins, "", " ") - common.WriteFile(self.eth.DataDir+"/plugins.json", json) + ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm) } func (self *Gui) RemovePlugin(pluginPath string) { delete(self.plugins, pluginPath) json, _ := json.MarshalIndent(self.plugins, "", " ") - common.WriteFile(self.eth.DataDir+"/plugins.json", json) + ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm) } func (self *Gui) DumpState(hash, path string) { diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go index 08f02f833a..d37d6f81b8 100644 --- a/cmd/mist/gui.go +++ b/cmd/mist/gui.go @@ -25,6 +25,7 @@ import "C" import ( "encoding/json" "fmt" + "io/ioutil" "math/big" "path" "runtime" @@ -91,8 +92,8 @@ func NewWindow(ethereum *eth.Ethereum) *Gui { plugins: make(map[string]plugin), serviceEvents: make(chan ServEv, 1), } - data, _ := common.ReadAllFile(path.Join(ethereum.DataDir, "plugins.json")) - json.Unmarshal([]byte(data), &gui.plugins) + data, _ := ioutil.ReadFile(path.Join(ethereum.DataDir, "plugins.json")) + json.Unmarshal(data, &gui.plugins) return gui } diff --git a/common/path.go b/common/path.go index d38b1fd5b3..a74a0d5bd3 100644 --- a/common/path.go +++ b/common/path.go @@ -2,7 +2,6 @@ package common import ( "fmt" - "io/ioutil" "os" "os/user" "path" @@ -43,35 +42,6 @@ func FileExist(filePath string) bool { return true } -func ReadAllFile(filePath string) (string, error) { - file, err := os.Open(filePath) - if err != nil { - return "", err - } - - data, err := ioutil.ReadAll(file) - if err != nil { - return "", err - } - - return string(data), nil -} - -func WriteFile(filePath string, content []byte) error { - fh, err := os.OpenFile(filePath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - return err - } - defer fh.Close() - - _, err = fh.Write(content) - if err != nil { - return err - } - - return nil -} - func AbsolutePath(Datadir string, filename string) string { if path.IsAbs(filename) { return filename diff --git a/common/path_test.go b/common/path_test.go index c831d1a57d..4b90c543b7 100644 --- a/common/path_test.go +++ b/common/path_test.go @@ -2,56 +2,11 @@ package common import ( "os" - "testing" + // "testing" checker "gopkg.in/check.v1" ) -func TestGoodFile(t *testing.T) { - goodpath := "~/goethereumtest.pass" - path := ExpandHomePath(goodpath) - contentstring := "3.14159265358979323846" - - err := WriteFile(path, []byte(contentstring)) - if err != nil { - t.Error("Could not write file") - } - - if !FileExist(path) { - t.Error("File not found at", path) - } - - v, err := ReadAllFile(path) - if err != nil { - t.Error("Could not read file", path) - } - if v != contentstring { - t.Error("Expected", contentstring, "Got", v) - } - -} - -func TestBadFile(t *testing.T) { - badpath := "/this/path/should/not/exist/goethereumtest.fail" - path := ExpandHomePath(badpath) - contentstring := "3.14159265358979323846" - - err := WriteFile(path, []byte(contentstring)) - if err == nil { - t.Error("Wrote file, but should not be able to", path) - } - - if FileExist(path) { - t.Error("Found file, but should not be able to", path) - } - - v, err := ReadAllFile(path) - if err == nil { - t.Error("Read file, but should not be able to", v) - } - -} - type CommonSuite struct{} var _ = checker.Suite(&CommonSuite{}) diff --git a/crypto/crypto.go b/crypto/crypto.go index 2d26dd25ea..442942c6c5 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -9,6 +9,7 @@ import ( "crypto/sha256" "fmt" "io" + "io/ioutil" "os" "encoding/hex" @@ -139,9 +140,10 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) { return ToECDSA(buf), nil } -// SaveECDSA saves a secp256k1 private key from the given file. +// SaveECDSA saves a secp256k1 private key to the given file with restrictive +// permissions func SaveECDSA(file string, key *ecdsa.PrivateKey) error { - return common.WriteFile(file, FromECDSA(key)) + return ioutil.WriteFile(file, FromECDSA(key), 0600) } func GenerateKey() (*ecdsa.PrivateKey, error) { diff --git a/jsre/jsre_test.go b/jsre/jsre_test.go index 8a771dae80..667ed4bdc7 100644 --- a/jsre/jsre_test.go +++ b/jsre/jsre_test.go @@ -2,9 +2,9 @@ package jsre import ( "github.com/robertkrimen/otto" + "io/ioutil" + "os" "testing" - - "github.com/ethereum/go-ethereum/common" ) type testNativeObjectBinding struct { @@ -26,7 +26,7 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value func TestExec(t *testing.T) { jsre := New("/tmp") - common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`)) + ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm) err := jsre.Exec("test.js") if err != nil { t.Errorf("expected no error, got %v", err) @@ -64,7 +64,7 @@ func TestBind(t *testing.T) { func TestLoadScript(t *testing.T) { jsre := New("/tmp") - common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`)) + ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm) _, err := jsre.Run(`loadScript("test.js")`) if err != nil { t.Errorf("expected no error, got %v", err) From 11d2ebc06ffffa8846d5d55cae5663fac6f685f1 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 15:45:56 +0000 Subject: [PATCH 14/25] unlocking coinbase without knowing address - accounts: remove Manager.getKey - cli: for -unlock coinbase, use account manager Coinbase() --- accounts/account_manager.go | 18 +++--------------- cmd/ethereum/main.go | 9 +++++++-- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index f063f8ca5e..392518703e 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -36,9 +36,8 @@ import ( "bytes" "crypto/ecdsa" crand "crypto/rand" - "os" - "errors" + "os" "sync" "time" @@ -101,17 +100,6 @@ func (am *Manager) firstAddr() ([]byte, error) { return addrs[0], nil } -func (am *Manager) getKey(addr []byte, keyAuth string) (*crypto.Key, error) { - if len(addr) == 0 { - var err error - addr, err = am.firstAddr() - if err != nil { - return nil, err - } - } - return am.keyStore.GetKey(addr, keyAuth) -} - func (am *Manager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } @@ -130,7 +118,7 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) // 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.getKey(addr, keyAuth) + key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { return err } @@ -143,7 +131,7 @@ func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duratio // 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.getKey(addr, keyAuth) + key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { return err } diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 6bbe1044f0..8983b85a6a 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -294,7 +294,7 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass var err error // Load startup keys. XXX we are going to need a different format // Attempt to unlock the account - passphrase := getPassPhrase(ctx, "", false) + passphrase = getPassPhrase(ctx, "", false) err = am.Unlock(common.FromHex(account), passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) @@ -310,7 +310,11 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) { account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) if len(account) > 0 { if account == "coinbase" { - account = "" + accbytes, err := am.Coinbase() + if err != nil { + utils.Fatalf("no coinbase account: %v", err) + } + account = common.ToHex(accbytes) } unlockAccount(ctx, am, account) } @@ -420,6 +424,7 @@ func accountExport(ctx *cli.Context) { } am := utils.GetAccountManager(ctx) auth := unlockAccount(ctx, am, account) + err := am.Export(keyfile, common.FromHex(account), auth) if err != nil { utils.Fatalf("Account export failed: %v", err) From abbdf4156057de8a4f866b0840defc00c2c500db Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 16:10:44 +0000 Subject: [PATCH 15/25] output error message if unlock address is invalid (fixes the wierd "read /path: is a directory") msg --- cmd/ethereum/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 8983b85a6a..2e721dc71d 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -295,7 +295,11 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass // Load startup keys. XXX we are going to need a different format // Attempt to unlock the account passphrase = getPassPhrase(ctx, "", false) - err = am.Unlock(common.FromHex(account), passphrase) + accbytes := common.FromHex(account) + if len(accbytes) == 0 { + utils.Fatalf("Invalid account address '%s'", account) + } + err = am.Unlock(accbytes, passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) } From 7577d1261403dbabdb30e21415d34b4e5da466ec Mon Sep 17 00:00:00 2001 From: zelig Date: Thu, 26 Mar 2015 18:55:39 +0000 Subject: [PATCH 16/25] max paranoia mode to UNsupport unencrypted keys entirely - remove account export functionality from CLI - remove accountExport method, - remove unencrypted-keys flag from everywhere - improve documentation --- accounts/account_manager.go | 2 + cmd/ethereum/main.go | 151 +++++++++++++++++------------------- cmd/utils/flags.go | 14 +--- 3 files changed, 74 insertions(+), 93 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 392518703e..34a2c48910 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -208,6 +208,8 @@ func zeroKey(k *ecdsa.PrivateKey) { } } +// USE WITH CAUTION = this will save an unencrypted private key on disk +// no cli or js interface func (am *Manager) Export(path string, addr []byte, keyAuth string) error { key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 2e721dc71d..42321e8bc2 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -77,9 +77,8 @@ The output of this command is supposed to be machine-readable. }, { - Action: accountList, - Name: "wallet", - Usage: "ethereum presale wallet", + Name: "wallet", + Usage: "ethereum presale wallet", Subcommands: []cli.Command{ { Action: importWallet, @@ -92,6 +91,28 @@ The output of this command is supposed to be machine-readable. Action: accountList, Name: "account", Usage: "manage accounts", + Description: ` + +Manage accounts lets you create new accounts, list all existing accounts, +import a private key into a new account. + +It supports interactive mode, when you are prompted for password as well as +non-interactive mode where passwords are supplied via a given password file. +Non-interactive mode is only meant for scripted use on test networks or known +safe environments. + +Make sure you remember the password you gave when creating a new account (with +either new or import). Without it you are not able to unlock your account. + +Note that exporting your key in unencrypted format is NOT supported. + +Keys are stored under /keys. +It is safe to transfer the entire directory or the individual keys therein +between ethereum nodes. +Make sure you backup your keys regularly. + +And finally. DO NOT FORGET YOUR PASSWORD. +`, Subcommands: []cli.Command{ { Action: accountList, @@ -106,12 +127,18 @@ The output of this command is supposed to be machine-readable. ethereum account new -Creates a new accountThe account is saved in encrypted format, you are prompted for a passphrase. -You must remember this passphrase to unlock your account in future. +Creates a new account. Prints the address. + +The account is saved in encrypted format, you are prompted for a passphrase. + +You must remember this passphrase to unlock your account in the future. + For non-interactive use the passphrase can be specified with the --password flag: ethereum --password account new +Note, this is meant to be used for testing only, it is a bad idea to save your +password to file or expose in any other way. `, }, { @@ -122,38 +149,23 @@ For non-interactive use the passphrase can be specified with the --password flag ethereum account import -Imports a private key from and creates a new account with the address -derived from the key. +Imports an unencrypted private key from and creates a new account. +Prints the address. + The keyfile is assumed to contain an unencrypted private key in canonical EC -format. +raw bytes format. The account is saved in encrypted format, you are prompted for a passphrase. -You must remember this passphrase to unlock your account in future. + +You must remember this passphrase to unlock your account in the future. + For non-interactive use the passphrase can be specified with the -password flag: ethereum --password account import - `, - }, - { - Action: accountExport, - Name: "export", - Usage: "export an account into key file", - Description: ` - - ethereum account export
- -Exports the given account's private key into keyfile using the canonical EC -format. -The account needs to be unlocked, if it is not the user is prompted for a -passphrase to unlock it. -For non-interactive use, the passphrase can be specified with the --unlock flag: - - ethereum --password account export
- Note: As you can directly copy your encrypted accounts to another ethereum instance, -this import/export mechanism is not needed when you transfer an account between +this import mechanism is not needed when you transfer an account between nodes. `, }, @@ -217,7 +229,6 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.RPCEnabledFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, - utils.UnencryptedKeysFlag, utils.VMDebugFlag, utils.ProtocolVersionFlag, utils.NetworkIdFlag, @@ -290,19 +301,17 @@ func execJSFiles(ctx *cli.Context) { } func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) { - if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { - var err error - // Load startup keys. XXX we are going to need a different format - // Attempt to unlock the account - passphrase = getPassPhrase(ctx, "", false) - accbytes := common.FromHex(account) - if len(accbytes) == 0 { - utils.Fatalf("Invalid account address '%s'", account) - } - err = am.Unlock(accbytes, passphrase) - if err != nil { - utils.Fatalf("Unlock account failed '%v'", err) - } + var err error + // Load startup keys. XXX we are going to need a different format + // Attempt to unlock the account + passphrase = getPassPhrase(ctx, "", false) + accbytes := common.FromHex(account) + if len(accbytes) == 0 { + utils.Fatalf("Invalid account address '%s'", account) + } + err = am.Unlock(accbytes, passphrase) + if err != nil { + utils.Fatalf("Unlock account failed '%v'", err) } return } @@ -343,32 +352,30 @@ func accountList(ctx *cli.Context) { } func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) { - if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { - passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) - if len(passfile) == 0 { - fmt.Println(desc) - auth, err := readPassword("Passphrase: ", true) + passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) + if len(passfile) == 0 { + fmt.Println(desc) + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + if confirmation { + confirm, err := readPassword("Repeat Passphrase: ", false) if err != nil { utils.Fatalf("%v", err) } - if confirmation { - confirm, err := readPassword("Repeat Passphrase: ", false) - if err != nil { - utils.Fatalf("%v", err) - } - if auth != confirm { - utils.Fatalf("Passphrases did not match.") - } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") } - passphrase = auth - - } else { - passbytes, err := ioutil.ReadFile(passfile) - if err != nil { - utils.Fatalf("Unable to read password file '%s': %v", passfile, err) - } - passphrase = string(passbytes) } + passphrase = auth + + } else { + passbytes, err := ioutil.ReadFile(passfile) + if err != nil { + utils.Fatalf("Unable to read password file '%s': %v", passfile, err) + } + passphrase = string(passbytes) } return } @@ -417,24 +424,6 @@ func accountImport(ctx *cli.Context) { fmt.Printf("Address: %x\n", acct) } -func accountExport(ctx *cli.Context) { - account := ctx.Args().First() - if len(account) == 0 { - utils.Fatalf("account address must be given as first argument") - } - keyfile := ctx.Args().Get(1) - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as second argument") - } - am := utils.GetAccountManager(ctx) - auth := unlockAccount(ctx, am, account) - - err := am.Export(keyfile, common.FromHex(account), auth) - if err != nil { - utils.Fatalf("Account export failed: %v", err) - } -} - func importchain(ctx *cli.Context) { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index dda4095023..f948cdb06b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -97,14 +97,9 @@ var ( Usage: "Enable mining", } - // key settings - UnencryptedKeysFlag = cli.BoolFlag{ - Name: "unencrypted-keys", - Usage: "disable private key disk encryption (for testing)", - } UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", - Usage: "unlock the account given until this program exits (prompts for password).", + Usage: "unlock the account given until this program exits (prompts for password). '--unlock coinbase' unlocks the primary (coinbase) account", Value: "", } PasswordFileFlag = cli.StringFlag{ @@ -249,12 +244,7 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat func GetAccountManager(ctx *cli.Context) *accounts.Manager { dataDir := ctx.GlobalString(DataDirFlag.Name) - var ks crypto.KeyStore2 - if ctx.GlobalBool(UnencryptedKeysFlag.Name) { - ks = crypto.NewKeyStorePlain(path.Join(dataDir, "plainkeys")) - } else { - ks = crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) - } + ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) return accounts.NewManager(ks) } From b0b0939879b9fb8453ec1c8fa2ceb522e56df3bc Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 21:27:52 +0100 Subject: [PATCH 17/25] renamed ethereum => geth --- cmd/{ethereum => geth}/admin.go | 0 cmd/{ethereum => geth}/blocktest.go | 0 cmd/{ethereum => geth}/js.go | 0 cmd/{ethereum => geth}/js_test.go | 0 cmd/{ethereum => geth}/main.go | 10 +++++----- 5 files changed, 5 insertions(+), 5 deletions(-) rename cmd/{ethereum => geth}/admin.go (100%) rename cmd/{ethereum => geth}/blocktest.go (100%) rename cmd/{ethereum => geth}/js.go (100%) rename cmd/{ethereum => geth}/js_test.go (100%) rename cmd/{ethereum => geth}/main.go (97%) diff --git a/cmd/ethereum/admin.go b/cmd/geth/admin.go similarity index 100% rename from cmd/ethereum/admin.go rename to cmd/geth/admin.go diff --git a/cmd/ethereum/blocktest.go b/cmd/geth/blocktest.go similarity index 100% rename from cmd/ethereum/blocktest.go rename to cmd/geth/blocktest.go diff --git a/cmd/ethereum/js.go b/cmd/geth/js.go similarity index 100% rename from cmd/ethereum/js.go rename to cmd/geth/js.go diff --git a/cmd/ethereum/js_test.go b/cmd/geth/js_test.go similarity index 100% rename from cmd/ethereum/js_test.go rename to cmd/geth/js_test.go diff --git a/cmd/ethereum/main.go b/cmd/geth/main.go similarity index 97% rename from cmd/ethereum/main.go rename to cmd/geth/main.go index 42321e8bc2..da505218bc 100644 --- a/cmd/ethereum/main.go +++ b/cmd/geth/main.go @@ -42,7 +42,7 @@ import ( ) const ( - ClientIdentifier = "Ethereum(G)" + ClientIdentifier = "Geth" Version = "0.9.4" ) @@ -183,9 +183,9 @@ Use "ethereum dump 0" to dump the genesis block. { Action: console, Name: "console", - Usage: `Ethereum Console: interactive JavaScript environment`, + Usage: `Geth Console: interactive JavaScript environment`, Description: ` -Console is an interactive shell for the Ethereum JavaScript runtime environment +The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console `, @@ -193,9 +193,9 @@ See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console { Action: execJSFiles, Name: "js", - Usage: `executes the given JavaScript files in the Ethereum JavaScript VM`, + Usage: `executes the given JavaScript files in the Geth JavaScript VM`, Description: ` -The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP +The JavaScript VM exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console `, }, From 5838847a9a47b58c6462a66bbce73266d72abde2 Mon Sep 17 00:00:00 2001 From: aperseghin Date: Thu, 26 Mar 2015 17:48:24 -0400 Subject: [PATCH 18/25] Update README.md Rename ethereum executable to geth --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0200a57d50..22b331cbd2 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Mist (GUI): `go get github.com/ethereum/go-ethereum/cmd/mist` -Ethereum (CLI): +Geth (CLI): `go get github.com/ethereum/go-ethereum/cmd/ethereum` @@ -32,10 +32,10 @@ Mist (GUI): godep go build -v ./cmd/mist ``` -Ethereum (CLI): +Geth (CLI): ``` -godep go build -v ./cmd/ethereum +godep go build -v ./cmd/geth ``` Instead of `build`, you can use `install` which will also install the resulting binary. @@ -61,7 +61,7 @@ Go Ethereum comes with several wrappers/executables found in [the `cmd` directory](https://github.com/ethereum/go-ethereum/tree/develop/cmd): * `mist` Official Ethereum Browser (ethereum GUI client) -* `ethereum` Ethereum CLI (ethereum command line interface client) +* `geth` Ethereum CLI (ethereum command line interface client) * `bootnode` runs a bootstrap node for the Discovery Protocol * `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suite: `cat file | ethtest`. @@ -73,12 +73,12 @@ Go Ethereum comes with several wrappers/executables found in Command line options ============================ -Both `mist` and `ethereum` can be configured via command line options, environment variables and config files. +Both `mist` and `geth` can be configured via command line options, environment variables and config files. To get the options available: ``` -ethereum -help +geth -help ``` For further details on options, see the [wiki](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) From b375bbee5fa0b04867cdecdc28e66078a2e32280 Mon Sep 17 00:00:00 2001 From: zelig Date: Thu, 26 Mar 2015 21:49:22 +0000 Subject: [PATCH 19/25] settable etherbase - etherbase flag for block reward destination - coinbase => etherbase - CLI- eth Config -> eth, xeth -> RPC / Miner - use primary instead of coinbase as the unlock magic wildcard - accounts: firstAddr/Coinbase -> Primary --- accounts/account_manager.go | 8 +------- cmd/geth/main.go | 8 ++++---- cmd/utils/flags.go | 8 +++++++- eth/backend.go | 27 +++++++++++++++++++++++---- xeth/xeth.go | 4 ++-- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 34a2c48910..e9eb8f8165 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -81,13 +81,7 @@ func (am *Manager) HasAccount(addr []byte) bool { 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) { +func (am *Manager) Primary() (addr []byte, err error) { addrs, err := am.keyStore.GetKeyAddresses() if os.IsNotExist(err) { return nil, ErrNoKeys diff --git a/cmd/geth/main.go b/cmd/geth/main.go index da505218bc..05e2e4ae65 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -221,6 +221,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.LogJSONFlag, utils.LogLevelFlag, utils.MaxPeersFlag, + utils.EtherbaseFlag, utils.MinerThreadsFlag, utils.MiningEnabledFlag, utils.NATFlag, @@ -322,10 +323,10 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) { account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) if len(account) > 0 { - if account == "coinbase" { - accbytes, err := am.Coinbase() + if account == "primary" { + accbytes, err := am.Primary() if err != nil { - utils.Fatalf("no coinbase account: %v", err) + utils.Fatalf("no primary account: %v", err) } account = common.ToHex(accbytes) } @@ -468,7 +469,6 @@ func dump(ctx *cli.Context) { } else { statedb := state.New(block.Root(), stateDb) fmt.Printf("%s\n", statedb.Dump()) - // fmt.Println(block) } } } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f948cdb06b..1b5559081b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -96,10 +96,15 @@ var ( Name: "mine", Usage: "Enable mining", } + EtherbaseFlag = cli.StringFlag{ + Name: "Etherbase", + Usage: "public address for block mining rewards. By default the address of your primary account is used", + Value: "primary", + } UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", - Usage: "unlock the account given until this program exits (prompts for password). '--unlock coinbase' unlocks the primary (coinbase) account", + Usage: "unlock the account given until this program exits (prompts for password). '--unlock primary' unlocks the primary account", Value: "", } PasswordFileFlag = cli.StringFlag{ @@ -215,6 +220,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { LogFile: ctx.GlobalString(LogFileFlag.Name), LogLevel: ctx.GlobalInt(LogLevelFlag.Name), LogJSON: ctx.GlobalString(LogJSONFlag.Name), + Etherbase: ctx.GlobalString(EtherbaseFlag.Name), MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), AccountManager: GetAccountManager(ctx), VmDebug: ctx.GlobalBool(VMDebugFlag.Name), diff --git a/eth/backend.go b/eth/backend.go index c73e767921..fed0da0169 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -63,6 +63,7 @@ type Config struct { Shh bool Dial bool + Etherbase string MinerThreads int AccountManager *accounts.Manager @@ -140,6 +141,7 @@ type Ethereum struct { Mining bool DataDir string + etherbase common.Address clientVersion string ethVersionId int netVersionId int @@ -185,6 +187,7 @@ func New(config *Config) (*Ethereum, error) { eventMux: &event.TypeMux{}, accountManager: config.AccountManager, DataDir: config.DataDir, + etherbase: common.HexToAddress(config.Etherbase), clientVersion: config.Name, // TODO should separate from Name ethVersionId: config.ProtocolVersion, netVersionId: config.NetworkId, @@ -297,15 +300,31 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { } func (s *Ethereum) StartMining() error { - cb, err := s.accountManager.Coinbase() + eb, err := s.Etherbase() if err != nil { - servlogger.Errorf("Cannot start mining without coinbase: %v\n", err) - return fmt.Errorf("no coinbase: %v", err) + err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) + servlogger.Errorln(err) + return err + } - s.miner.Start(common.BytesToAddress(cb)) + + s.miner.Start(eb) return nil } +func (s *Ethereum) Etherbase() (eb common.Address, err error) { + eb = s.etherbase + if (eb == common.Address{}) { + var ebbytes []byte + ebbytes, err = s.accountManager.Primary() + eb = common.BytesToAddress(ebbytes) + if (eb == common.Address{}) { + err = fmt.Errorf("no accounts found") + } + } + return +} + func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) Miner() *miner.Miner { return s.miner } diff --git a/xeth/xeth.go b/xeth/xeth.go index bf30fc2fcc..3a9855bf34 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -250,8 +250,8 @@ func (self *XEth) IsListening() bool { } func (self *XEth) Coinbase() string { - cb, _ := self.backend.AccountManager().Coinbase() - return common.ToHex(cb) + eb, _ := self.backend.Etherbase() + return eb.Hex() } func (self *XEth) NumberToHuman(balance string) string { From e29396b6915a27d3e44be45fe9e540c6ef39f1dd Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Fri, 27 Mar 2015 09:36:18 +0100 Subject: [PATCH 20/25] Use ExtraDB for storage. Fixes #577 --- cmd/geth/admin.go | 5 ++-- cmd/geth/js.go | 3 +- cmd/utils/flags.go | 3 +- rpc/api.go | 23 ++++------------ rpc/api_test.go | 69 +++++++++++++++++++++++----------------------- rpc/http.go | 4 +-- xeth/xeth.go | 10 +++++++ 7 files changed, 56 insertions(+), 61 deletions(-) diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go index 139395dad9..3a58b88814 100644 --- a/cmd/geth/admin.go +++ b/cmd/geth/admin.go @@ -9,10 +9,10 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/xeth" "github.com/robertkrimen/otto" ) @@ -69,14 +69,13 @@ func (js *jsre) startRPC(call otto.FunctionCall) otto.Value { fmt.Println(err) return otto.FalseValue() } - dataDir := js.ethereum.DataDir l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port)) if err != nil { fmt.Printf("Can't listen on %s:%d: %v", addr, port, err) return otto.FalseValue() } - go http.Serve(l, rpc.JSONRPC(xeth.New(js.ethereum, nil), dataDir)) + go http.Serve(l, rpc.JSONRPC(xeth.New(js.ethereum, nil))) return otto.TrueValue() } diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 8e88a1c543..59a8469fa3 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -91,8 +91,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath string, interactive bool) *jsre { func (js *jsre) apiBindings() { - ethApi := rpc.NewEthereumApi(js.xeth, js.ethereum.DataDir) - ethApi.Close() + ethApi := rpc.NewEthereumApi(js.xeth) //js.re.Bind("jeth", rpc.NewJeth(ethApi, js.re.ToVal)) jeth := rpc.NewJeth(ethApi, js.re.ToVal, js.re) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f948cdb06b..ea11cb1580 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -251,11 +251,10 @@ func GetAccountManager(ctx *cli.Context) *accounts.Manager { func StartRPC(eth *eth.Ethereum, ctx *cli.Context) { addr := ctx.GlobalString(RPCListenAddrFlag.Name) port := ctx.GlobalInt(RPCPortFlag.Name) - dataDir := ctx.GlobalString(DataDirFlag.Name) fmt.Println("Starting RPC on port: ", port) 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)) + go http.Serve(l, rpc.JSONRPC(xeth.New(eth, nil))) } diff --git a/rpc/api.go b/rpc/api.go index aa5b54199f..f2915f658e 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -3,13 +3,11 @@ package rpc import ( "encoding/json" "math/big" - "path" "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/xeth" ) @@ -19,15 +17,9 @@ type EthereumApi struct { db common.Database } -func NewEthereumApi(xeth *xeth.XEth, dataDir string) *EthereumApi { - // What about when dataDir is empty? - db, err := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps")) - if err != nil { - panic(err) - } +func NewEthereumApi(xeth *xeth.XEth) *EthereumApi { api := &EthereumApi{ eth: xeth, - db: db, } return api @@ -44,10 +36,6 @@ func (api *EthereumApi) xethAtStateNum(num int64) *xeth.XEth { return api.xeth().AtStateNum(num) } -func (api *EthereumApi) Close() { - api.db.Close() -} - func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { // Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC rpclogger.Debugf("%s %s", req.Method, req.Params) @@ -370,7 +358,8 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err return err } - api.db.Put([]byte(args.Database+args.Key), args.Value) + api.xeth().DbPut([]byte(args.Database+args.Key), args.Value) + *reply = true case "db_getString": args := new(DbArgs) @@ -382,7 +371,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err return err } - res, _ := api.db.Get([]byte(args.Database + args.Key)) + res, _ := api.xeth().DbGet([]byte(args.Database + args.Key)) *reply = string(res) case "db_putHex": args := new(DbHexArgs) @@ -394,7 +383,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err return err } - api.db.Put([]byte(args.Database+args.Key), args.Value) + api.xeth().DbPut([]byte(args.Database+args.Key), args.Value) *reply = true case "db_getHex": args := new(DbHexArgs) @@ -406,7 +395,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err return err } - res, _ := api.db.Get([]byte(args.Database + args.Key)) + res, _ := api.xeth().DbGet([]byte(args.Database + args.Key)) *reply = common.ToHex(res) case "shh_version": *reply = api.xeth().WhisperVersion() diff --git a/rpc/api_test.go b/rpc/api_test.go index a00c2f3f1e..ac9b67fac8 100644 --- a/rpc/api_test.go +++ b/rpc/api_test.go @@ -6,7 +6,7 @@ import ( "testing" // "time" - "github.com/ethereum/go-ethereum/xeth" + // "github.com/ethereum/go-ethereum/xeth" ) func TestWeb3Sha3(t *testing.T) { @@ -26,49 +26,48 @@ func TestWeb3Sha3(t *testing.T) { } } -func TestDbStr(t *testing.T) { - jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}` - jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}` - expected := "myString" +// func TestDbStr(t *testing.T) { +// jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}` +// jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}` +// expected := "myString" - xeth := &xeth.XEth{} - api := NewEthereumApi(xeth, "") - defer api.db.Close() - var response interface{} +// xeth := &xeth.XEth{} +// api := NewEthereumApi(xeth) +// var response interface{} - var req RpcRequest - json.Unmarshal([]byte(jsonput), &req) - _ = api.GetRequestReply(&req, &response) +// var req RpcRequest +// json.Unmarshal([]byte(jsonput), &req) +// _ = api.GetRequestReply(&req, &response) - json.Unmarshal([]byte(jsonget), &req) - _ = api.GetRequestReply(&req, &response) +// json.Unmarshal([]byte(jsonget), &req) +// _ = api.GetRequestReply(&req, &response) - if response.(string) != expected { - t.Errorf("Expected %s got %s", expected, response) - } -} +// if response.(string) != expected { +// t.Errorf("Expected %s got %s", expected, response) +// } +// } -func TestDbHexStr(t *testing.T) { - jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}` - jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}` - expected := "0xbeef" +// func TestDbHexStr(t *testing.T) { +// jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}` +// jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}` +// expected := "0xbeef" - xeth := &xeth.XEth{} - api := NewEthereumApi(xeth, "") - defer api.db.Close() - var response interface{} +// xeth := &xeth.XEth{} +// api := NewEthereumApi(xeth) +// defer api.db.Close() +// var response interface{} - var req RpcRequest - json.Unmarshal([]byte(jsonput), &req) - _ = api.GetRequestReply(&req, &response) +// var req RpcRequest +// json.Unmarshal([]byte(jsonput), &req) +// _ = api.GetRequestReply(&req, &response) - json.Unmarshal([]byte(jsonget), &req) - _ = api.GetRequestReply(&req, &response) +// json.Unmarshal([]byte(jsonget), &req) +// _ = api.GetRequestReply(&req, &response) - if response.(string) != expected { - t.Errorf("Expected %s got %s", expected, response) - } -} +// if response.(string) != expected { +// t.Errorf("Expected %s got %s", expected, response) +// } +// } // func TestFilterClose(t *testing.T) { // t.Skip() diff --git a/rpc/http.go b/rpc/http.go index 3dfb677815..879ffce3b9 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -18,8 +18,8 @@ const ( ) // JSONRPC returns a handler that implements the Ethereum JSON-RPC API. -func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler { - api := NewEthereumApi(pipe, dataDir) +func JSONRPC(pipe *xeth.XEth) http.Handler { + api := NewEthereumApi(pipe) return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { // TODO this needs to be configurable diff --git a/xeth/xeth.go b/xeth/xeth.go index bf30fc2fcc..8bdeddbc96 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -209,6 +209,16 @@ func (self *XEth) Accounts() []string { return accountAddresses } +func (self *XEth) DbPut(key, val []byte) bool { + self.backend.ExtraDb().Put(key, val) + return true +} + +func (self *XEth) DbGet(key []byte) ([]byte, error) { + val, err := self.backend.ExtraDb().Get(key) + return val, err +} + func (self *XEth) PeerCount() int { return self.backend.PeerCount() } From eb102bf4bb0bff773824ff467fbb2e49c1f6939b Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 27 Mar 2015 12:14:00 +0100 Subject: [PATCH 21/25] Etherbase => etherbase --- cmd/utils/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 12d4df9230..2a3e2f4476 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -97,7 +97,7 @@ var ( Usage: "Enable mining", } EtherbaseFlag = cli.StringFlag{ - Name: "Etherbase", + Name: "etherbase", Usage: "public address for block mining rewards. By default the address of your primary account is used", Value: "primary", } From 00f8319faf95e53333a9573fdc9e49df23238607 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 27 Mar 2015 14:22:38 +0100 Subject: [PATCH 22/25] Explicitly check memory's data store. #515 --- core/vm/memory.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/vm/memory.go b/core/vm/memory.go index dd47fa1b50..f5984d82f1 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -15,10 +15,17 @@ func NewMemory() *Memory { } func (m *Memory) Set(offset, size uint64, value []byte) { + // If the length of the store is 0 this is a complete failure + // memory size is set prior to calling this method so enough size + // should always be available. + if len(m.store) == 0 { + panic("INVALID memory: store empty") + } + value = common.RightPadBytes(value, int(size)) totSize := offset + size - lenSize := uint64(len(m.store) - 1) + lenSize := int64(len(m.store) - 1) if totSize > lenSize { // Calculate the diff between the sizes diff := totSize - lenSize From 8a22cd5e6ca88eef0207cfb597175f720d6deda3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 27 Mar 2015 16:09:57 +0100 Subject: [PATCH 23/25] Removed defer/panic. #503 --- core/vm/gas.go | 164 ++++++++++++++++++++++++++-------------------- core/vm/memory.go | 40 ++++++----- core/vm/stack.go | 10 ++- core/vm/vm.go | 88 +++++++++++++++++-------- 4 files changed, 181 insertions(+), 121 deletions(-) diff --git a/core/vm/gas.go b/core/vm/gas.go index c4d5e4c4e3..e64810cd5f 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -1,11 +1,9 @@ package vm -import "math/big" - -type req struct { - stack int - gas *big.Int -} +import ( + "fmt" + "math/big" +) var ( GasQuickStep = big.NewInt(2) @@ -56,75 +54,30 @@ var ( GasCopyWord = big.NewInt(3) ) -var _baseCheck = map[OpCode]req{ - // Req stack Gas price - ADD: {2, GasFastestStep}, - LT: {2, GasFastestStep}, - GT: {2, GasFastestStep}, - SLT: {2, GasFastestStep}, - SGT: {2, GasFastestStep}, - EQ: {2, GasFastestStep}, - ISZERO: {1, GasFastestStep}, - SUB: {2, GasFastestStep}, - AND: {2, GasFastestStep}, - OR: {2, GasFastestStep}, - XOR: {2, GasFastestStep}, - NOT: {1, GasFastestStep}, - BYTE: {2, GasFastestStep}, - CALLDATALOAD: {1, GasFastestStep}, - CALLDATACOPY: {3, GasFastestStep}, - MLOAD: {1, GasFastestStep}, - MSTORE: {2, GasFastestStep}, - MSTORE8: {2, GasFastestStep}, - CODECOPY: {3, GasFastestStep}, - MUL: {2, GasFastStep}, - DIV: {2, GasFastStep}, - SDIV: {2, GasFastStep}, - MOD: {2, GasFastStep}, - SMOD: {2, GasFastStep}, - SIGNEXTEND: {2, GasFastStep}, - ADDMOD: {3, GasMidStep}, - MULMOD: {3, GasMidStep}, - JUMP: {1, GasMidStep}, - JUMPI: {2, GasSlowStep}, - EXP: {2, GasSlowStep}, - ADDRESS: {0, GasQuickStep}, - ORIGIN: {0, GasQuickStep}, - CALLER: {0, GasQuickStep}, - CALLVALUE: {0, GasQuickStep}, - CODESIZE: {0, GasQuickStep}, - GASPRICE: {0, GasQuickStep}, - COINBASE: {0, GasQuickStep}, - TIMESTAMP: {0, GasQuickStep}, - NUMBER: {0, GasQuickStep}, - CALLDATASIZE: {0, GasQuickStep}, - DIFFICULTY: {0, GasQuickStep}, - GASLIMIT: {0, GasQuickStep}, - POP: {0, GasQuickStep}, - PC: {0, GasQuickStep}, - MSIZE: {0, GasQuickStep}, - GAS: {0, GasQuickStep}, - BLOCKHASH: {1, GasExtStep}, - BALANCE: {0, GasExtStep}, - EXTCODESIZE: {1, GasExtStep}, - EXTCODECOPY: {4, GasExtStep}, - SLOAD: {1, GasStorageGet}, - SSTORE: {2, Zero}, - SHA3: {1, GasSha3Base}, - CREATE: {3, GasCreate}, - CALL: {7, GasCall}, - CALLCODE: {7, GasCall}, - JUMPDEST: {0, GasJumpDest}, - SUICIDE: {1, Zero}, - RETURN: {2, Zero}, -} +func baseCheck(op OpCode, stack *stack, gas *big.Int) error { + // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit + // PUSH is also allowed to calculate the same price for all PUSHes + // DUP requirements are handled elsewhere (except for the stack limit check) + if op >= PUSH1 && op <= PUSH32 { + op = PUSH1 + } + if op >= SWAP1 && op <= SWAP16 { + op = SWAP1 + } -func baseCheck(op OpCode, stack *stack, gas *big.Int) { if r, ok := _baseCheck[op]; ok { - stack.require(r.stack) + err := stack.require(r.stackPop) + if err != nil { + return err + } + + if r.stackPush && len(stack.data)-r.stackPop == 1024 { + return fmt.Errorf("stack limit reached (%d)", maxStack) + } gas.Add(gas, r.gas) } + return nil } func toWordSize(size *big.Int) *big.Int { @@ -133,3 +86,74 @@ func toWordSize(size *big.Int) *big.Int { tmp.Div(tmp, u256(32)) return tmp } + +type req struct { + stackPop int + gas *big.Int + stackPush bool +} + +var _baseCheck = map[OpCode]req{ + // opcode | stack pop | gas price | stack push + ADD: {2, GasFastestStep, true}, + LT: {2, GasFastestStep, true}, + GT: {2, GasFastestStep, true}, + SLT: {2, GasFastestStep, true}, + SGT: {2, GasFastestStep, true}, + EQ: {2, GasFastestStep, true}, + ISZERO: {1, GasFastestStep, true}, + SUB: {2, GasFastestStep, true}, + AND: {2, GasFastestStep, true}, + OR: {2, GasFastestStep, true}, + XOR: {2, GasFastestStep, true}, + NOT: {1, GasFastestStep, true}, + BYTE: {2, GasFastestStep, true}, + CALLDATALOAD: {1, GasFastestStep, true}, + CALLDATACOPY: {3, GasFastestStep, true}, + MLOAD: {1, GasFastestStep, true}, + MSTORE: {2, GasFastestStep, false}, + MSTORE8: {2, GasFastestStep, false}, + CODECOPY: {3, GasFastestStep, false}, + MUL: {2, GasFastStep, true}, + DIV: {2, GasFastStep, true}, + SDIV: {2, GasFastStep, true}, + MOD: {2, GasFastStep, true}, + SMOD: {2, GasFastStep, true}, + SIGNEXTEND: {2, GasFastStep, true}, + ADDMOD: {3, GasMidStep, true}, + MULMOD: {3, GasMidStep, true}, + JUMP: {1, GasMidStep, false}, + JUMPI: {2, GasSlowStep, false}, + EXP: {2, GasSlowStep, true}, + ADDRESS: {0, GasQuickStep, true}, + ORIGIN: {0, GasQuickStep, true}, + CALLER: {0, GasQuickStep, true}, + CALLVALUE: {0, GasQuickStep, true}, + CODESIZE: {0, GasQuickStep, true}, + GASPRICE: {0, GasQuickStep, true}, + COINBASE: {0, GasQuickStep, true}, + TIMESTAMP: {0, GasQuickStep, true}, + NUMBER: {0, GasQuickStep, true}, + CALLDATASIZE: {0, GasQuickStep, true}, + DIFFICULTY: {0, GasQuickStep, true}, + GASLIMIT: {0, GasQuickStep, true}, + POP: {1, GasQuickStep, false}, + PC: {0, GasQuickStep, true}, + MSIZE: {0, GasQuickStep, true}, + GAS: {0, GasQuickStep, true}, + BLOCKHASH: {1, GasExtStep, true}, + BALANCE: {0, GasExtStep, true}, + EXTCODESIZE: {1, GasExtStep, true}, + EXTCODECOPY: {4, GasExtStep, false}, + SLOAD: {1, GasStorageGet, true}, + SSTORE: {2, Zero, false}, + SHA3: {1, GasSha3Base, true}, + CREATE: {3, GasCreate, true}, + CALL: {7, GasCall, true}, + CALLCODE: {7, GasCall, true}, + JUMPDEST: {0, GasJumpDest, false}, + SUICIDE: {1, Zero, false}, + RETURN: {2, Zero, false}, + PUSH1: {0, GasFastStep, true}, + DUP1: {0, Zero, true}, +} diff --git a/core/vm/memory.go b/core/vm/memory.go index f5984d82f1..a80a6aae08 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -15,28 +15,32 @@ func NewMemory() *Memory { } func (m *Memory) Set(offset, size uint64, value []byte) { - // If the length of the store is 0 this is a complete failure - // memory size is set prior to calling this method so enough size - // should always be available. - if len(m.store) == 0 { + // length of store may never be less than offset + size. + // The store should be resized PRIOR to setting the memory + if size > uint64(len(m.store)) { panic("INVALID memory: store empty") } - value = common.RightPadBytes(value, int(size)) - - totSize := offset + size - lenSize := int64(len(m.store) - 1) - if totSize > lenSize { - // Calculate the diff between the sizes - diff := totSize - lenSize - if diff > 0 { - // Create a new empty slice and append it - newSlice := make([]byte, diff-1) - // Resize slice - m.store = append(m.store, newSlice...) - } + // It's possible the offset is greater than 0 and size equals 0. This is because + // the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP) + if size > 0 { + copy(m.store[offset:offset+size], common.RightPadBytes(value, int(size))) } - copy(m.store[offset:offset+size], value) + + /* + totSize := offset + size + lenSize := uint64(len(m.store) - 1) + if totSize > lenSize { + // Calculate the diff between the sizes + diff := totSize - lenSize + if diff > 0 { + // Create a new empty slice and append it + newSlice := make([]byte, diff-1) + // Resize slice + m.store = append(m.store, newSlice...) + } + } + */ } func (m *Memory) Resize(size uint64) { diff --git a/core/vm/stack.go b/core/vm/stack.go index 1e093476bf..1686377082 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -17,10 +17,7 @@ type stack struct { } func (st *stack) push(d *big.Int) { - if len(st.data) == maxStack { - panic(fmt.Sprintf("stack limit reached (%d)", maxStack)) - } - + // NOTE push limit (1024) is checked in baseCheck stackItem := new(big.Int).Set(d) if len(st.data) > st.ptr { st.data[st.ptr] = stackItem @@ -52,10 +49,11 @@ func (st *stack) peek() *big.Int { return st.data[st.len()-1] } -func (st *stack) require(n int) { +func (st *stack) require(n int) error { if st.len() < n { - panic(fmt.Sprintf("stack underflow (%d <=> %d)", len(st.data), n)) + return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n) } + return nil } func (st *stack) Print() { diff --git a/core/vm/vm.go b/core/vm/vm.go index 562689dca1..0618446c27 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -45,20 +45,32 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address().Bytes()[:4], context.Address(), len(code), context.Gas, callData).Endl() - if self.Recoverable { - // Recover from any require exception - defer func() { - if r := recover(); r != nil { - self.Printf(" %v", r).Endl() + /* + if self.Recoverable { + // Recover from any require exception + defer func() { + if r := recover(); r != nil { + self.Printf(" %v", r).Endl() - context.UseGas(context.Gas) + context.UseGas(context.Gas) - ret = context.Return(nil) + ret = context.Return(nil) - err = fmt.Errorf("%v", r) - } - }() - } + err = fmt.Errorf("%v", r) + } + }() + } + */ + + defer func() { + if err != nil { + self.Printf(" %v", err).Endl() + // In case of a VM exception (known exceptions) all gas consumed (panics NOT included). + context.UseGas(context.Gas) + + ret = context.Return(nil) + } + }() if context.CodeAddr != nil { if p := Precompiled[context.CodeAddr.Str()]; p != nil { @@ -76,18 +88,20 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { step = 0 statedb = self.env.State() - jump = func(from uint64, to *big.Int) { + jump = func(from uint64, to *big.Int) error { p := to.Uint64() nop := context.GetOp(p) if !destinations.Has(p) { - panic(fmt.Sprintf("invalid jump destination (%v) %v", nop, p)) + return fmt.Errorf("invalid jump destination (%v) %v", nop, p) } self.Printf(" ~> %v", to) pc = to.Uint64() self.Endl() + + return nil } ) @@ -105,7 +119,10 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { op = context.GetOp(pc) self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len()) - newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) + newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) + if err != nil { + return nil, err + } self.Printf("(g) %-3v (%v)", gas, context.Gas) @@ -600,14 +617,18 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { self.Printf(" {0x%x : 0x%x}", loc, val.Bytes()) case JUMP: - jump(pc, stack.pop()) + if err := jump(pc, stack.pop()); err != nil { + return nil, err + } continue case JUMPI: pos, cond := stack.pop(), stack.pop() if cond.Cmp(common.BigTrue) >= 0 { - jump(pc, pos) + if err := jump(pc, pos); err != nil { + return nil, err + } continue } @@ -720,7 +741,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { default: self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl() - panic(fmt.Errorf("Invalid opcode %x", op)) + return nil, fmt.Errorf("Invalid opcode %x", op) } pc++ @@ -729,28 +750,38 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } } -func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int) { +func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) ) - baseCheck(op, stack, gas) + err := baseCheck(op, stack, gas) + if err != nil { + return nil, nil, err + } // stack Check, memory resize & gas phase switch op { - case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: - gas.Set(GasFastestStep) case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: n := int(op - SWAP1 + 2) - stack.require(n) + err := stack.require(n) + if err != nil { + return nil, nil, err + } gas.Set(GasFastestStep) case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: n := int(op - DUP1 + 1) - stack.require(n) + err := stack.require(n) + if err != nil { + return nil, nil, err + } gas.Set(GasFastestStep) case LOG0, LOG1, LOG2, LOG3, LOG4: n := int(op - LOG0) - stack.require(n + 2) + err := stack.require(n + 2) + if err != nil { + return nil, nil, err + } mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1] @@ -762,7 +793,10 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo case EXP: gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), GasExpByte)) case SSTORE: - stack.require(2) + err := stack.require(2) + if err != nil { + return nil, nil, err + } var g *big.Int y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] @@ -853,7 +887,7 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo } } - return newMemSize, gas + return newMemSize, gas, nil } func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) { @@ -869,7 +903,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context * tmp := new(big.Int).Set(context.Gas) - panic(OOG(gas, tmp).Error()) + return nil, OOG(gas, tmp) } } From df648cbc60f53dd334bd41b713e08ceac08212a0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 27 Mar 2015 16:11:19 +0100 Subject: [PATCH 24/25] Removed comments --- core/vm/memory.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/core/vm/memory.go b/core/vm/memory.go index a80a6aae08..b77d486ebb 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -26,21 +26,6 @@ func (m *Memory) Set(offset, size uint64, value []byte) { if size > 0 { copy(m.store[offset:offset+size], common.RightPadBytes(value, int(size))) } - - /* - totSize := offset + size - lenSize := uint64(len(m.store) - 1) - if totSize > lenSize { - // Calculate the diff between the sizes - diff := totSize - lenSize - if diff > 0 { - // Create a new empty slice and append it - newSlice := make([]byte, diff-1) - // Resize slice - m.store = append(m.store, newSlice...) - } - } - */ } func (m *Memory) Resize(size uint64) { From 3ea8c7301e6227467e39ae3daa0f382f06b16fba Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 27 Mar 2015 16:53:05 +0100 Subject: [PATCH 25/25] PUSH gas fix --- core/vm/gas.go | 8 ++++---- core/vm/vm.go | 18 +----------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/core/vm/gas.go b/core/vm/gas.go index e64810cd5f..2d5d7ae186 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -61,8 +61,8 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error { if op >= PUSH1 && op <= PUSH32 { op = PUSH1 } - if op >= SWAP1 && op <= SWAP16 { - op = SWAP1 + if op >= DUP1 && op <= DUP16 { + op = DUP1 } if r, ok := _baseCheck[op]; ok { @@ -71,7 +71,7 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error { return err } - if r.stackPush && len(stack.data)-r.stackPop == 1024 { + if r.stackPush && len(stack.data)-r.stackPop+1 > 1024 { return fmt.Errorf("stack limit reached (%d)", maxStack) } @@ -154,6 +154,6 @@ var _baseCheck = map[OpCode]req{ JUMPDEST: {0, GasJumpDest, false}, SUICIDE: {1, Zero, false}, RETURN: {2, Zero, false}, - PUSH1: {0, GasFastStep, true}, + PUSH1: {0, GasFastestStep, true}, DUP1: {0, Zero, true}, } diff --git a/core/vm/vm.go b/core/vm/vm.go index 0618446c27..88fbdf7638 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -45,23 +45,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address().Bytes()[:4], context.Address(), len(code), context.Gas, callData).Endl() - /* - if self.Recoverable { - // Recover from any require exception - defer func() { - if r := recover(); r != nil { - self.Printf(" %v", r).Endl() - - context.UseGas(context.Gas) - - ret = context.Return(nil) - - err = fmt.Errorf("%v", r) - } - }() - } - */ - + // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if err != nil { self.Printf(" %v", err).Endl()