The status-light-stable branch of ethereum has been vendored

This commit is contained in:
Victor Farazdagi 2016-07-26 05:54:35 +03:00
parent 96c1b5ec40
commit a26a3ad649
111 changed files with 28816 additions and 1056 deletions

View File

@ -0,0 +1,57 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package backends
import (
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"golang.org/x/net/context"
)
// This nil assignment ensures compile time that nilBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*nilBackend)(nil)
// nilBackend implements bind.ContractBackend, but panics on any method call.
// Its sole purpose is to support the binding tests to construct the generated
// wrappers without calling any methods on them.
type nilBackend struct{}
func (*nilBackend) ContractCall(context.Context, common.Address, []byte, bool) ([]byte, error) {
panic("not implemented")
}
func (*nilBackend) EstimateGasLimit(context.Context, common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
panic("not implemented")
}
func (*nilBackend) HasCode(context.Context, common.Address, bool) (bool, error) {
panic("not implemented")
}
func (*nilBackend) SuggestGasPrice(context.Context) (*big.Int, error) { panic("not implemented") }
func (*nilBackend) PendingAccountNonce(context.Context, common.Address) (uint64, error) {
panic("not implemented")
}
func (*nilBackend) SendTransaction(context.Context, *types.Transaction) error {
panic("not implemented")
}
// NewNilBackend creates a new binding backend that can be used for instantiation
// but will panic on any invocation. Its sole purpose is to help testing.
func NewNilBackend() bind.ContractBackend {
return new(nilBackend)
}

View File

@ -0,0 +1,260 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package backends
import (
"encoding/json"
"fmt"
"math/big"
"sync"
"sync/atomic"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
)
// This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*rpcBackend)(nil)
// rpcBackend implements bind.ContractBackend, and acts as the data provider to
// Ethereum contracts bound to Go structs. It uses an RPC connection to delegate
// all its functionality.
//
// Note: The current implementation is a blocking one. This should be replaced
// by a proper async version when a real RPC client is created.
type rpcBackend struct {
client rpc.Client // RPC client connection to interact with an API server
autoid uint32 // ID number to use for the next API request
lock sync.Mutex // Singleton access until we get to request multiplexing
}
// NewRPCBackend creates a new binding backend to an RPC provider that can be
// used to interact with remote contracts.
func NewRPCBackend(client rpc.Client) bind.ContractBackend {
return &rpcBackend{
client: client,
}
}
// request is a JSON RPC request package assembled internally from the client
// method calls.
type request struct {
JSONRPC string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
ID int `json:"id"` // Auto incrementing ID number for this request
Method string `json:"method"` // Remote procedure name to invoke on the server
Params []interface{} `json:"params"` // List of parameters to pass through (keep types simple)
}
// response is a JSON RPC response package sent back from the API server.
type response struct {
JSONRPC string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
ID int `json:"id"` // Auto incrementing ID number for this request
Error *failure `json:"error"` // Any error returned by the remote side
Result json.RawMessage `json:"result"` // Whatever the remote side sends us in reply
}
// failure is a JSON RPC response error field sent back from the API server.
type failure struct {
Code int `json:"code"` // JSON RPC error code associated with the failure
Message string `json:"message"` // Specific error message of the failure
}
// request forwards an API request to the RPC server, and parses the response.
//
// This is currently painfully non-concurrent, but it will have to do until we
// find the time for niceties like this :P
func (b *rpcBackend) request(ctx context.Context, method string, params []interface{}) (json.RawMessage, error) {
b.lock.Lock()
defer b.lock.Unlock()
if ctx == nil {
ctx = context.Background()
}
// Ugly hack to serialize an empty list properly
if params == nil {
params = []interface{}{}
}
// Assemble the request object
reqID := int(atomic.AddUint32(&b.autoid, 1))
req := &request{
JSONRPC: "2.0",
ID: reqID,
Method: method,
Params: params,
}
if err := b.client.Send(req); err != nil {
return nil, err
}
res := new(response)
errc := make(chan error, 1)
go func() {
errc <- b.client.Recv(res)
}()
select {
case err := <-errc:
if err != nil {
return nil, err
}
case <-ctx.Done():
return nil, ctx.Err()
}
if res.Error != nil {
if res.Error.Message == bind.ErrNoCode.Error() {
return nil, bind.ErrNoCode
}
return nil, fmt.Errorf("remote error: %s", res.Error.Message)
}
return res.Result, nil
}
// HasCode implements ContractVerifier.HasCode by retrieving any code associated
// with the contract from the remote node, and checking its size.
func (b *rpcBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
// Execute the RPC code retrieval
block := "latest"
if pending {
block = "pending"
}
res, err := b.request(ctx, "eth_getCode", []interface{}{contract.Hex(), block})
if err != nil {
return false, err
}
var hex string
if err := json.Unmarshal(res, &hex); err != nil {
return false, err
}
// Convert the response back to a Go byte slice and return
return len(common.FromHex(hex)) > 0, nil
}
// ContractCall implements ContractCaller.ContractCall, delegating the execution of
// a contract call to the remote node, returning the reply to for local processing.
func (b *rpcBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
// Pack up the request into an RPC argument
args := struct {
To common.Address `json:"to"`
Data string `json:"data"`
}{
To: contract,
Data: common.ToHex(data),
}
// Execute the RPC call and retrieve the response
block := "latest"
if pending {
block = "pending"
}
res, err := b.request(ctx, "eth_call", []interface{}{args, block})
if err != nil {
return nil, err
}
var hex string
if err := json.Unmarshal(res, &hex); err != nil {
return nil, err
}
// Convert the response back to a Go byte slice and return
return common.FromHex(hex), nil
}
// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating
// the current account nonce retrieval to the remote node.
func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
res, err := b.request(ctx, "eth_getTransactionCount", []interface{}{account.Hex(), "pending"})
if err != nil {
return 0, err
}
var hex string
if err := json.Unmarshal(res, &hex); err != nil {
return 0, err
}
nonce, ok := new(big.Int).SetString(hex, 0)
if !ok {
return 0, fmt.Errorf("invalid nonce hex: %s", hex)
}
return nonce.Uint64(), nil
}
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the
// gas price oracle request to the remote node.
func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
res, err := b.request(ctx, "eth_gasPrice", nil)
if err != nil {
return nil, err
}
var hex string
if err := json.Unmarshal(res, &hex); err != nil {
return nil, err
}
price, ok := new(big.Int).SetString(hex, 0)
if !ok {
return nil, fmt.Errorf("invalid price hex: %s", hex)
}
return price, nil
}
// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating
// the gas estimation to the remote node.
func (b *rpcBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
// Pack up the request into an RPC argument
args := struct {
From common.Address `json:"from"`
To *common.Address `json:"to"`
Value *rpc.HexNumber `json:"value"`
Data string `json:"data"`
}{
From: sender,
To: contract,
Data: common.ToHex(data),
Value: rpc.NewHexNumber(value),
}
// Execute the RPC call and retrieve the response
res, err := b.request(ctx, "eth_estimateGas", []interface{}{args})
if err != nil {
return nil, err
}
var hex string
if err := json.Unmarshal(res, &hex); err != nil {
return nil, err
}
estimate, ok := new(big.Int).SetString(hex, 0)
if !ok {
return nil, fmt.Errorf("invalid estimate hex: %s", hex)
}
return estimate, nil
}
// SendTransaction implements ContractTransactor.SendTransaction, delegating the
// raw transaction injection to the remote node.
func (b *rpcBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
data, err := rlp.EncodeToBytes(tx)
if err != nil {
return err
}
res, err := b.request(ctx, "eth_sendRawTransaction", []interface{}{common.ToHex(data)})
if err != nil {
return err
}
var hex string
if err := json.Unmarshal(res, &hex); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,212 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package backends
import (
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"golang.org/x/net/context"
)
// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier)
var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)}
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow easily testing contract bindings.
type SimulatedBackend struct {
database ethdb.Database // In memory database to store our testing data
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
pendingBlock *types.Block // Currently pending block that will be imported on request
pendingState *state.StateDB // Currently pending state that will be the active on on request
}
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
// for testing purposes.
func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend {
database, _ := ethdb.NewMemDatabase()
core.WriteGenesisBlockForTesting(database, accounts...)
blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux))
backend := &SimulatedBackend{
database: database,
blockchain: blockchain,
}
backend.Rollback()
return backend
}
// Commit imports all the pending transactions as a single block and starts a
// fresh new state.
func (b *SimulatedBackend) Commit() {
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
}
b.Rollback()
}
// Rollback aborts all pending transactions, reverting to the last committed state.
func (b *SimulatedBackend) Rollback() {
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
}
// HasCode implements ContractVerifier.HasCode, checking whether there is any
// code associated with a certain account in the blockchain.
func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
if pending {
return len(b.pendingState.GetCode(contract)) > 0, nil
}
statedb, _ := b.blockchain.State()
return len(statedb.GetCode(contract)) > 0, nil
}
// ContractCall implements ContractCaller.ContractCall, executing the specified
// contract with the given input data.
func (b *SimulatedBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
// Create a copy of the current state db to screw around with
var (
block *types.Block
statedb *state.StateDB
)
if pending {
block, statedb = b.pendingBlock, b.pendingState.Copy()
} else {
block = b.blockchain.CurrentBlock()
statedb, _ = b.blockchain.State()
}
// If there's no code to interact with, respond with an appropriate error
if code := statedb.GetCode(contract); len(code) == 0 {
return nil, bind.ErrNoCode
}
// Set infinite balance to the a fake caller account
from := statedb.GetOrNewStateObject(common.Address{})
from.SetBalance(common.MaxBig)
// Assemble the call invocation to measure the gas usage
msg := callmsg{
from: from,
to: &contract,
gasPrice: new(big.Int),
gasLimit: common.MaxBig,
value: new(big.Int),
data: data,
}
// Execute the call and return
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
out, _, err := core.ApplyMessage(vmenv, msg, gaspool)
return out, err
}
// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving
// the nonce currently pending for the account.
func (b *SimulatedBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
}
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// chain doens't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil
}
// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the
// requested code against the currently pending block/state and returning the used
// gas.
func (b *SimulatedBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
// Create a copy of the currently pending state db to screw around with
var (
block = b.pendingBlock
statedb = b.pendingState.Copy()
)
// If there's no code to interact with, respond with an appropriate error
if contract != nil {
if code := statedb.GetCode(*contract); len(code) == 0 {
return nil, bind.ErrNoCode
}
}
// Set infinite balance to the a fake caller account
from := statedb.GetOrNewStateObject(sender)
from.SetBalance(common.MaxBig)
// Assemble the call invocation to measure the gas usage
msg := callmsg{
from: from,
to: contract,
gasPrice: new(big.Int),
gasLimit: common.MaxBig,
value: value,
data: data,
}
// Execute the call and return
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
_, gas, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return gas, err
}
// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
// transaction injection to the remote node.
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTx(tx)
}
block.AddTx(tx)
})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
return nil
}
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
from *state.StateObject
to *common.Address
gasLimit *big.Int
gasPrice *big.Int
value *big.Int
data []byte
}
func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil }
func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.to }
func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
func (m callmsg) Gas() *big.Int { return m.gasLimit }
func (m callmsg) Value() *big.Int { return m.value }
func (m callmsg) Data() []byte { return m.data }

View File

@ -56,27 +56,21 @@ var (
big_ts = reflect.TypeOf([]*big.Int(nil))
)
// U256 will ensure unsigned 256bit on big nums
// U256 converts a big Int into a 256bit EVM number.
func U256(n *big.Int) []byte {
return common.LeftPadBytes(common.U256(n).Bytes(), 32)
}
// S256 will ensure signed 256bit on big nums
func U2U256(n uint64) []byte {
return U256(big.NewInt(int64(n)))
}
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
func packNum(value reflect.Value) []byte {
switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return U2U256(value.Uint())
return U256(new(big.Int).SetUint64(value.Uint()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return U2U256(uint64(value.Int()))
return U256(big.NewInt(value.Int()))
case reflect.Ptr:
return U256(value.Interface().(*big.Int))
}
return nil
}

View File

@ -192,6 +192,7 @@ func (am *Manager) Lock(addr common.Address) error {
// shortens the active unlock timeout. If the address was previously unlocked
// indefinitely the timeout is not altered.
func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Duration) error {
a, key, err := am.getDecryptedKey(a, passphrase)
if err != nil {
return err
@ -231,7 +232,6 @@ func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Durati
}
func (am *Manager) syncAccounts(a string, key *Key) error {
for _, service := range *am.sync {
if whisperInstance, ok := service.(*whisper.Whisper); ok && key.WhisperEnabled {
err := whisperInstance.InjectIdentity(key.PrivateKey)

View File

@ -0,0 +1,136 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common/compiler"
)
var (
abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind")
binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)")
typFlag = flag.String("type", "", "Go struct name for the binding (default = package name)")
solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind")
solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested")
excFlag = flag.String("exc", "", "Comma separated types to exclude from binding")
pkgFlag = flag.String("pkg", "", "Go package name to generate the binding into")
outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)")
)
func main() {
// Parse and ensure all needed inputs are specified
flag.Parse()
if *abiFlag == "" && *solFlag == "" {
fmt.Printf("No contract ABI (--abi) or Solidity source (--sol) specified\n")
os.Exit(-1)
} else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && *solFlag != "" {
fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n")
os.Exit(-1)
}
if *pkgFlag == "" {
fmt.Printf("No destination Go package specified (--pkg)\n")
os.Exit(-1)
}
// If the entire solidity code was specified, build and bind based on that
var (
abis []string
bins []string
types []string
)
if *solFlag != "" {
// Generate the list of types to exclude from binding
exclude := make(map[string]bool)
for _, kind := range strings.Split(*excFlag, ",") {
exclude[strings.ToLower(kind)] = true
}
// Build the Solidity source into bindable components
solc, err := compiler.New(*solcFlag)
if err != nil {
fmt.Printf("Failed to locate Solidity compiler: %v\n", err)
os.Exit(-1)
}
source, err := ioutil.ReadFile(*solFlag)
if err != nil {
fmt.Printf("Failed to read Soldity source code: %v\n", err)
os.Exit(-1)
}
contracts, err := solc.Compile(string(source))
if err != nil {
fmt.Printf("Failed to build Solidity contract: %v\n", err)
os.Exit(-1)
}
// Gather all non-excluded contract for binding
for name, contract := range contracts {
if exclude[strings.ToLower(name)] {
continue
}
abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
abis = append(abis, string(abi))
bins = append(bins, contract.Code)
types = append(types, name)
}
} else {
// Otherwise load up the ABI, optional bytecode and type name from the parameters
abi, err := ioutil.ReadFile(*abiFlag)
if err != nil {
fmt.Printf("Failed to read input ABI: %v\n", err)
os.Exit(-1)
}
abis = append(abis, string(abi))
bin := []byte{}
if *binFlag != "" {
if bin, err = ioutil.ReadFile(*binFlag); err != nil {
fmt.Printf("Failed to read input bytecode: %v\n", err)
os.Exit(-1)
}
}
bins = append(bins, string(bin))
kind := *typFlag
if kind == "" {
kind = *pkgFlag
}
types = append(types, kind)
}
// Generate the contract binding
code, err := bind.Bind(types, abis, bins, *pkgFlag)
if err != nil {
fmt.Printf("Failed to generate ABI binding: %v\n", err)
os.Exit(-1)
}
// Either flush it out to a file or display on the standard output
if *outFlag == "" {
fmt.Printf("%s\n", code)
return
}
if err := ioutil.WriteFile(*outFlag, []byte(code), 0600); err != nil {
fmt.Printf("Failed to write ABI binding: %v\n", err)
os.Exit(-1)
}
}

View File

@ -0,0 +1,82 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// bootnode runs a bootstrap node for the Ethereum Discovery Protocol.
package main
import (
"crypto/ecdsa"
"flag"
"os"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/nat"
)
func main() {
var (
listenAddr = flag.String("addr", ":30301", "listen address")
genKey = flag.String("genkey", "", "generate a node key and quit")
nodeKeyFile = flag.String("nodekey", "", "private key filename")
nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)")
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
nodeKey *ecdsa.PrivateKey
err error
)
flag.Var(glog.GetVerbosity(), "verbosity", "log verbosity (0-9)")
flag.Var(glog.GetVModule(), "vmodule", "log verbosity pattern")
glog.SetToStderr(true)
flag.Parse()
if *genKey != "" {
key, err := crypto.GenerateKey()
if err != nil {
utils.Fatalf("could not generate key: %v", err)
}
if err := crypto.SaveECDSA(*genKey, key); err != nil {
utils.Fatalf("%v", err)
}
os.Exit(0)
}
natm, err := nat.Parse(*natdesc)
if err != nil {
utils.Fatalf("-nat: %v", err)
}
switch {
case *nodeKeyFile == "" && *nodeKeyHex == "":
utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key")
case *nodeKeyFile != "" && *nodeKeyHex != "":
utils.Fatalf("Options -nodekey and -nodekeyhex are mutually exclusive")
case *nodeKeyFile != "":
if nodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil {
utils.Fatalf("-nodekey: %v", err)
}
case *nodeKeyHex != "":
if nodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil {
utils.Fatalf("-nodekeyhex: %v", err)
}
}
if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil {
utils.Fatalf("%v", err)
}
select {}
}

View File

@ -0,0 +1,51 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// disasm is a pretty-printer for EVM bytecode.
package main
import (
"fmt"
"io/ioutil"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)
func main() {
code, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
code = common.Hex2Bytes(string(code[:len(code)-1]))
fmt.Printf("%x\n", code)
for pc := uint64(0); pc < uint64(len(code)); pc++ {
op := vm.OpCode(code[pc])
fmt.Printf("%-5d %v", pc, op)
switch op {
case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, vm.PUSH30, vm.PUSH31, vm.PUSH32:
a := uint64(op) - uint64(vm.PUSH1) + 1
fmt.Printf(" => %x", code[pc+1:pc+1+a])
pc += a
}
fmt.Println()
}
}

View File

@ -0,0 +1 @@
60006102ff5360003560001a60008114156103395760013560405260216040516020025990590160009052606052604051602002816060513760405160200281019050506002604051121561005957604051602002606051f35b604051602002599059016000905260a052600060c052604051602002599059016000905260e0526000610100526001610120525b604051610120511215610109576060515161012051602002606051015112156100d8576101205160200260605101516101005160200260e051015260016101005101610100526100f9565b61012051602002606051015160c05160200260a0510152600160c0510160c0525b600161012051016101205261008d565b60216020599059016000905260c051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260a0518260005b8381101561016657808301518186015260208101905061014b565b50505050825160200281019050604059905901600090526102405281610240515283602061024051015261024051905090509050905060c05160200280599059016000905281816020850151855160003060195a03f1508090509050905060a05260216020599059016000905261010051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260e0518260005b8381101561022557808301518186015260208101905061020a565b50505050825160200281019050604059905901600090526102c052816102c051528360206102c05101526102c05190509050905090506101005160200280599059016000905281816020850151855160003060195a03f1508090509050905060e05260405160200259905901600090526102e0526000610120525b610100516101205112156102d7576101205160200260e0510151610120516020026102e051015260016101205101610120526102a0565b60605151610100516020026102e05101526000610120525b60c05161012051121561032d576101205160200260a05101516101205160016101005101016020026102e051015260016101205101610120526102ef565b6040516020026102e051f35b50

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,276 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// evm executes EVM code snippets.
package main
import (
"fmt"
"math/big"
"os"
"runtime"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/urfave/cli.v1"
)
var (
app *cli.App
DebugFlag = cli.BoolFlag{
Name: "debug",
Usage: "output full trace logs",
}
ForceJitFlag = cli.BoolFlag{
Name: "forcejit",
Usage: "forces jit compilation",
}
DisableJitFlag = cli.BoolFlag{
Name: "nojit",
Usage: "disabled jit compilation",
}
CodeFlag = cli.StringFlag{
Name: "code",
Usage: "EVM code",
}
GasFlag = cli.StringFlag{
Name: "gas",
Usage: "gas limit for the evm",
Value: "10000000000",
}
PriceFlag = cli.StringFlag{
Name: "price",
Usage: "price set for the evm",
Value: "0",
}
ValueFlag = cli.StringFlag{
Name: "value",
Usage: "value set for the evm",
Value: "0",
}
DumpFlag = cli.BoolFlag{
Name: "dump",
Usage: "dumps the state after the run",
}
InputFlag = cli.StringFlag{
Name: "input",
Usage: "input for the EVM",
}
SysStatFlag = cli.BoolFlag{
Name: "sysstat",
Usage: "display system stats",
}
VerbosityFlag = cli.IntFlag{
Name: "verbosity",
Usage: "sets the verbosity level",
}
CreateFlag = cli.BoolFlag{
Name: "create",
Usage: "indicates the action should be create rather than call",
}
)
func init() {
app = utils.NewApp("0.2", "the evm command line interface")
app.Flags = []cli.Flag{
CreateFlag,
DebugFlag,
VerbosityFlag,
ForceJitFlag,
DisableJitFlag,
SysStatFlag,
CodeFlag,
GasFlag,
PriceFlag,
ValueFlag,
DumpFlag,
InputFlag,
}
app.Action = run
}
func run(ctx *cli.Context) error {
glog.SetToStderr(true)
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
sender := statedb.CreateAccount(common.StringToAddress("sender"))
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
Debug: ctx.GlobalBool(DebugFlag.Name),
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
})
tstart := time.Now()
var (
ret []byte
err error
)
if ctx.GlobalBool(CreateFlag.Name) {
input := append(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
ret, _, err = vmenv.Create(
sender,
input,
common.Big(ctx.GlobalString(GasFlag.Name)),
common.Big(ctx.GlobalString(PriceFlag.Name)),
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
} else {
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
ret, err = vmenv.Call(
sender,
receiver.Address(),
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
common.Big(ctx.GlobalString(GasFlag.Name)),
common.Big(ctx.GlobalString(PriceFlag.Name)),
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
}
vmdone := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit()
fmt.Println(string(statedb.Dump()))
}
vm.StdErrFormat(vmenv.StructLogs())
if ctx.GlobalBool(SysStatFlag.Name) {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", vmdone)
fmt.Printf(`alloc: %d
tot alloc: %d
no. malloc: %d
heap alloc: %d
heap objs: %d
num gc: %d
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
}
fmt.Printf("OUT: 0x%x", ret)
if err != nil {
fmt.Printf(" error: %v", err)
}
fmt.Println()
return nil
}
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
type VMEnv struct {
state *state.StateDB
block *types.Block
transactor *common.Address
value *big.Int
depth int
Gas *big.Int
time *big.Int
logs []vm.StructLog
evm *vm.EVM
}
func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
env := &VMEnv{
state: state,
transactor: &transactor,
value: value,
time: big.NewInt(time.Now().Unix()),
}
cfg.Logger.Collector = env
env.evm = vm.New(env, cfg)
return env
}
// ruleSet implements vm.RuleSet and will always default to the homestead rule set.
type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() }
func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
func (self *VMEnv) Origin() common.Address { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) Time() *big.Int { return self.time }
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash {
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
return self.block.Hash()
}
return common.Hash{}
}
func (self *VMEnv) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}
func (self *VMEnv) StructLogs() []vm.StructLog {
return self.logs
}
func (self *VMEnv) AddLog(log *vm.Log) {
self.state.AddLog(log)
}
func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
core.Transfer(from, to, amount)
}
func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
self.Gas = gas
return core.Call(self, caller, addr, data, gas, price, value)
}
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.CallCode(self, caller, addr, data, gas, price, value)
}
func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
return core.DelegateCall(self, caller, addr, data, gas, price)
}
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return core.Create(self, caller, data, gas, price, value)
}

View File

@ -0,0 +1,354 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"io/ioutil"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/node"
)
var (
walletCommand = cli.Command{
Name: "wallet",
Usage: "ethereum presale wallet",
Subcommands: []cli.Command{
{
Action: importWallet,
Name: "import",
Usage: "import ethereum presale wallet",
},
},
Description: `
get wallet import /path/to/my/presale.wallet
will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.
`}
accountCommand = cli.Command{
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.
' help' shows a list of subcommands or help for one subcommand.
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 <DATADIR>/keystore.
It is safe to transfer the entire directory or the individual keys therein
between ethereum nodes by simply copying.
Make sure you backup your keys regularly.
In order to use your account to send transactions, you need to unlock them using
the '--unlock' option. The argument is a space separated list of addresses or
indexes. If used non-interactively with a passwordfile, the file should contain
the respective passwords one per line. If you unlock n accounts and the password
file contains less than n entries, then the last password is meant to apply to
all remaining accounts.
And finally. DO NOT FORGET YOUR PASSWORD.
`,
Subcommands: []cli.Command{
{
Action: accountList,
Name: "list",
Usage: "print account addresses",
},
{
Action: accountCreate,
Name: "new",
Usage: "create a new account",
Description: `
ethereum account new
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 <passwordfile> 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.
`,
},
{
Action: accountUpdate,
Name: "update",
Usage: "update an existing account",
Description: `
ethereum account update <address>
Update an existing account.
The account is saved in the newest version in encrypted format, you are prompted
for a passphrase to unlock the account and another to save the updated file.
This same command can therefore be used to migrate an account of a deprecated
format to the newest format or change the password for an account.
For non-interactive use the passphrase can be specified with the --password flag:
ethereum --password <passwordfile> account update <address>
Since only one password can be given, only format update can be performed,
changing your password is only possible interactively.
`,
},
{
Action: accountImport,
Name: "import",
Usage: "import a private key into a new account",
Description: `
ethereum account import <keyfile>
Imports an unencrypted private key from <keyfile> and creates a new account.
Prints the address.
The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
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 <passwordfile> account import <keyfile>
Note:
As you can directly copy your encrypted accounts to another ethereum instance,
this import mechanism is not needed when you transfer an account between
nodes.
`,
},
},
}
)
func accountList(ctx *cli.Context) error {
var sync *[]node.Service
accman := utils.MakeAccountManager(ctx, sync)
for i, acct := range accman.Accounts() {
fmt.Printf("Account #%d: {%x} %s\n", i, acct.Address, acct.File)
}
return nil
}
// tries unlocking the specified account a few times.
func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
account, err := utils.MakeAddress(accman, address)
if err != nil {
utils.Fatalf("Could not list accounts: %v", err)
}
for trials := 0; trials < 3; trials++ {
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
password := getPassPhrase(prompt, false, i, passwords)
err = accman.Unlock(account, password)
if err == nil {
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
return account, password
}
if err, ok := err.(*accounts.AmbiguousAddrError); ok {
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
return ambiguousAddrRecovery(accman, err, password), password
}
if err != accounts.ErrDecrypt {
// No need to prompt again if the error is not decryption-related.
break
}
}
// All trials expended to unlock account, bail out
utils.Fatalf("Failed to unlock account %s (%v)", address, err)
return accounts.Account{}, ""
}
// getPassPhrase retrieves the password associated with an account, either fetched
// from a list of preloaded passphrases, or requested interactively from the user.
func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
// If a list of passwords was supplied, retrieve from them
if len(passwords) > 0 {
if i < len(passwords) {
return passwords[i]
}
return passwords[len(passwords)-1]
}
// Otherwise prompt the user for the password
if prompt != "" {
fmt.Println(prompt)
}
password, err := console.Stdin.PromptPassword("Passphrase: ")
if err != nil {
utils.Fatalf("Failed to read passphrase: %v", err)
}
if confirmation {
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
if err != nil {
utils.Fatalf("Failed to read passphrase confirmation: %v", err)
}
if password != confirm {
utils.Fatalf("Passphrases do not match")
}
}
return password
}
// getWhisperYesNo retrieves an indication of whether or not the user wants the created
// account to also be enabled as a whisper identity
func getWhisperYesNo(prompt string) bool {
// prompt the user for the whisper preference
if prompt != "" {
fmt.Println(prompt)
}
shhRes, err := console.Stdin.PromptConfirm("Enable the new account as a Whisper Identity?")
if err != nil {
utils.Fatalf("Failed to read response: %v", err)
}
return shhRes
}
func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrError, auth string) accounts.Account {
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
for _, a := range err.Matches {
fmt.Println(" ", a.File)
}
fmt.Println("Testing your passphrase against all of them...")
var match *accounts.Account
for _, a := range err.Matches {
if err := am.Unlock(a, auth); err == nil {
match = &a
break
}
}
if match == nil {
utils.Fatalf("None of the listed files could be unlocked.")
}
fmt.Printf("Your passphrase unlocked %s\n", match.File)
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
for _, a := range err.Matches {
if a != *match {
fmt.Println(" ", a.File)
}
}
return *match
}
// accountCreate creates a new account into the keystore defined by the CLI flags.
func accountCreate(ctx *cli.Context) error {
var sync *[]node.Service
accman := utils.MakeAccountManager(ctx, sync)
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
whisper := getWhisperYesNo("You can also choose to enable your new account as a Whisper identity.")
account, err := accman.NewAccount(password, whisper)
if err != nil {
utils.Fatalf("Failed to create account: %v", err)
}
fmt.Printf("Address: {%x}\n", account.Address)
return nil
}
// accountUpdate transitions an account from a previous format to the current
// one, also providing the possibility to change the pass-phrase.
func accountUpdate(ctx *cli.Context) error {
if len(ctx.Args()) == 0 {
utils.Fatalf("No accounts specified to update")
}
var sync *[]node.Service
accman := utils.MakeAccountManager(ctx, sync)
account, oldPassword := unlockAccount(ctx, accman, ctx.Args().First(), 0, nil)
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
if err := accman.Update(account, oldPassword, newPassword); err != nil {
utils.Fatalf("Could not update the account: %v", err)
}
return nil
}
func importWallet(ctx *cli.Context) error {
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)
}
var sync *[]node.Service
accman := utils.MakeAccountManager(ctx, sync)
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
acct, err := accman.ImportPreSaleKey(keyJson, passphrase)
if err != nil {
utils.Fatalf("%v", err)
}
fmt.Printf("Address: {%x}\n", acct.Address)
return nil
}
func accountImport(ctx *cli.Context) error {
keyfile := ctx.Args().First()
if len(keyfile) == 0 {
utils.Fatalf("keyfile must be given as argument")
}
key, err := crypto.LoadECDSA(keyfile)
if err != nil {
utils.Fatalf("Failed to load the private key: %v", err)
}
var sync *[]node.Service
accman := utils.MakeAccountManager(ctx, sync)
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
acct, err := accman.ImportECDSA(key, passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
fmt.Printf("Address: {%x}\n", acct.Address)
return nil
}

View File

@ -0,0 +1,207 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"os"
"path/filepath"
"strconv"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/urfave/cli.v1"
)
var (
importCommand = cli.Command{
Action: importChain,
Name: "import",
Usage: `import a blockchain file`,
}
exportCommand = cli.Command{
Action: exportChain,
Name: "export",
Usage: `export blockchain into file`,
Description: `
Requires a first argument of the file to write to.
Optional second and third arguments control the first and
last block to write. In this mode, the file will be appended
if already existing.
`,
}
upgradedbCommand = cli.Command{
Action: upgradeDB,
Name: "upgradedb",
Usage: "upgrade chainblock database",
}
removedbCommand = cli.Command{
Action: removeDB,
Name: "removedb",
Usage: "Remove blockchain and state databases",
}
dumpCommand = cli.Command{
Action: dump,
Name: "dump",
Usage: `dump a specific block from storage`,
Description: `
The arguments are interpreted as block numbers or hashes.
Use "ethereum dump 0" to dump the genesis block.
`,
}
)
func importChain(ctx *cli.Context) error {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
}
chain, chainDb := utils.MakeChain(ctx)
start := time.Now()
err := utils.ImportChain(chain, ctx.Args().First())
chainDb.Close()
if err != nil {
utils.Fatalf("Import error: %v", err)
}
fmt.Printf("Import done in %v", time.Since(start))
return nil
}
func exportChain(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
chain, _ := utils.MakeChain(ctx)
start := time.Now()
var err error
fp := ctx.Args().First()
if len(ctx.Args()) < 3 {
err = utils.ExportChain(chain, fp)
} else {
// This can be improved to allow for numbers larger than 9223372036854775807
first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64)
last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64)
if ferr != nil || lerr != nil {
utils.Fatalf("Export error in parsing parameters: block number not an integer\n")
}
if first < 0 || last < 0 {
utils.Fatalf("Export error: block number must be greater than 0\n")
}
err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last))
}
if err != nil {
utils.Fatalf("Export error: %v\n", err)
}
fmt.Printf("Export done in %v", time.Since(start))
return nil
}
func removeDB(ctx *cli.Context) error {
confirm, err := console.Stdin.PromptConfirm("Remove local database?")
if err != nil {
utils.Fatalf("%v", err)
}
if confirm {
fmt.Println("Removing chaindata...")
start := time.Now()
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), utils.ChainDbName(ctx)))
fmt.Printf("Removed in %v\n", time.Since(start))
} else {
fmt.Println("Operation aborted")
}
return nil
}
func upgradeDB(ctx *cli.Context) error {
glog.Infoln("Upgrading blockchain database")
chain, chainDb := utils.MakeChain(ctx)
bcVersion := core.GetBlockChainVersion(chainDb)
if bcVersion == 0 {
bcVersion = core.BlockChainVersion
}
// Export the current chain.
filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("20060102_150405"))
exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
if err := utils.ExportChain(chain, exportFile); err != nil {
utils.Fatalf("Unable to export chain for reimport %s", err)
}
chainDb.Close()
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), utils.ChainDbName(ctx)))
// Import the chain file.
chain, chainDb = utils.MakeChain(ctx)
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
err := utils.ImportChain(chain, exportFile)
chainDb.Close()
if err != nil {
utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)", err, exportFile)
} else {
os.Remove(exportFile)
glog.Infoln("Import finished")
}
return nil
}
func dump(ctx *cli.Context) error {
chain, chainDb := utils.MakeChain(ctx)
for _, arg := range ctx.Args() {
var block *types.Block
if hashish(arg) {
block = chain.GetBlockByHash(common.HexToHash(arg))
} else {
num, _ := strconv.Atoi(arg)
block = chain.GetBlockByNumber(uint64(num))
}
if block == nil {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
state, err := state.New(block.Root(), chainDb)
if err != nil {
utils.Fatalf("could not create new state: %v", err)
}
fmt.Printf("%s\n", state.Dump())
}
}
chainDb.Close()
return nil
}
// hashish returns true for strings that look like hashes.
func hashish(x string) bool {
_, err := strconv.Atoi(x)
return err != nil
}
func closeAll(dbs ...ethdb.Database) {
for _, db := range dbs {
db.Close()
}
}

View File

@ -0,0 +1,185 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"os"
"os/signal"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/rpc"
"gopkg.in/urfave/cli.v1"
)
var (
consoleCommand = cli.Command{
Action: localConsole,
Name: "console",
Usage: `Geth Console: interactive JavaScript environment`,
Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`,
}
attachCommand = cli.Command{
Action: remoteConsole,
Name: "attach",
Usage: `Geth Console: interactive JavaScript environment (connect to node)`,
Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.
This command allows to open a console on a running geth node.
`,
}
javascriptCommand = cli.Command{
Action: ephemeralConsole,
Name: "js",
Usage: `executes the given JavaScript files in the Geth JavaScript VM`,
Description: `
The JavaScript VM exposes a node admin interface as well as the Ðapp
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`,
}
)
// localConsole starts a new geth node, attaching a JavaScript console to it at the
// same time.
func localConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
node, _ := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx)
startNode(ctx, node)
defer node.Stop()
// Attach to the newly started node and start the JavaScript console
client := rpc.NewClientRestartWrapper(func() rpc.Client {
client, err := node.Attach()
if err != nil {
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
}
return client
})
config := console.Config{
DataDir: node.DataDir(),
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
console, err := console.New(config)
if err != nil {
utils.Fatalf("Failed to start the JavaScript console: %v", err)
}
defer console.Stop(false)
// If only a short execution was requested, evaluate and return
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
console.Evaluate(script)
return nil
}
// Otherwise print the welcome screen and enter interactive mode
console.Welcome()
console.Interactive()
return nil
}
// remoteConsole will connect to a remote geth instance, attaching a JavaScript
// console to it.
func remoteConsole(ctx *cli.Context) error {
// Attach to a remotely running geth instance and start the JavaScript console
client := rpc.NewClientRestartWrapper(func() rpc.Client {
client, err := utils.NewRemoteRPCClient(ctx)
if err != nil {
utils.Fatalf("Unable to attach to remote geth: %v", err)
}
return client
})
config := console.Config{
DataDir: utils.MustMakeDataDir(ctx),
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
console, err := console.New(config)
if err != nil {
utils.Fatalf("Failed to start the JavaScript console: %v", err)
}
defer console.Stop(false)
// If only a short execution was requested, evaluate and return
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
console.Evaluate(script)
return nil
}
// Otherwise print the welcome screen and enter interactive mode
console.Welcome()
console.Interactive()
return nil
}
// ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript
// console to it, and each of the files specified as arguments and tears the
// everything down.
func ephemeralConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
node, _ := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx)
startNode(ctx, node)
defer node.Stop()
// Attach to the newly started node and start the JavaScript console
client := rpc.NewClientRestartWrapper(func() rpc.Client {
client, err := node.Attach()
if err != nil {
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
}
return client
})
config := console.Config{
DataDir: node.DataDir(),
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
console, err := console.New(config)
if err != nil {
utils.Fatalf("Failed to start the JavaScript console: %v", err)
}
defer console.Stop(false)
// Evaluate each of the specified JavaScript files
for _, file := range ctx.Args() {
if err = console.Execute(file); err != nil {
utils.Fatalf("Failed to execute %s: %v", file, err)
}
}
// Wait for pending callbacks, but stop for Ctrl-C.
abort := make(chan os.Signal, 1)
signal.Notify(abort, os.Interrupt)
go func() {
<-abort
os.Exit(0)
}()
console.Stop(true)
return nil
}

View File

@ -0,0 +1 @@
{"code":"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.23","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}}

View File

@ -0,0 +1,24 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// Simple wrapper to translate the API exposed methods and types to inthernal
// Go versions of the same types.
#include "_cgo_export.h"
int run(const char* args) {
return doRun((char*)args);
}

View File

@ -0,0 +1,46 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// Contains a simple library definition to allow creating a Geth instance from
// straight C code.
package main
// #ifdef __cplusplus
// extern "C" {
// #endif
//
// extern int run(const char*);
//
// #ifdef __cplusplus
// }
// #endif
import "C"
import (
"fmt"
"os"
"strings"
)
//export doRun
func doRun(args *C.char) C.int {
// This is equivalent to geth.main, just modified to handle the function arg passing
if err := app.Run(strings.Split("geth "+C.GoString(args), " ")); err != nil {
fmt.Fprintln(os.Stderr, err)
return -1
}
return 0
}

View File

@ -0,0 +1,56 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// Contains specialized code for running Geth on Android.
package main
// #include <android/log.h>
// #cgo LDFLAGS: -llog
import "C"
import (
"bufio"
"os"
)
func init() {
// Redirect the standard output and error to logcat
oldStdout, oldStderr := os.Stdout, os.Stderr
outRead, outWrite, _ := os.Pipe()
errRead, errWrite, _ := os.Pipe()
os.Stdout = outWrite
os.Stderr = errWrite
go func() {
scanner := bufio.NewScanner(outRead)
for scanner.Scan() {
line := scanner.Text()
C.__android_log_write(C.ANDROID_LOG_INFO, C.CString("Stdout"), C.CString(line))
oldStdout.WriteString(line + "\n")
}
}()
go func() {
scanner := bufio.NewScanner(errRead)
for scanner.Scan() {
line := scanner.Text()
C.__android_log_write(C.ANDROID_LOG_INFO, C.CString("Stderr"), C.CString(line))
oldStderr.WriteString(line + "\n")
}
}()
}

View File

@ -0,0 +1,425 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// geth is the official command-line client for Ethereum.
package main
import (
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"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/console"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/release"
"github.com/ethereum/go-ethereum/rlp"
"gopkg.in/urfave/cli.v1"
)
const (
clientIdentifier = "Geth" // Client identifier to advertise over the network
versionMajor = 1 // Major version component of the current release
versionMinor = 5 // Minor version component of the current release
versionPatch = 0 // Patch version component of the current release
versionMeta = "unstable" // Version metadata to append to the version string
versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle
)
var (
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
verString string // Combined textual representation of all the version components
relConfig release.Config // Structured version information and release oracle config
app *cli.App
)
func init() {
// Construct the textual version string from the individual components
verString = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch)
if versionMeta != "" {
verString += "-" + versionMeta
}
if gitCommit != "" {
verString += "-" + gitCommit[:8]
}
// Construct the version release oracle configuration
relConfig.Oracle = common.HexToAddress(versionOracle)
relConfig.Major = uint32(versionMajor)
relConfig.Minor = uint32(versionMinor)
relConfig.Patch = uint32(versionPatch)
commit, _ := hex.DecodeString(gitCommit)
copy(relConfig.Commit[:], commit)
// Initialize the CLI app and start Geth
app = utils.NewApp(verString, "the go-ethereum command line interface")
app.Action = geth
app.HideVersion = true // we have a command to print the version
app.Commands = []cli.Command{
importCommand,
exportCommand,
upgradedbCommand,
removedbCommand,
dumpCommand,
monitorCommand,
accountCommand,
walletCommand,
consoleCommand,
attachCommand,
javascriptCommand,
{
Action: makedag,
Name: "makedag",
Usage: "generate ethash dag (for testing)",
Description: `
The makedag command generates an ethash DAG in /tmp/dag.
This command exists to support the system testing project.
Regular users do not need to execute it.
`,
},
{
Action: gpuinfo,
Name: "gpuinfo",
Usage: "gpuinfo",
Description: `
Prints OpenCL device info for all found GPUs.
`,
},
{
Action: gpubench,
Name: "gpubench",
Usage: "benchmark GPU",
Description: `
Runs quick benchmark on first GPU found.
`,
},
{
Action: version,
Name: "version",
Usage: "print ethereum version numbers",
Description: `
The output of this command is supposed to be machine-readable.
`,
},
{
Action: initGenesis,
Name: "init",
Usage: "bootstraps and initialises a new genesis block (JSON)",
Description: `
The init command initialises a new genesis block and definition for the network.
This is a destructive action and changes the network in which you will be
participating.
`,
},
}
app.Flags = []cli.Flag{
utils.IdentityFlag,
utils.UnlockedAccountFlag,
utils.PasswordFileFlag,
utils.BootnodesFlag,
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.BlockchainVersionFlag,
utils.OlympicFlag,
utils.FastSyncFlag,
utils.LightModeFlag,
utils.NoDefSrvFlag,
utils.LightServFlag,
utils.LightPeersFlag,
utils.CacheFlag,
utils.LightKDFFlag,
utils.JSpathFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
utils.MaxPendingPeersFlag,
utils.EtherbaseFlag,
utils.GasPriceFlag,
utils.SupportDAOFork,
utils.OpposeDAOFork,
utils.MinerThreadsFlag,
utils.MiningEnabledFlag,
utils.MiningGPUFlag,
utils.AutoDAGFlag,
utils.TargetGasLimitFlag,
utils.NATFlag,
utils.NatspecEnabledFlag,
utils.NoDiscoverFlag,
utils.NoEthFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
utils.RPCApiFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,
utils.WSApiFlag,
utils.WSAllowedOriginsFlag,
utils.IPCDisabledFlag,
utils.IPCApiFlag,
utils.IPCPathFlag,
utils.ExecFlag,
utils.PreloadJSFlag,
utils.WhisperEnabledFlag,
utils.DevModeFlag,
utils.TestNetFlag,
utils.VMForceJitFlag,
utils.VMJitCacheFlag,
utils.VMEnableJitFlag,
utils.NetworkIdFlag,
utils.RPCCORSDomainFlag,
utils.MetricsEnabledFlag,
utils.FakePoWFlag,
utils.SolcPathFlag,
utils.GpoMinGasPriceFlag,
utils.GpoMaxGasPriceFlag,
utils.GpoFullBlockRatioFlag,
utils.GpobaseStepDownFlag,
utils.GpobaseStepUpFlag,
utils.GpobaseCorrectionFactorFlag,
utils.ExtraDataFlag,
}
app.Flags = append(app.Flags, debug.Flags...)
app.Before = func(ctx *cli.Context) error {
runtime.GOMAXPROCS(runtime.NumCPU())
if err := debug.Setup(ctx); err != nil {
return err
}
// Start system runtime metrics collection
go metrics.CollectProcessMetrics(3 * time.Second)
// This should be the only place where reporting is enabled
// because it is not intended to run while testing.
// In addition to this check, bad block reports are sent only
// for chains with the main network genesis block and network id 1.
eth.EnableBadBlockReporting = true
utils.SetupNetwork(ctx)
return nil
}
app.After = func(ctx *cli.Context) error {
logger.Flush()
debug.Exit()
console.Stdin.Close() // Resets terminal mode.
return nil
}
}
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func makeDefaultExtra() []byte {
var clientInfo = struct {
Version uint
Name string
GoVersion string
Os string
}{uint(versionMajor<<16 | versionMinor<<8 | versionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
extra, err := rlp.EncodeToBytes(clientInfo)
if err != nil {
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
}
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
glog.V(logger.Debug).Infof("extra: %x\n", extra)
return nil
}
return extra
}
// geth is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down.
func geth(ctx *cli.Context) error {
node, _ := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx)
startNode(ctx, node)
node.Wait()
return nil
}
// initGenesis will initialise the given JSON format genesis file and writes it as
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
func initGenesis(ctx *cli.Context) error {
genesisPath := ctx.Args().First()
if len(genesisPath) == 0 {
utils.Fatalf("must supply path to genesis JSON file")
}
chainDb, err := ethdb.NewLDBDatabase(filepath.Join(utils.MustMakeDataDir(ctx), utils.ChainDbName(ctx)), 0, 0)
if err != nil {
utils.Fatalf("could not open database: %v", err)
}
genesisFile, err := os.Open(genesisPath)
if err != nil {
utils.Fatalf("failed to read genesis file: %v", err)
}
block, err := core.WriteGenesisBlock(chainDb, genesisFile)
if err != nil {
utils.Fatalf("failed to write genesis block: %v", err)
}
glog.V(logger.Info).Infof("successfully wrote genesis block and/or chain rule set: %x", block.Hash())
return nil
}
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
func startNode(ctx *cli.Context, stack *node.Node) {
// Report geth version
glog.V(logger.Info).Infof("instance: Geth/%s/%s/%s\n", verString, runtime.Version(), runtime.GOOS)
// Start up the node itself
utils.StartNode(stack)
if !ctx.GlobalBool(utils.NoEthFlag.Name) {
if ctx.GlobalBool(utils.LightModeFlag.Name) && !ctx.GlobalBool(utils.NoDefSrvFlag.Name) {
// add default light server; test phase only
url := "enode://201aa667e0b75462c8837708dbc3c91b43f84d233efda2f4e2c5ae0ea237d646db656375b394fb35d841cf8ea2814e3629af4821d3b0204508f7eb8cea8e7f31@40.118.3.223:30303"
if ctx.GlobalBool(utils.TestNetFlag.Name) {
url = "enode://2737bebb1e70cf682553c974d9551b74a917cb4f61292150abc10d2c122c8d369c82cb2b71ff107120ea2547419d2d9e998c637d45a6ff57bb01e83cfc1d5115@40.118.3.223:30304"
//url = "enode://2737bebb1e70cf682553c974d9551b74a917cb4f61292150abc10d2c122c8d369c82cb2b71ff107120ea2547419d2d9e998c637d45a6ff57bb01e83cfc1d5115@[::]:30303"
}
node, err := discover.ParseNode(url)
if err == nil {
stack.Server().AddPeer(node)
}
}
// Unlock any account specifically requested
var accman *accounts.Manager
if err := stack.Service(&accman); err != nil {
utils.Fatalf("ethereum service not running: %v", err)
}
passwords := utils.MakePasswordList(ctx)
accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
for i, account := range accounts {
if trimmed := strings.TrimSpace(account); trimmed != "" {
unlockAccount(ctx, accman, trimmed, i, passwords)
}
}
// Start auxiliary services if enabled
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
utils.Fatalf("ethereum service not running: %v", err)
}
if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil {
utils.Fatalf("Failed to start mining: %v", err)
}
}
}
}
func makedag(ctx *cli.Context) error {
args := ctx.Args()
wrongArgs := func() {
utils.Fatalf(`Usage: geth makedag <block number> <outputdir>`)
}
switch {
case len(args) == 2:
blockNum, err := strconv.ParseUint(args[0], 0, 64)
dir := args[1]
if err != nil {
wrongArgs()
} else {
dir = filepath.Clean(dir)
// seems to require a trailing slash
if !strings.HasSuffix(dir, "/") {
dir = dir + "/"
}
_, err = ioutil.ReadDir(dir)
if err != nil {
utils.Fatalf("Can't find dir")
}
fmt.Println("making DAG, this could take awhile...")
ethash.MakeDAG(blockNum, dir)
}
default:
wrongArgs()
}
return nil
}
func gpuinfo(ctx *cli.Context) error {
eth.PrintOpenCLDevices()
return nil
}
func gpubench(ctx *cli.Context) error {
args := ctx.Args()
wrongArgs := func() {
utils.Fatalf(`Usage: geth gpubench <gpu number>`)
}
switch {
case len(args) == 1:
n, err := strconv.ParseUint(args[0], 0, 64)
if err != nil {
wrongArgs()
}
eth.GPUBench(n)
case len(args) == 0:
eth.GPUBench(0)
default:
wrongArgs()
}
return nil
}
func version(c *cli.Context) error {
fmt.Println(clientIdentifier)
fmt.Println("Version:", verString)
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
fmt.Println("Network Id:", c.GlobalInt(utils.NetworkIdFlag.Name))
fmt.Println("Go Version:", runtime.Version())
fmt.Println("OS:", runtime.GOOS)
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
return nil
}

View File

@ -0,0 +1,369 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"math"
"reflect"
"runtime"
"strings"
"time"
"sort"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gizak/termui"
"gopkg.in/urfave/cli.v1"
)
var (
monitorCommandAttachFlag = cli.StringFlag{
Name: "attach",
Value: "ipc:" + node.DefaultIPCEndpoint(),
Usage: "API endpoint to attach to",
}
monitorCommandRowsFlag = cli.IntFlag{
Name: "rows",
Value: 5,
Usage: "Maximum rows in the chart grid",
}
monitorCommandRefreshFlag = cli.IntFlag{
Name: "refresh",
Value: 3,
Usage: "Refresh interval in seconds",
}
monitorCommand = cli.Command{
Action: monitor,
Name: "monitor",
Usage: `Geth Monitor: node metrics monitoring and visualization`,
Description: `
The Geth monitor is a tool to collect and visualize various internal metrics
gathered by the node, supporting different chart types as well as the capacity
to display multiple metrics simultaneously.
`,
Flags: []cli.Flag{
monitorCommandAttachFlag,
monitorCommandRowsFlag,
monitorCommandRefreshFlag,
},
}
)
// monitor starts a terminal UI based monitoring tool for the requested metrics.
func monitor(ctx *cli.Context) error {
var (
client rpc.Client
err error
)
// Attach to an Ethereum node over IPC or RPC
endpoint := ctx.String(monitorCommandAttachFlag.Name)
if client, err = utils.NewRemoteRPCClientFromString(endpoint); err != nil {
utils.Fatalf("Unable to attach to geth node: %v", err)
}
defer client.Close()
// Retrieve all the available metrics and resolve the user pattens
metrics, err := retrieveMetrics(client)
if err != nil {
utils.Fatalf("Failed to retrieve system metrics: %v", err)
}
monitored := resolveMetrics(metrics, ctx.Args())
if len(monitored) == 0 {
list := expandMetrics(metrics, "")
sort.Strings(list)
if len(list) > 0 {
utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - "))
} else {
utils.Fatalf("No metrics collected by geth (--%s).\n", utils.MetricsEnabledFlag.Name)
}
}
sort.Strings(monitored)
if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 {
utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - "))
}
// Create and configure the chart UI defaults
if err := termui.Init(); err != nil {
utils.Fatalf("Unable to initialize terminal UI: %v", err)
}
defer termui.Close()
rows := len(monitored)
if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max {
rows = max
}
cols := (len(monitored) + rows - 1) / rows
for i := 0; i < rows; i++ {
termui.Body.AddRows(termui.NewRow())
}
// Create each individual data chart
footer := termui.NewPar("")
footer.Block.Border = true
footer.Height = 3
charts := make([]*termui.LineChart, len(monitored))
units := make([]int, len(monitored))
data := make([][]float64, len(monitored))
for i := 0; i < len(monitored); i++ {
charts[i] = createChart((termui.TermHeight() - footer.Height) / rows)
row := termui.Body.Rows[i%rows]
row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i]))
}
termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer)))
refreshCharts(client, monitored, data, units, charts, ctx, footer)
termui.Body.Align()
termui.Render(termui.Body)
// Watch for various system events, and periodically refresh the charts
termui.Handle("/sys/kbd/C-c", func(termui.Event) {
termui.StopLoop()
})
termui.Handle("/sys/wnd/resize", func(termui.Event) {
termui.Body.Width = termui.TermWidth()
for _, chart := range charts {
chart.Height = (termui.TermHeight() - footer.Height) / rows
}
termui.Body.Align()
termui.Render(termui.Body)
})
go func() {
tick := time.NewTicker(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second)
for range tick.C {
if refreshCharts(client, monitored, data, units, charts, ctx, footer) {
termui.Body.Align()
}
termui.Render(termui.Body)
}
}()
termui.Loop()
return nil
}
// retrieveMetrics contacts the attached geth node and retrieves the entire set
// of collected system metrics.
func retrieveMetrics(client rpc.Client) (map[string]interface{}, error) {
req := map[string]interface{}{
"id": new(int64),
"method": "debug_metrics",
"jsonrpc": "2.0",
"params": []interface{}{true},
}
if err := client.Send(req); err != nil {
return nil, err
}
var res rpc.JSONSuccessResponse
if err := client.Recv(&res); err != nil {
return nil, err
}
if res.Result != nil {
if mets, ok := res.Result.(map[string]interface{}); ok {
return mets, nil
}
}
return nil, fmt.Errorf("unable to retrieve metrics")
}
// resolveMetrics takes a list of input metric patterns, and resolves each to one
// or more canonical metric names.
func resolveMetrics(metrics map[string]interface{}, patterns []string) []string {
res := []string{}
for _, pattern := range patterns {
res = append(res, resolveMetric(metrics, pattern, "")...)
}
return res
}
// resolveMetrics takes a single of input metric pattern, and resolves it to one
// or more canonical metric names.
func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string {
results := []string{}
// If a nested metric was requested, recurse optionally branching (via comma)
parts := strings.SplitN(pattern, "/", 2)
if len(parts) > 1 {
for _, variation := range strings.Split(parts[0], ",") {
if submetrics, ok := metrics[variation].(map[string]interface{}); !ok {
utils.Fatalf("Failed to retrieve system metrics: %s", path+variation)
return nil
} else {
results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...)
}
}
return results
}
// Depending what the last link is, return or expand
for _, variation := range strings.Split(pattern, ",") {
switch metric := metrics[variation].(type) {
case float64:
// Final metric value found, return as singleton
results = append(results, path+variation)
case map[string]interface{}:
results = append(results, expandMetrics(metric, path+variation+"/")...)
default:
utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric))
return nil
}
}
return results
}
// expandMetrics expands the entire tree of metrics into a flat list of paths.
func expandMetrics(metrics map[string]interface{}, path string) []string {
// Iterate over all fields and expand individually
list := []string{}
for name, metric := range metrics {
switch metric := metric.(type) {
case float64:
// Final metric value found, append to list
list = append(list, path+name)
case map[string]interface{}:
// Tree of metrics found, expand recursively
list = append(list, expandMetrics(metric, path+name+"/")...)
default:
utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric))
return nil
}
}
return list
}
// fetchMetric iterates over the metrics map and retrieves a specific one.
func fetchMetric(metrics map[string]interface{}, metric string) float64 {
parts, found := strings.Split(metric, "/"), true
for _, part := range parts[:len(parts)-1] {
metrics, found = metrics[part].(map[string]interface{})
if !found {
return 0
}
}
if v, ok := metrics[parts[len(parts)-1]].(float64); ok {
return v
}
return 0
}
// refreshCharts retrieves a next batch of metrics, and inserts all the new
// values into the active datasets and charts
func refreshCharts(client rpc.Client, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) {
values, err := retrieveMetrics(client)
for i, metric := range metrics {
if len(data) < 512 {
data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...)
} else {
data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...)
}
if updateChart(metric, data[i], &units[i], charts[i], err) {
realign = true
}
}
updateFooter(ctx, err, footer)
return
}
// updateChart inserts a dataset into a line chart, scaling appropriately as to
// not display weird labels, also updating the chart label accordingly.
func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) {
dataUnits := []string{"", "K", "M", "G", "T", "E"}
timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"}
colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed}
// Extract only part of the data that's actually visible
if chart.Width*2 < len(data) {
data = data[:chart.Width*2]
}
// Find the maximum value and scale under 1K
high := 0.0
if len(data) > 0 {
high = data[0]
for _, value := range data[1:] {
high = math.Max(high, value)
}
}
unit, scale := 0, 1.0
for high >= 1000 && unit+1 < len(dataUnits) {
high, unit, scale = high/1000, unit+1, scale*1000
}
// If the unit changes, re-create the chart (hack to set max height...)
if unit != *base {
realign, *base, *chart = true, unit, *createChart(chart.Height)
}
// Update the chart's data points with the scaled values
if cap(chart.Data) < len(data) {
chart.Data = make([]float64, len(data))
}
chart.Data = chart.Data[:len(data)]
for i, value := range data {
chart.Data[i] = value / scale
}
// Update the chart's label with the scale units
units := dataUnits
if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") || strings.Contains(metric, "/time/") {
units = timeUnits
}
chart.BorderLabel = metric
if len(units[unit]) > 0 {
chart.BorderLabel += " [" + units[unit] + "]"
}
chart.LineColor = colors[unit] | termui.AttrBold
if err != nil {
chart.LineColor = termui.ColorRed | termui.AttrBold
}
return
}
// createChart creates an empty line chart with the default configs.
func createChart(height int) *termui.LineChart {
chart := termui.NewLineChart()
if runtime.GOOS == "windows" {
chart.Mode = "dot"
}
chart.DataLabels = []string{""}
chart.Height = height
chart.AxesColor = termui.ColorWhite
chart.PaddingBottom = -2
chart.BorderLabelFg = chart.BorderFg | termui.AttrBold
chart.BorderFg = chart.BorderBg
return chart
}
// updateFooter updates the footer contents based on any encountered errors.
func updateFooter(ctx *cli.Context, err error, footer *termui.Par) {
// Generate the basic footer
refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second
footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh)
footer.TextFgColor = termui.ThemeAttr("par.fg") | termui.AttrBold
// Append any encountered errors
if err != nil {
footer.Text = fmt.Sprintf("Error: %v.", err)
footer.TextFgColor = termui.ColorRed | termui.AttrBold
}
}

View File

@ -0,0 +1,220 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// Contains the geth command usage template and generator.
package main
import (
"io"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug"
"gopkg.in/urfave/cli.v1"
)
// AppHelpTemplate is the test template for the default, global app help topic.
var AppHelpTemplate = `NAME:
{{.App.Name}} - {{.App.Usage}}
USAGE:
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if .App.Version}}
VERSION:
{{.App.Version}}
{{end}}{{if len .App.Authors}}
AUTHOR(S):
{{range .App.Authors}}{{ . }}{{end}}
{{end}}{{if .App.Commands}}
COMMANDS:
{{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .FlagGroups}}
{{range .FlagGroups}}{{.Name}} OPTIONS:
{{range .Flags}}{{.}}
{{end}}
{{end}}{{end}}{{if .App.Copyright }}
COPYRIGHT:
{{.App.Copyright}}
{{end}}
`
// flagGroup is a collection of flags belonging to a single topic.
type flagGroup struct {
Name string
Flags []cli.Flag
}
// AppHelpFlagGroups is the application flags, grouped by functionality.
var AppHelpFlagGroups = []flagGroup{
{
Name: "ETHEREUM",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.NetworkIdFlag,
utils.OlympicFlag,
utils.TestNetFlag,
utils.DevModeFlag,
utils.IdentityFlag,
utils.FastSyncFlag,
utils.LightModeFlag,
utils.NoDefSrvFlag,
utils.LightServFlag,
utils.LightPeersFlag,
utils.LightKDFFlag,
utils.CacheFlag,
utils.BlockchainVersionFlag,
},
},
{
Name: "ACCOUNT",
Flags: []cli.Flag{
utils.UnlockedAccountFlag,
utils.PasswordFileFlag,
},
},
{
Name: "API AND CONSOLE",
Flags: []cli.Flag{
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
utils.RPCApiFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,
utils.WSApiFlag,
utils.WSAllowedOriginsFlag,
utils.IPCDisabledFlag,
utils.IPCApiFlag,
utils.IPCPathFlag,
utils.RPCCORSDomainFlag,
utils.JSpathFlag,
utils.ExecFlag,
utils.PreloadJSFlag,
},
},
{
Name: "NETWORKING",
Flags: []cli.Flag{
utils.BootnodesFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
utils.MaxPendingPeersFlag,
utils.NATFlag,
utils.NoDiscoverFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
},
},
{
Name: "MINER",
Flags: []cli.Flag{
utils.MiningEnabledFlag,
utils.MinerThreadsFlag,
utils.MiningGPUFlag,
utils.AutoDAGFlag,
utils.EtherbaseFlag,
utils.TargetGasLimitFlag,
utils.GasPriceFlag,
utils.ExtraDataFlag,
},
},
{
Name: "GAS PRICE ORACLE",
Flags: []cli.Flag{
utils.GpoMinGasPriceFlag,
utils.GpoMaxGasPriceFlag,
utils.GpoFullBlockRatioFlag,
utils.GpobaseStepDownFlag,
utils.GpobaseStepUpFlag,
utils.GpobaseCorrectionFactorFlag,
},
},
{
Name: "VIRTUAL MACHINE",
Flags: []cli.Flag{
utils.VMEnableJitFlag,
utils.VMForceJitFlag,
utils.VMJitCacheFlag,
},
},
{
Name: "LOGGING AND DEBUGGING",
Flags: append([]cli.Flag{
utils.MetricsEnabledFlag,
utils.FakePoWFlag,
}, debug.Flags...),
},
{
Name: "EXPERIMENTAL",
Flags: []cli.Flag{
utils.WhisperEnabledFlag,
utils.NatspecEnabledFlag,
utils.NoEthFlag,
},
},
{
Name: "MISCELLANEOUS",
Flags: []cli.Flag{
utils.SolcPathFlag,
},
},
}
func init() {
// Override the default app help template
cli.AppHelpTemplate = AppHelpTemplate
// Define a one shot struct to pass to the usage template
type helpData struct {
App interface{}
FlagGroups []flagGroup
}
// Override the default app help printer, but only for the global app help
originalHelpPrinter := cli.HelpPrinter
cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) {
if tmpl == AppHelpTemplate {
// Iterate over all the flags and add any uncategorized ones
categorized := make(map[string]struct{})
for _, group := range AppHelpFlagGroups {
for _, flag := range group.Flags {
categorized[flag.String()] = struct{}{}
}
}
uncategorized := []cli.Flag{}
for _, flag := range data.(*cli.App).Flags {
if _, ok := categorized[flag.String()]; !ok {
uncategorized = append(uncategorized, flag)
}
}
if len(uncategorized) > 0 {
// Append all ungategorized options to the misc group
miscs := len(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags)
AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = append(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags, uncategorized...)
// Make sure they are removed afterwards
defer func() {
AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags[:miscs]
}()
}
// Render out custom usage screen
originalHelpPrinter(w, tmpl, helpData{data, AppHelpFlagGroups})
} else {
originalHelpPrinter(w, tmpl, data)
}
}
}

View File

@ -0,0 +1,170 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// gethrpctest is a command to run the external RPC tests.
package main
import (
"flag"
"io/ioutil"
"log"
"os"
"os/signal"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tests"
"github.com/ethereum/go-ethereum/whisper"
)
const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
var (
testFile = flag.String("json", "", "Path to the .json test file to load")
testName = flag.String("test", "", "Name of the test from the .json file to run")
testKey = flag.String("key", defaultTestKey, "Private key of a test account to inject")
)
func main() {
flag.Parse()
// Enable logging errors, we really do want to see those
glog.SetV(2)
glog.SetToStderr(true)
// Load the test suite to run the RPC against
tests, err := tests.LoadBlockTests(*testFile)
if err != nil {
log.Fatalf("Failed to load test suite: %v", err)
}
test, found := tests[*testName]
if !found {
log.Fatalf("Requested test (%s) not found within suite", *testName)
}
// Create the protocol stack to run the test with
keydir, err := ioutil.TempDir("", "")
if err != nil {
log.Fatalf("Failed to create temporary keystore directory: %v", err)
}
defer os.RemoveAll(keydir)
stack, err := MakeSystemNode(keydir, *testKey, test)
if err != nil {
log.Fatalf("Failed to assemble test stack: %v", err)
}
if err := stack.Start(); err != nil {
log.Fatalf("Failed to start test node: %v", err)
}
defer stack.Stop()
log.Println("Test node started...")
// Make sure the tests contained within the suite pass
if err := RunTest(stack, test); err != nil {
log.Fatalf("Failed to run the pre-configured test: %v", err)
}
log.Println("Initial test suite passed...")
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
}
// MakeSystemNode configures a protocol stack for the RPC tests based on a given
// keystore path and initial pre-state.
func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node.Node, error) {
// Create a networkless protocol stack
stack, err := node.New(&node.Config{
IPCPath: node.DefaultIPCEndpoint(),
HTTPHost: common.DefaultHTTPHost,
HTTPPort: common.DefaultHTTPPort,
HTTPModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"},
WSHost: common.DefaultWSHost,
WSPort: common.DefaultWSPort,
WSModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"},
NoDiscovery: true,
})
if err != nil {
return nil, err
}
// Create the keystore and inject an unlocked account if requested
accman := accounts.NewPlaintextManager(keydir)
if len(privkey) > 0 {
key, err := crypto.HexToECDSA(privkey)
if err != nil {
return nil, err
}
a, err := accman.ImportECDSA(key, "")
if err != nil {
return nil, err
}
if err := accman.Unlock(a, ""); err != nil {
return nil, err
}
}
// Initialize and register the Ethereum protocol
db, _ := ethdb.NewMemDatabase()
if _, err := test.InsertPreState(db); err != nil {
return nil, err
}
ethConf := &eth.Config{
TestGenesisState: db,
TestGenesisBlock: test.Genesis,
ChainConfig: &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock},
AccountManager: accman,
}
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
return nil, err
}
// Initialize and register the Whisper protocol
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
return nil, err
}
return stack, nil
}
// RunTest executes the specified test against an already pre-configured protocol
// stack to ensure basic checks pass before running RPC tests.
func RunTest(stack *node.Node, test *tests.BlockTest) error {
var ethereum *eth.Ethereum
stack.Service(&ethereum)
blockchain := ethereum.BlockChain()
// Process the blocks and verify the imported headers
blocks, err := test.TryBlocksInsert(blockchain)
if err != nil {
return err
}
if err := test.ValidateImportedHeaders(blockchain, blocks); err != nil {
return err
}
// Retrieve the assembled state and validate it
stateDb, err := blockchain.State()
if err != nil {
return err
}
if err := test.ValidatePostState(stateDb); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,143 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// rlpdump is a pretty-printer for RLP data.
package main
import (
"bytes"
"encoding/hex"
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/ethereum/go-ethereum/rlp"
)
var (
hexMode = flag.String("hex", "", "dump given hex data")
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
)
func init() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>] [filename]")
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, `
Dumps RLP data from the given file in readable form.
If the filename is omitted, data is read from stdin.`)
}
}
func main() {
flag.Parse()
var r io.Reader
switch {
case *hexMode != "":
data, err := hex.DecodeString(*hexMode)
if err != nil {
die(err)
}
r = bytes.NewReader(data)
case flag.NArg() == 0:
r = os.Stdin
case flag.NArg() == 1:
fd, err := os.Open(flag.Arg(0))
if err != nil {
die(err)
}
defer fd.Close()
r = fd
default:
fmt.Fprintln(os.Stderr, "Error: too many arguments")
flag.Usage()
os.Exit(2)
}
s := rlp.NewStream(r, 0)
for {
if err := dump(s, 0); err != nil {
if err != io.EOF {
die(err)
}
break
}
fmt.Println()
}
}
func dump(s *rlp.Stream, depth int) error {
kind, size, err := s.Kind()
if err != nil {
return err
}
switch kind {
case rlp.Byte, rlp.String:
str, err := s.Bytes()
if err != nil {
return err
}
if len(str) == 0 || !*noASCII && isASCII(str) {
fmt.Printf("%s%q", ws(depth), str)
} else {
fmt.Printf("%s%x", ws(depth), str)
}
case rlp.List:
s.List()
defer s.ListEnd()
if size == 0 {
fmt.Print(ws(depth) + "[]")
} else {
fmt.Println(ws(depth) + "[")
for i := 0; ; i++ {
if i > 0 {
fmt.Print(",\n")
}
if err := dump(s, depth+1); err == rlp.EOL {
break
} else if err != nil {
return err
}
}
fmt.Print(ws(depth) + "]")
}
}
return nil
}
func isASCII(b []byte) bool {
for _, c := range b {
if c < 32 || c > 126 {
return false
}
}
return true
}
func ws(n int) string {
return strings.Repeat(" ", n)
}
func die(args ...interface{}) {
fmt.Fprintln(os.Stderr, args...)
os.Exit(1)
}

View File

@ -120,7 +120,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
}
}
glog.Infoln("Importing blockchain", fn)
glog.Infoln("Importing blockchain ", fn)
fh, err := os.Open(fn)
if err != nil {
return err
@ -182,7 +182,7 @@ func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
}
func ExportChain(blockchain *core.BlockChain, fn string) error {
glog.Infoln("Exporting blockchain to", fn)
glog.Infoln("Exporting blockchain to ", fn)
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
@ -191,12 +191,12 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
if err := blockchain.Export(fh); err != nil {
return err
}
glog.Infoln("Exported blockchain to", fn)
glog.Infoln("Exported blockchain to ", fn)
return nil
}
func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
glog.Infoln("Exporting blockchain to", fn)
glog.Infoln("Exporting blockchain to ", fn)
// TODO verify mode perms
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
@ -206,6 +206,6 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
if err := blockchain.ExportN(fh, first, last); err != nil {
return err
}
glog.Infoln("Exported blockchain to", fn)
glog.Infoln("Exported blockchain to ", fn)
return nil
}

View File

@ -129,10 +129,6 @@ var (
Name: "dev",
Usage: "Developer mode: pre-configured private network with several debugging flags",
}
GenesisFileFlag = cli.StringFlag{
Name: "genesis",
Usage: "Insert/overwrite the genesis block (JSON format)",
}
IdentityFlag = cli.StringFlag{
Name: "identity",
Usage: "Custom node name",
@ -164,6 +160,10 @@ var (
Name: "light",
Usage: "Enable light client mode",
}
NoDefSrvFlag = cli.BoolFlag{
Name: "nodefsrv",
Usage: "Don't add default LES server (only for test version)",
}
LightServFlag = cli.IntFlag{
Name: "lightserv",
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
@ -178,6 +178,15 @@ var (
Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
}
// Fork settings
SupportDAOFork = cli.BoolFlag{
Name: "support-dao-fork",
Usage: "Updates the chain rules to support the DAO hard-fork",
}
OpposeDAOFork = cli.BoolFlag{
Name: "oppose-dao-fork",
Usage: "Updates the chain rules to oppose the DAO hard-fork",
}
// Miner settings
// TODO: refactor CPU vs GPU mining flags
MiningEnabledFlag = cli.BoolFlag{
@ -562,20 +571,6 @@ func MakeWSRpcHost(ctx *cli.Context) string {
return ctx.GlobalString(WSListenAddrFlag.Name)
}
// MakeGenesisBlock loads up a genesis block from an input file specified in the
// command line, or returns the empty string if none set.
func MakeGenesisBlock(ctx *cli.Context) string {
genesis := ctx.GlobalString(GenesisFileFlag.Name)
if genesis == "" {
return ""
}
data, err := ioutil.ReadFile(genesis)
if err != nil {
Fatalf("Failed to load custom genesis file: %v", err)
}
return string(data)
}
// MakeDatabaseHandles raises out the number of allowed file handles per process
// for Geth and returns half of the allowance to assign to the database.
func MakeDatabaseHandles() int {
@ -718,9 +713,9 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
ethConf := &eth.Config{
ChainConfig: MustMakeChainConfig(ctx),
Genesis: MakeGenesisBlock(ctx),
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
LightMode: ctx.GlobalBool(LightModeFlag.Name),
NoDefSrv: ctx.GlobalBool(NoDefSrvFlag.Name),
LightServ: ctx.GlobalInt(LightServFlag.Name),
LightPeers: ctx.GlobalInt(LightPeersFlag.Name),
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
@ -745,6 +740,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
}
// Configure the Whisper service
shhEnable := ctx.GlobalBool(WhisperEnabledFlag.Name)
@ -757,17 +753,13 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
ethConf.NetworkId = 1
}
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
ethConf.Genesis = core.OlympicGenesisBlock()
}
ethConf.Genesis = core.OlympicGenesisBlock()
case ctx.GlobalBool(TestNetFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
ethConf.NetworkId = 2
}
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
ethConf.Genesis = core.TestNetGenesisBlock()
}
ethConf.Genesis = core.TestNetGenesisBlock()
state.StartingNonce = 1048576 // (2**20)
light.StartingNonce = 1048576 // (2**20)
@ -783,9 +775,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
stackConf.ListenAddr = ":0"
}
// Override the Ethereum protocol configs
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
ethConf.Genesis = core.OlympicGenesisBlock()
}
ethConf.Genesis = core.OlympicGenesisBlock()
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
ethConf.GasPrice = new(big.Int)
}
@ -872,24 +862,62 @@ func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
// MustMakeChainConfigFromDb reads the chain configuration from the given database.
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0)
// If the chain is already initialized, use any existing chain configs
config := new(core.ChainConfig)
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0)
if genesis != nil {
// Existing genesis block, use stored config if available.
storedConfig, err := core.GetChainConfig(db, genesis.Hash())
if err == nil {
return storedConfig
} else if err != core.ChainConfigNotFoundErr {
switch err {
case nil:
config = storedConfig
case core.ChainConfigNotFoundErr:
// No configs found, use empty, will populate below
default:
Fatalf("Could not make chain configuration: %v", err)
}
}
var homesteadBlockNo *big.Int
if ctx.GlobalBool(TestNetFlag.Name) {
homesteadBlockNo = params.TestNetHomesteadBlock
} else {
homesteadBlockNo = params.MainNetHomesteadBlock
// Set any missing fields due to them being unset or system upgrade
if config.HomesteadBlock == nil {
if ctx.GlobalBool(TestNetFlag.Name) {
config.HomesteadBlock = params.TestNetHomesteadBlock
} else {
config.HomesteadBlock = params.MainNetHomesteadBlock
}
}
return &core.ChainConfig{HomesteadBlock: homesteadBlockNo}
if config.DAOForkBlock == nil {
if ctx.GlobalBool(TestNetFlag.Name) {
config.DAOForkBlock = params.TestNetDAOForkBlock
} else {
config.DAOForkBlock = params.MainNetDAOForkBlock
}
config.DAOForkSupport = true
}
// Force override any existing configs if explicitly requested
switch {
case ctx.GlobalBool(SupportDAOFork.Name):
config.DAOForkSupport = true
case ctx.GlobalBool(OpposeDAOFork.Name):
config.DAOForkSupport = false
}
// Temporarilly display a proper message so the user knows which fork its on
if !ctx.GlobalBool(TestNetFlag.Name) && (genesis == nil || genesis.Hash() == common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")) {
choice := "SUPPORT"
if !config.DAOForkSupport {
choice = "OPPOSE"
}
current := fmt.Sprintf("Geth is currently configured to %s the DAO hard-fork!", choice)
howtoswap := fmt.Sprintf("You can change your choice prior to block #%v with --support-dao-fork or --oppose-dao-fork.", config.DAOForkBlock)
howtosync := fmt.Sprintf("After the hard-fork block #%v passed, changing chains requires a resync from scratch!", config.DAOForkBlock)
separator := strings.Repeat("-", len(howtoswap))
glog.V(logger.Warn).Info(separator)
glog.V(logger.Warn).Info(current)
glog.V(logger.Warn).Info(howtoswap)
glog.V(logger.Warn).Info(howtosync)
glog.V(logger.Warn).Info(separator)
}
return config
}
func ChainDbName(ctx *cli.Context) string {

View File

@ -1,12 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store

View File

@ -1,3 +0,0 @@
language: go
go:
- 1.2

View File

@ -1,140 +0,0 @@
# common
[![Build
Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
The common package contains the ethereum utility library.
# Installation
As a subdirectory the main go-ethereum repository, you get it with
`go get github.com/ethereum/go-ethereum`.
# Usage
## RLP (Recursive Linear Prefix) Encoding
RLP Encoding is an encoding scheme used by the Ethereum project. It
encodes any native value or list to a string.
More in depth information about the encoding scheme see the
[Wiki](http://wiki.ethereum.org/index.php/RLP) article.
```go
rlp := common.Encode("doge")
fmt.Printf("%q\n", rlp) // => "\0x83dog"
rlp = common.Encode([]interface{}{"dog", "cat"})
fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat"
decoded := common.Decode(rlp)
fmt.Println(decoded) // => ["dog" "cat"]
```
## Patricia Trie
Patricie Tree is a merkle trie used by the Ethereum project.
More in depth information about the (modified) Patricia Trie can be
found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree).
The patricia trie uses a db as backend and could be anything as long as
it satisfies the Database interface found in `common/db.go`.
```go
db := NewDatabase()
// db, root
trie := common.NewTrie(db, "")
trie.Put("puppy", "dog")
trie.Put("horse", "stallion")
trie.Put("do", "verb")
trie.Put("doge", "coin")
// Look up the key "do" in the trie
out := trie.Get("do")
fmt.Println(out) // => verb
trie.Delete("puppy")
```
The patricia trie, in combination with RLP, provides a robust,
cryptographically authenticated data structure that can be used to store
all (key, value) bindings.
```go
// ... Create db/trie
// Note that RLP uses interface slices as list
value := common.Encode([]interface{}{"one", 2, "three", []interface{}{42}})
// Store the RLP encoded value of the list
trie.Put("mykey", value)
```
## Value
Value is a Generic Value which is used in combination with RLP data or
`([])interface{}` structures. It may serve as a bridge between RLP data
and actual real values and takes care of all the type checking and
casting. Unlike Go's `reflect.Value` it does not panic if it's unable to
cast to the requested value. It simple returns the base value of that
type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
### Creating a new Value
`NewEmptyValue()` returns a new \*Value with it's initial value set to a
`[]interface{}`
`AppendList()` appends a list to the current value.
`Append(v)` appends the value (v) to the current value/list.
```go
val := common.NewEmptyValue().Append(1).Append("2")
val.AppendList().Append(3)
```
### Retrieving values
`Get(i)` returns the `i` item in the list.
`Uint()` returns the value as an unsigned int64.
`Slice()` returns the value as a interface slice.
`Str()` returns the value as a string.
`Bytes()` returns the value as a byte slice.
`Len()` assumes current to be a slice and returns its length.
`Byte()` returns the value as a single byte.
```go
val := common.NewValue([]interface{}{1,"2",[]interface{}{3}})
val.Get(0).Uint() // => 1
val.Get(1).Str() // => "2"
s := val.Get(2) // => Value([]interface{}{3})
s.Get(0).Uint() // => 3
```
## Decoding
Decoding streams of RLP data is simplified
```go
val := common.NewValueFromBytes(rlpData)
val.Get(0).Uint()
```
## Encoding
Encoding from Value to RLP is done with the `Encode` method. The
underlying value can be anything RLP can encode (int, str, lists, bytes)
```go
val := common.NewValue([]interface{}{1,"2",[]interface{}{3}})
rlp := val.Encode()
// Store the rlp data
Store(rlp)
```

View File

@ -0,0 +1,96 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package math
import (
"math/big"
"sort"
"github.com/ethereum/go-ethereum/common"
)
type Summer interface {
Sum(i int) *big.Int
Len() int
}
func Sum(slice Summer) (sum *big.Int) {
sum = new(big.Int)
for i := 0; i < slice.Len(); i++ {
sum.Add(sum, slice.Sum(i))
}
return
}
type Vector struct {
Gas, Price *big.Int
}
type VectorsBy func(v1, v2 Vector) bool
func (self VectorsBy) Sort(vectors []Vector) {
bs := vectorSorter{
vectors: vectors,
by: self,
}
sort.Sort(bs)
}
type vectorSorter struct {
vectors []Vector
by func(v1, v2 Vector) bool
}
func (v vectorSorter) Len() int { return len(v.vectors) }
func (v vectorSorter) Less(i, j int) bool { return v.by(v.vectors[i], v.vectors[j]) }
func (v vectorSorter) Swap(i, j int) { v.vectors[i], v.vectors[j] = v.vectors[j], v.vectors[i] }
func PriceSort(v1, v2 Vector) bool { return v1.Price.Cmp(v2.Price) < 0 }
func GasSort(v1, v2 Vector) bool { return v1.Gas.Cmp(v2.Gas) < 0 }
type vectorSummer struct {
vectors []Vector
by func(v Vector) *big.Int
}
type VectorSum func(v Vector) *big.Int
func (v VectorSum) Sum(vectors []Vector) *big.Int {
vs := vectorSummer{
vectors: vectors,
by: v,
}
return Sum(vs)
}
func (v vectorSummer) Len() int { return len(v.vectors) }
func (v vectorSummer) Sum(i int) *big.Int { return v.by(v.vectors[i]) }
func GasSum(v Vector) *big.Int { return v.Gas }
var etherInWei = new(big.Rat).SetInt(common.String2Big("1000000000000000000"))
func GasPrice(bp, gl, ep *big.Int) *big.Int {
BP := new(big.Rat).SetInt(bp)
GL := new(big.Rat).SetInt(gl)
EP := new(big.Rat).SetInt(ep)
GP := new(big.Rat).Quo(BP, GL)
GP = GP.Quo(GP, EP)
return GP.Mul(GP, etherInWei).Num()
}

View File

@ -0,0 +1,262 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build ignore
package natspec
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/httpclient"
"github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/xeth"
"github.com/robertkrimen/otto"
)
type abi2method map[[8]byte]*method
type NatSpec struct {
jsvm *otto.Otto
abiDocJson []byte
userDoc userDoc
tx, data string
}
// main entry point for to get natspec notice for a transaction
// the implementation is frontend friendly in that it always gives back
// a notice that is safe to display
// :FIXME: the second return value is an error, which can be used to fine-tune bahaviour
func GetNotice(xeth *xeth.XEth, tx string, http *httpclient.HTTPClient) (notice string) {
ns, err := New(xeth, tx, http)
if err != nil {
if ns == nil {
return getFallbackNotice(fmt.Sprintf("no NatSpec info found for contract: %v", err), tx)
} else {
return getFallbackNotice(fmt.Sprintf("invalid NatSpec info: %v", err), tx)
}
}
notice, err = ns.Notice()
if err != nil {
return getFallbackNotice(fmt.Sprintf("NatSpec notice error: %v", err), tx)
}
return
}
func getFallbackNotice(comment, tx string) string {
return fmt.Sprintf("About to submit transaction (%s): %s", comment, tx)
}
type transaction struct {
To string `json:"to"`
Data string `json:"data"`
}
type jsonTx struct {
Params []transaction `json:"params"`
}
type contractInfo struct {
Source string `json:"source"`
Language string `json:"language"`
Version string `json:"compilerVersion"`
AbiDefinition json.RawMessage `json:"abiDefinition"`
UserDoc userDoc `json:"userDoc"`
DeveloperDoc json.RawMessage `json:"developerDoc"`
}
func New(xeth *xeth.XEth, jsontx string, http *httpclient.HTTPClient) (self *NatSpec, err error) {
// extract contract address from tx
var tx jsonTx
err = json.Unmarshal([]byte(jsontx), &tx)
if err != nil {
return
}
t := tx.Params[0]
contractAddress := t.To
content, err := FetchDocsForContract(contractAddress, xeth, http)
if err != nil {
return
}
self, err = NewWithDocs(content, jsontx, t.Data)
return
}
// also called by admin.contractInfo.get
func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, client *httpclient.HTTPClient) (content []byte, err error) {
// retrieve contract hash from state
codehex := xeth.CodeAt(contractAddress)
codeb := xeth.CodeAtBytes(contractAddress)
if codehex == "0x" {
err = fmt.Errorf("contract (%v) not found", contractAddress)
return
}
codehash := common.BytesToHash(crypto.Keccak256(codeb))
// set up nameresolver with natspecreg + urlhint contract addresses
reg := registrar.New(xeth)
// resolve host via HashReg/UrlHint Resolver
hash, err := reg.HashToHash(codehash)
if err != nil {
return
}
if client.HasScheme("bzz") {
content, err = client.Get("bzz://"+hash.Hex()[2:], "")
if err == nil { // non-fatal
return
}
err = nil
//falling back to urlhint
}
uri, err := reg.HashToUrl(hash)
if err != nil {
return
}
// get content via http client and authenticate content using hash
content, err = client.GetAuthContent(uri, hash)
if err != nil {
return
}
return
}
func NewWithDocs(infoDoc []byte, tx string, data string) (self *NatSpec, err error) {
var contract contractInfo
err = json.Unmarshal(infoDoc, &contract)
if err != nil {
return
}
self = &NatSpec{
jsvm: otto.New(),
abiDocJson: []byte(contract.AbiDefinition),
userDoc: contract.UserDoc,
tx: tx,
data: data,
}
// load and require natspec js (but it is meant to be protected environment)
_, err = self.jsvm.Run(natspecJS)
if err != nil {
return
}
_, err = self.jsvm.Run("var natspec = require('natspec');")
return
}
// type abiDoc []method
// type method struct {
// Name string `json:name`
// Inputs []input `json:inputs`
// abiKey [8]byte
// }
// type input struct {
// Name string `json:name`
// Type string `json:type`
// }
// json skeleton for abi doc (contract method definitions)
type method struct {
Notice string `json:notice`
name string
}
type userDoc struct {
Methods map[string]*method `json:methods`
}
func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) {
for signature, m := range self.userDoc.Methods {
name := strings.Split(signature, "(")[0]
hash := []byte(common.Bytes2Hex(crypto.Keccak256([]byte(signature))))
var key [8]byte
copy(key[:], hash[:8])
if bytes.Equal(key[:], abiKey[:]) {
meth = m
meth.name = name
return
}
}
return
}
func (self *NatSpec) Notice() (notice string, err error) {
var abiKey [8]byte
if len(self.data) < 10 {
err = fmt.Errorf("Invalid transaction data")
return
}
copy(abiKey[:], self.data[2:10])
meth := self.makeAbi2method(abiKey)
if meth == nil {
err = fmt.Errorf("abi key does not match any method")
return
}
notice, err = self.noticeForMethod(self.tx, meth.name, meth.Notice)
return
}
func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) {
if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil {
return "", fmt.Errorf("natspec.js error setting transaction: %v", err)
}
if _, err = self.jsvm.Run("var abi = " + string(self.abiDocJson) + ";"); err != nil {
return "", fmt.Errorf("natspec.js error setting abi: %v", err)
}
if _, err = self.jsvm.Run("var method = '" + name + "';"); err != nil {
return "", fmt.Errorf("natspec.js error setting method: %v", err)
}
if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil {
return "", fmt.Errorf("natspec.js error setting expression: %v", err)
}
self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};")
value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);")
if err != nil {
return "", fmt.Errorf("natspec.js error evaluating expression: %v", err)
}
evalError := "Natspec evaluation failed, wrong input params"
if value.String() == evalError {
return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression)
}
if len(value.String()) == 0 {
return "", fmt.Errorf("natspec.js error evaluating expression")
}
return value.String(), nil
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,197 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package number
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
var tt256 = new(big.Int).Lsh(big.NewInt(1), 256)
var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
var tt255 = new(big.Int).Lsh(big.NewInt(1), 255)
func limitUnsigned256(x *Number) *Number {
x.num.And(x.num, tt256m1)
return x
}
func limitSigned256(x *Number) *Number {
if x.num.Cmp(tt255) < 0 {
return x
} else {
x.num.Sub(x.num, tt256)
return x
}
}
// Number function
type Initialiser func(n int64) *Number
// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations
// to give "fake" bounded integers. New types of Number can be created through NewInitialiser returning a lambda
// with the new Initialiser.
type Number struct {
num *big.Int
limit func(n *Number) *Number
}
// Returns a new initialiser for a new *Number without having to expose certain fields
func NewInitialiser(limiter func(*Number) *Number) Initialiser {
return func(n int64) *Number {
return &Number{big.NewInt(n), limiter}
}
}
// Return a Number with a UNSIGNED limiter up to 256 bits
func Uint256(n int64) *Number {
return &Number{big.NewInt(n), limitUnsigned256}
}
// Return a Number with a SIGNED limiter up to 256 bits
func Int256(n int64) *Number {
return &Number{big.NewInt(n), limitSigned256}
}
// Returns a Number with a SIGNED unlimited size
func Big(n int64) *Number {
return &Number{big.NewInt(n), func(x *Number) *Number { return x }}
}
// Sets i to sum of x+y
func (i *Number) Add(x, y *Number) *Number {
i.num.Add(x.num, y.num)
return i.limit(i)
}
// Sets i to difference of x-y
func (i *Number) Sub(x, y *Number) *Number {
i.num.Sub(x.num, y.num)
return i.limit(i)
}
// Sets i to product of x*y
func (i *Number) Mul(x, y *Number) *Number {
i.num.Mul(x.num, y.num)
return i.limit(i)
}
// Sets i to the quotient prodject of x/y
func (i *Number) Div(x, y *Number) *Number {
i.num.Div(x.num, y.num)
return i.limit(i)
}
// Sets i to x % y
func (i *Number) Mod(x, y *Number) *Number {
i.num.Mod(x.num, y.num)
return i.limit(i)
}
// Sets i to x << s
func (i *Number) Lsh(x *Number, s uint) *Number {
i.num.Lsh(x.num, s)
return i.limit(i)
}
// Sets i to x^y
func (i *Number) Pow(x, y *Number) *Number {
i.num.Exp(x.num, y.num, big.NewInt(0))
return i.limit(i)
}
// Setters
// Set x to i
func (i *Number) Set(x *Number) *Number {
i.num.Set(x.num)
return i.limit(i)
}
// Set x bytes to i
func (i *Number) SetBytes(x []byte) *Number {
i.num.SetBytes(x)
return i.limit(i)
}
// Cmp compares x and y and returns:
//
// -1 if x < y
// 0 if x == y
// +1 if x > y
func (i *Number) Cmp(x *Number) int {
return i.num.Cmp(x.num)
}
// Getters
// Returns the string representation of i
func (i *Number) String() string {
return i.num.String()
}
// Returns the byte representation of i
func (i *Number) Bytes() []byte {
return i.num.Bytes()
}
// Uint64 returns the Uint64 representation of x. If x cannot be represented in an int64, the result is undefined.
func (i *Number) Uint64() uint64 {
return i.num.Uint64()
}
// Int64 returns the int64 representation of x. If x cannot be represented in an int64, the result is undefined.
func (i *Number) Int64() int64 {
return i.num.Int64()
}
// Returns the signed version of i
func (i *Number) Int256() *Number {
return Int(0).Set(i)
}
// Returns the unsigned version of i
func (i *Number) Uint256() *Number {
return Uint(0).Set(i)
}
// Returns the index of the first bit that's set to 1
func (i *Number) FirstBitSet() int {
for j := 0; j < i.num.BitLen(); j++ {
if i.num.Bit(j) > 0 {
return j
}
}
return i.num.BitLen()
}
// Variables
var (
Zero = Uint(0)
One = Uint(1)
Two = Uint(2)
MaxUint256 = Uint(0).SetBytes(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
MinOne = Int(-1)
// "typedefs"
Uint = Uint256
Int = Int256
)

View File

@ -128,7 +128,10 @@ func (m callmsg) FromFrontier() (common.Address, error) {
return m.from.Address(), nil
}
func (m callmsg) Nonce() uint64 {
return m.from.Nonce()
return 0
}
func (m callmsg) CheckNonce() bool {
return false
}
func (m callmsg) To() *common.Address {
return m.to

View File

@ -0,0 +1,101 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package rle implements the run-length encoding used for Ethereum data.
package rle
import (
"bytes"
"errors"
"github.com/ethereum/go-ethereum/crypto"
)
const (
token byte = 0xfe
emptyShaToken = 0xfd
emptyListShaToken = 0xfe
tokenToken = 0xff
)
var empty = crypto.Keccak256([]byte(""))
var emptyList = crypto.Keccak256([]byte{0x80})
func Decompress(dat []byte) ([]byte, error) {
buf := new(bytes.Buffer)
for i := 0; i < len(dat); i++ {
if dat[i] == token {
if i+1 < len(dat) {
switch dat[i+1] {
case emptyShaToken:
buf.Write(empty)
case emptyListShaToken:
buf.Write(emptyList)
case tokenToken:
buf.WriteByte(token)
default:
buf.Write(make([]byte, int(dat[i+1]-2)))
}
i++
} else {
return nil, errors.New("error reading bytes. token encountered without proceeding bytes")
}
} else {
buf.WriteByte(dat[i])
}
}
return buf.Bytes(), nil
}
func compressChunk(dat []byte) (ret []byte, n int) {
switch {
case dat[0] == token:
return []byte{token, tokenToken}, 1
case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0:
j := 0
for j <= 254 && j < len(dat) {
if dat[j] != 0 {
break
}
j++
}
return []byte{token, byte(j + 2)}, j
case len(dat) >= 32:
if dat[0] == empty[0] && bytes.Compare(dat[:32], empty) == 0 {
return []byte{token, emptyShaToken}, 32
} else if dat[0] == emptyList[0] && bytes.Compare(dat[:32], emptyList) == 0 {
return []byte{token, emptyListShaToken}, 32
}
fallthrough
default:
return dat[:1], 1
}
}
func Compress(dat []byte) []byte {
buf := new(bytes.Buffer)
i := 0
for i < len(dat) {
b, n := compressChunk(dat[i:])
buf.Write(b)
i += n
}
return buf.Bytes()
}

View File

@ -0,0 +1,341 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package console
import (
"encoding/json"
"fmt"
"io"
"time"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc"
"github.com/robertkrimen/otto"
"golang.org/x/net/context"
)
// bridge is a collection of JavaScript utility methods to bride the .js runtime
// environment and the Go RPC connection backing the remote method calls.
type bridge struct {
client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through
prompter UserPrompter // Input prompter to allow interactive user feedback
printer io.Writer // Output writer to serialize any display strings to
ctx context.Context
}
// newBridge creates a new JavaScript wrapper around an RPC client.
func newBridge(client *rpc.ClientRestartWrapper, prompter UserPrompter, printer io.Writer) *bridge {
return &bridge{
client: client,
prompter: prompter,
printer: printer,
}
}
func (b *bridge) setContext(ctx context.Context) {
b.ctx = ctx
}
// NewAccount is a wrapper around the personal.newAccount RPC method that uses a
// non-echoing password prompt to aquire the passphrase and executes the original
// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
var (
password string
confirm string
err error
)
switch {
// No password was specified, prompt the user for it
case len(call.ArgumentList) == 0:
if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil {
throwJSException(err.Error())
}
if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil {
throwJSException(err.Error())
}
if password != confirm {
throwJSException("passphrases don't match!")
}
// A single string password was specified, use that
case len(call.ArgumentList) == 1 && call.Argument(0).IsString():
password, _ = call.Argument(0).ToString()
// Otherwise fail with some error
default:
throwJSException("expected 0 or 1 string argument")
}
// Password aquired, execute the call and return
ret, err := call.Otto.Call("jeth.newAccount", nil, password)
if err != nil {
throwJSException(err.Error())
}
return ret
}
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
// uses a non-echoing password prompt to aquire the passphrase and executes the
// original RPC method (saved in jeth.unlockAccount) with it to actually execute
// the RPC call.
func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
// Make sure we have an account specified to unlock
if !call.Argument(0).IsString() {
throwJSException("first argument must be the account to unlock")
}
account := call.Argument(0)
// If password is not given or is the null value, prompt the user for it
var passwd otto.Value
if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
fmt.Fprintf(b.printer, "Unlock account %s\n", account)
if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
}
} else {
if !call.Argument(1).IsString() {
throwJSException("password must be a string")
}
passwd = call.Argument(1)
}
// Third argument is the duration how long the account must be unlocked.
duration := otto.NullValue()
if call.Argument(2).IsDefined() && !call.Argument(2).IsNull() {
if !call.Argument(2).IsNumber() {
throwJSException("unlock duration must be a number")
}
duration = call.Argument(2)
}
// Send the request to the backend and return
val, err := call.Otto.Call("jeth.unlockAccount", nil, account, passwd, duration)
if err != nil {
throwJSException(err.Error())
}
return val
}
// Sleep will block the console for the specified number of seconds.
func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) {
if call.Argument(0).IsNumber() {
sleep, _ := call.Argument(0).ToInteger()
time.Sleep(time.Duration(sleep) * time.Second)
return otto.TrueValue()
}
return throwJSException("usage: sleep(<number of seconds>)")
}
// SleepBlocks will block the console for a specified number of new blocks optionally
// until the given timeout is reached.
func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) {
var (
blocks = int64(0)
sleep = int64(9999999999999999) // indefinitely
)
// Parse the input parameters for the sleep
nArgs := len(call.ArgumentList)
if nArgs == 0 {
throwJSException("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
}
if nArgs >= 1 {
if call.Argument(0).IsNumber() {
blocks, _ = call.Argument(0).ToInteger()
} else {
throwJSException("expected number as first argument")
}
}
if nArgs >= 2 {
if call.Argument(1).IsNumber() {
sleep, _ = call.Argument(1).ToInteger()
} else {
throwJSException("expected number as second argument")
}
}
// go through the console, this will allow web3 to call the appropriate
// callbacks if a delayed response or notification is received.
blockNumber := func() int64 {
result, err := call.Otto.Run("eth.blockNumber")
if err != nil {
throwJSException(err.Error())
}
block, err := result.ToInteger()
if err != nil {
throwJSException(err.Error())
}
return block
}
// Poll the current block number until either it ot a timeout is reached
targetBlockNr := blockNumber() + blocks
deadline := time.Now().Add(time.Duration(sleep) * time.Second)
for time.Now().Before(deadline) {
if blockNumber() >= targetBlockNr {
return otto.TrueValue()
}
time.Sleep(time.Second)
}
return otto.FalseValue()
}
// Send will serialize the first argument, send it to the node and returns the response.
func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
// Ensure that we've got a batch request (array) or a single request (object)
arg := call.Argument(0).Object()
if arg == nil || (arg.Class() != "Array" && arg.Class() != "Object") {
throwJSException("request must be an object or array")
}
// Convert the otto VM arguments to Go values
data, err := call.Otto.Call("JSON.stringify", nil, arg)
if err != nil {
throwJSException(err.Error())
}
reqjson, err := data.ToString()
if err != nil {
throwJSException(err.Error())
}
var (
reqs []rpc.JSONRequest
batch = true
)
if err = json.Unmarshal([]byte(reqjson), &reqs); err != nil {
// single request?
reqs = make([]rpc.JSONRequest, 1)
if err = json.Unmarshal([]byte(reqjson), &reqs[0]); err != nil {
throwJSException("invalid request")
}
batch = false
}
// Iteratively execute the requests
call.Otto.Set("response_len", len(reqs))
call.Otto.Run("var ret_response = new Array(response_len);")
for i, req := range reqs {
// Execute the RPC request and parse the reply
client := b.client.Client()
if err = client.Send(&req); err != nil {
return newErrorResponse(call, -32603, err.Error(), req.Id)
}
errc := make(chan error, 1)
errc2 := make(chan error)
go func(){
if b.ctx != nil {
select {
case <-b.ctx.Done():
b.client.Restart()
errc2 <- b.ctx.Err()
case err := <-errc:
errc2 <- err
}
} else {
errc2 <- <-errc
}
}()
result := make(map[string]interface{})
errc <- client.Recv(&result)
err := <-errc2
if err != nil {
return newErrorResponse(call, -32603, err.Error(), req.Id)
}
// Feed the reply back into the JavaScript runtime environment
id, _ := result["id"]
jsonver, _ := result["jsonrpc"]
call.Otto.Set("ret_id", id)
call.Otto.Set("ret_jsonrpc", jsonver)
call.Otto.Set("response_idx", i)
if res, ok := result["result"]; ok {
payload, _ := json.Marshal(res)
call.Otto.Set("ret_result", string(payload))
response, err = call.Otto.Run(`
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) };
`)
continue
}
if res, ok := result["error"]; ok {
payload, _ := json.Marshal(res)
call.Otto.Set("ret_result", string(payload))
response, err = call.Otto.Run(`
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, error: JSON.parse(ret_result) };
`)
continue
}
return newErrorResponse(call, -32603, fmt.Sprintf("Invalid response"), new(int64))
}
// Convert single requests back from batch ones
if !batch {
call.Otto.Run("ret_response = ret_response[0];")
}
// Execute any registered callbacks
if call.Argument(1).IsObject() {
call.Otto.Set("callback", call.Argument(1))
call.Otto.Run(`
if (Object.prototype.toString.call(callback) == '[object Function]') {
callback(null, ret_response);
}
`)
}
return
}
// throwJSException panics on an otto.Value. The Otto VM will recover from the
// Go panic and throw msg as a JavaScript error.
func throwJSException(msg interface{}) otto.Value {
val, err := otto.ToValue(msg)
if err != nil {
glog.V(logger.Error).Infof("Failed to serialize JavaScript exception %v: %v", msg, err)
}
panic(val)
}
// newErrorResponse creates a JSON RPC error response for a specific request id,
// containing the specified error code and error message. Beside returning the
// error to the caller, it also sets the ret_error and ret_response JavaScript
// variables.
func newErrorResponse(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
// Bundle the error into a JSON RPC call response
res := rpc.JSONErrResponse{
Version: rpc.JSONRPCVersion,
Id: id,
Error: rpc.JSONError{
Code: code,
Message: msg,
},
}
// Serialize the error response into JavaScript variables
errObj, err := json.Marshal(res.Error)
if err != nil {
glog.V(logger.Error).Infof("Failed to serialize JSON RPC error: %v", err)
}
resObj, err := json.Marshal(res)
if err != nil {
glog.V(logger.Error).Infof("Failed to serialize JSON RPC error response: %v", err)
}
if _, err = call.Otto.Run("ret_error = " + string(errObj)); err != nil {
glog.V(logger.Error).Infof("Failed to set `ret_error` to the occurred error: %v", err)
}
resVal, err := call.Otto.Run("ret_response = " + string(resObj))
if err != nil {
glog.V(logger.Error).Infof("Failed to set `ret_response` to the JSON RPC response: %v", err)
}
return resVal
}

View File

@ -0,0 +1,424 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package console
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/signal"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
"github.com/ethereum/go-ethereum/internal/jsre"
"github.com/ethereum/go-ethereum/internal/web3ext"
"github.com/ethereum/go-ethereum/rpc"
"github.com/mattn/go-colorable"
"github.com/peterh/liner"
"github.com/robertkrimen/otto"
"golang.org/x/net/context"
)
var (
passwordRegexp = regexp.MustCompile("personal.[nus]")
onlyWhitespace = regexp.MustCompile("^\\s*$")
exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
)
// HistoryFile is the file within the data directory to store input scrollback.
const HistoryFile = "history"
// DefaultPrompt is the default prompt line prefix to use for user input querying.
const DefaultPrompt = "> "
// Config is te collection of configurations to fine tune the behavior of the
// JavaScript console.
type Config struct {
DataDir string // Data directory to store the console history at
DocRoot string // Filesystem path from where to load JavaScript files from
Client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through
Prompt string // Input prompt prefix string (defaults to DefaultPrompt)
Prompter UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter)
Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout)
Preload []string // Absolute paths to JavaScript files to preload
}
// Console is a JavaScript interpreted runtime environment. It is a fully fleged
// JavaScript console attached to a running node via an external or in-process RPC
// client.
type Console struct {
client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through
jsre *jsre.JSRE // JavaScript runtime environment running the interpreter
prompt string // Input prompt prefix string
prompter UserPrompter // Input prompter to allow interactive user feedback
histPath string // Absolute path to the console scrollback history
history []string // Scroll history maintained by the console
printer io.Writer // Output writer to serialize any display strings to
setContext func(context.Context)
}
func New(config Config) (*Console, error) {
// Handle unset config values gracefully
if config.Prompter == nil {
config.Prompter = Stdin
}
if config.Prompt == "" {
config.Prompt = DefaultPrompt
}
if config.Printer == nil {
config.Printer = colorable.NewColorableStdout()
}
// Initialize the console and return
console := &Console{
client: config.Client,
jsre: jsre.New(config.DocRoot, config.Printer),
prompt: config.Prompt,
prompter: config.Prompter,
printer: config.Printer,
histPath: filepath.Join(config.DataDir, HistoryFile),
}
if err := console.init(config.Preload); err != nil {
return nil, err
}
return console, nil
}
// init retrieves the available APIs from the remote RPC provider and initializes
// the console's JavaScript namespaces based on the exposed modules.
func (c *Console) init(preload []string) error {
// Initialize the JavaScript <-> Go RPC bridge
bridge := newBridge(c.client, c.prompter, c.printer)
c.setContext = bridge.setContext
c.jsre.Set("jeth", struct{}{})
jethObj, _ := c.jsre.Get("jeth")
jethObj.Object().Set("send", bridge.Send)
jethObj.Object().Set("sendAsync", bridge.Send)
consoleObj, _ := c.jsre.Get("console")
consoleObj.Object().Set("log", c.consoleOutput)
consoleObj.Object().Set("error", c.consoleOutput)
// Load all the internal utility JavaScript libraries
if err := c.jsre.Compile("bignumber.js", jsre.BigNumber_JS); err != nil {
return fmt.Errorf("bignumber.js: %v", err)
}
if err := c.jsre.Compile("web3.js", jsre.Web3_JS); err != nil {
return fmt.Errorf("web3.js: %v", err)
}
if _, err := c.jsre.Run("var Web3 = require('web3');"); err != nil {
return fmt.Errorf("web3 require: %v", err)
}
if _, err := c.jsre.Run("var web3 = new Web3(jeth);"); err != nil {
return fmt.Errorf("web3 provider: %v", err)
}
// Load the supported APIs into the JavaScript runtime environment
apis, err := c.client.Client().SupportedModules()
if err != nil {
return fmt.Errorf("api modules: %v", err)
}
flatten := "var eth = web3.eth; var personal = web3.personal; "
for api := range apis {
if api == "web3" {
continue // manually mapped or ignore
}
if file, ok := web3ext.Modules[api]; ok {
if err = c.jsre.Compile(fmt.Sprintf("%s.js", api), file); err != nil {
return fmt.Errorf("%s.js: %v", api, err)
}
flatten += fmt.Sprintf("var %s = web3.%s; ", api, api)
}
}
if _, err = c.jsre.Run(flatten); err != nil {
return fmt.Errorf("namespace flattening: %v", err)
}
// Initialize the global name register (disabled for now)
//c.jsre.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
// If the console is in interactive mode, instrument password related methods to query the user
if c.prompter != nil {
// Retrieve the account management object to instrument
personal, err := c.jsre.Get("personal")
if err != nil {
return err
}
// Override the unlockAccount and newAccount methods since these require user interaction.
// Assign the jeth.unlockAccount and jeth.newAccount in the Console the original web3 callbacks.
// These will be called by the jeth.* methods after they got the password from the user and send
// the original web3 request to the backend.
if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface
if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil {
return fmt.Errorf("personal.unlockAccount: %v", err)
}
if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil {
return fmt.Errorf("personal.newAccount: %v", err)
}
obj.Set("unlockAccount", bridge.UnlockAccount)
obj.Set("newAccount", bridge.NewAccount)
}
}
// The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer.
admin, err := c.jsre.Get("admin")
if err != nil {
return err
}
if obj := admin.Object(); obj != nil { // make sure the admin api is enabled over the interface
obj.Set("sleepBlocks", bridge.SleepBlocks)
obj.Set("sleep", bridge.Sleep)
}
// Preload any JavaScript files before starting the console
for _, path := range preload {
if err := c.jsre.Exec(path); err != nil {
failure := err.Error()
if ottoErr, ok := err.(*otto.Error); ok {
failure = ottoErr.String()
}
return fmt.Errorf("%s: %v", path, failure)
}
}
// Configure the console's input prompter for scrollback and tab completion
if c.prompter != nil {
if content, err := ioutil.ReadFile(c.histPath); err != nil {
c.prompter.SetHistory(nil)
} else {
c.history = strings.Split(string(content), "\n")
c.prompter.SetHistory(c.history)
}
c.prompter.SetWordCompleter(c.AutoCompleteInput)
}
return nil
}
// consoleOutput is an override for the console.log and console.error methods to
// stream the output into the configured output stream instead of stdout.
func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value {
output := []string{}
for _, argument := range call.ArgumentList {
output = append(output, fmt.Sprintf("%v", argument))
}
fmt.Fprintln(c.printer, strings.Join(output, " "))
return otto.Value{}
}
// AutoCompleteInput is a pre-assembled word completer to be used by the user
// input prompter to provide hints to the user about the methods available.
func (c *Console) AutoCompleteInput(line string, pos int) (string, []string, string) {
// No completions can be provided for empty inputs
if len(line) == 0 || pos == 0 {
return "", nil, ""
}
// Chunck data to relevant part for autocompletion
// E.g. in case of nested lines eth.getBalance(eth.coinb<tab><tab>
start := 0
for start = pos - 1; start > 0; start-- {
// Skip all methods and namespaces (i.e. including te dot)
if line[start] == '.' || (line[start] >= 'a' && line[start] <= 'z') || (line[start] >= 'A' && line[start] <= 'Z') {
continue
}
// Handle web3 in a special way (i.e. other numbers aren't auto completed)
if start >= 3 && line[start-3:start] == "web3" {
start -= 3
continue
}
// We've hit an unexpected character, autocomplete form here
start++
break
}
return line[:start], c.jsre.CompleteKeywords(line[start:pos]), line[pos:]
}
// Welcome show summary of current Geth instance and some metadata about the
// console's available modules.
func (c *Console) Welcome() {
// Print some generic Geth metadata
fmt.Fprintf(c.printer, "Welcome to the Geth JavaScript console!\n\n")
c.jsre.Run(`
console.log("instance: " + web3.version.node);
console.log("coinbase: " + eth.coinbase);
console.log("at block: " + eth.blockNumber + " (" + new Date(1000 * eth.getBlock(eth.blockNumber).timestamp) + ")");
console.log(" datadir: " + admin.datadir);
`)
// List all the supported modules for the user to call
if apis, err := c.client.Client().SupportedModules(); err == nil {
modules := make([]string, 0, len(apis))
for api, version := range apis {
modules = append(modules, fmt.Sprintf("%s:%s", api, version))
}
sort.Strings(modules)
fmt.Fprintln(c.printer, " modules:", strings.Join(modules, " "))
}
fmt.Fprintln(c.printer)
}
// Evaluate executes code and pretty prints the result to the specified output
// stream.
func (c *Console) Evaluate(statement string) error {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(c.printer, "[native] error: %v\n", r)
}
}()
if err := c.jsre.Evaluate(statement, c.printer); err != nil {
return err
}
return nil
}
// Interactive starts an interactive user session, where input is propted from
// the configured user prompter.
func (c *Console) Interactive() {
var (
prompt = c.prompt // Current prompt line (used for multi-line inputs)
indents = 0 // Current number of input indents (used for multi-line inputs)
input = "" // Current user input
scheduler = make(chan string) // Channel to send the next prompt on and receive the input
)
// Start a goroutine to listen for promt requests and send back inputs
go func() {
for {
// Read the next user input
line, err := c.prompter.PromptInput(<-scheduler)
if err != nil {
// In case of an error, either clear the prompt or fail
if err == liner.ErrPromptAborted { // ctrl-C
prompt, indents, input = c.prompt, 0, ""
scheduler <- ""
continue
}
close(scheduler)
return
}
// User input retrieved, send for interpretation and loop
scheduler <- line
}
}()
// Monitor Ctrl-C too in case the input is empty and we need to bail
abort := make(chan os.Signal, 1)
signal.Notify(abort, os.Interrupt)
// Start sending prompts to the user and reading back inputs
for {
// Send the next prompt, triggering an input read and process the result
scheduler <- prompt
select {
case <-abort:
// User forcefully quite the console
fmt.Fprintln(c.printer, "caught interrupt, exiting")
return
case line, ok := <-scheduler:
// User input was returned by the prompter, handle special cases
if !ok || (indents <= 0 && exit.MatchString(line)) {
return
}
if onlyWhitespace.MatchString(line) {
continue
}
// Append the line to the input and check for multi-line interpretation
input += line + "\n"
indents = countIndents(input)
if indents <= 0 {
prompt = c.prompt
} else {
prompt = strings.Repeat(".", indents*3) + " "
}
// If all the needed lines are present, save the command and run
if indents <= 0 {
if len(input) > 0 && input[0] != ' ' && !passwordRegexp.MatchString(input) {
if command := strings.TrimSpace(input); len(c.history) == 0 || command != c.history[len(c.history)-1] {
c.history = append(c.history, command)
if c.prompter != nil {
c.prompter.AppendHistory(command)
}
}
}
done := make(chan struct{})
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
c.setContext(ctx)
c.Evaluate(input)
c.setContext(nil)
close(done)
input = ""
}
}
}
}
// countIndents returns the number of identations for the given input.
// In case of invalid input such as var a = } the result can be negative.
func countIndents(input string) int {
var (
indents = 0
inString = false
strOpenChar = ' ' // keep track of the string open char to allow var str = "I'm ....";
charEscaped = false // keep track if the previous char was the '\' char, allow var str = "abc\"def";
)
for _, c := range input {
switch c {
case '\\':
// indicate next char as escaped when in string and previous char isn't escaping this backslash
if !charEscaped && inString {
charEscaped = true
}
case '\'', '"':
if inString && !charEscaped && strOpenChar == c { // end string
inString = false
} else if !inString && !charEscaped { // begin string
inString = true
strOpenChar = c
}
charEscaped = false
case '{', '(':
if !inString { // ignore brackets when in string, allow var str = "a{"; without indenting
indents++
}
charEscaped = false
case '}', ')':
if !inString {
indents--
}
charEscaped = false
default:
charEscaped = false
}
}
return indents
}
// Execute runs the JavaScript file specified as the argument.
func (c *Console) Execute(path string) error {
return c.jsre.Exec(path)
}
// Stop cleans up the console and terminates the runtime envorinment.
func (c *Console) Stop(graceful bool) error {
if err := ioutil.WriteFile(c.histPath, []byte(strings.Join(c.history, "\n")), 0600); err != nil {
return err
}
if err := os.Chmod(c.histPath, 0600); err != nil { // Force 0600, even if it was different previously
return err
}
c.jsre.Stop(graceful)
return nil
}

View File

@ -0,0 +1,165 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package console
import (
"fmt"
"strings"
"github.com/peterh/liner"
)
// Stdin holds the stdin line reader (also using stdout for printing prompts).
// Only this reader may be used for input because it keeps an internal buffer.
var Stdin = newTerminalPrompter()
// UserPrompter defines the methods needed by the console to promt the user for
// various types of inputs.
type UserPrompter interface {
// PromptInput displays the given prompt to the user and requests some textual
// data to be entered, returning the input of the user.
PromptInput(prompt string) (string, error)
// PromptPassword displays the given prompt to the user and requests some textual
// data to be entered, but one which must not be echoed out into the terminal.
// The method returns the input provided by the user.
PromptPassword(prompt string) (string, error)
// PromptConfirm displays the given prompt to the user and requests a boolean
// choice to be made, returning that choice.
PromptConfirm(prompt string) (bool, error)
// SetHistory sets the the input scrollback history that the prompter will allow
// the user to scoll back to.
SetHistory(history []string)
// AppendHistory appends an entry to the scrollback history. It should be called
// if and only if the prompt to append was a valid command.
AppendHistory(command string)
// SetWordCompleter sets the completion function that the prompter will call to
// fetch completion candidates when the user presses tab.
SetWordCompleter(completer WordCompleter)
}
// WordCompleter takes the currently edited line with the cursor position and
// returns the completion candidates for the partial word to be completed. If
// the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello,
// wo!!!", 9) is passed to the completer which may returns ("Hello, ", {"world",
// "Word"}, "!!!") to have "Hello, world!!!".
type WordCompleter func(line string, pos int) (string, []string, string)
// terminalPrompter is a UserPrompter backed by the liner package. It supports
// prompting the user for various input, among others for non-echoing password
// input.
type terminalPrompter struct {
*liner.State
warned bool
supported bool
normalMode liner.ModeApplier
rawMode liner.ModeApplier
}
// newTerminalPrompter creates a liner based user input prompter working off the
// standard input and output streams.
func newTerminalPrompter() *terminalPrompter {
p := new(terminalPrompter)
// Get the original mode before calling NewLiner.
// This is usually regular "cooked" mode where characters echo.
normalMode, _ := liner.TerminalMode()
// Turn on liner. It switches to raw mode.
p.State = liner.NewLiner()
rawMode, err := liner.TerminalMode()
if err != nil || !liner.TerminalSupported() {
p.supported = false
} else {
p.supported = true
p.normalMode = normalMode
p.rawMode = rawMode
// Switch back to normal mode while we're not prompting.
normalMode.ApplyMode()
}
p.SetCtrlCAborts(true)
p.SetTabCompletionStyle(liner.TabPrints)
return p
}
// PromptInput displays the given prompt to the user and requests some textual
// data to be entered, returning the input of the user.
func (p *terminalPrompter) PromptInput(prompt string) (string, error) {
if p.supported {
p.rawMode.ApplyMode()
defer p.normalMode.ApplyMode()
} else {
// liner tries to be smart about printing the prompt
// and doesn't print anything if input is redirected.
// Un-smart it by printing the prompt always.
fmt.Print(prompt)
prompt = ""
defer fmt.Println()
}
return p.State.Prompt(prompt)
}
// PromptPassword displays the given prompt to the user and requests some textual
// data to be entered, but one which must not be echoed out into the terminal.
// The method returns the input provided by the user.
func (p *terminalPrompter) PromptPassword(prompt string) (passwd string, err error) {
if p.supported {
p.rawMode.ApplyMode()
defer p.normalMode.ApplyMode()
return p.State.PasswordPrompt(prompt)
}
if !p.warned {
fmt.Println("!! Unsupported terminal, password will be echoed.")
p.warned = true
}
// Just as in Prompt, handle printing the prompt here instead of relying on liner.
fmt.Print(prompt)
passwd, err = p.State.Prompt("")
fmt.Println()
return passwd, err
}
// PromptConfirm displays the given prompt to the user and requests a boolean
// choice to be made, returning that choice.
func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) {
input, err := p.Prompt(prompt + " [y/N] ")
if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
return true, nil
}
return false, err
}
// SetHistory sets the the input scrollback history that the prompter will allow
// the user to scoll back to.
func (p *terminalPrompter) SetHistory(history []string) {
p.State.ReadHistory(strings.NewReader(strings.Join(history, "\n")))
}
// AppendHistory appends an entry to the scrollback history. It should be called
// if and only if the prompt to append was a valid command.
func (p *terminalPrompter) AppendHistory(command string) {
p.State.AppendHistory(command)
}
// SetWordCompleter sets the completion function that the prompter will call to
// fetch completion candidates when the user presses tab.
func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) {
p.State.SetWordCompleter(liner.WordCompleter(completer))
}

View File

@ -1,12 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store

View File

@ -64,7 +64,7 @@ func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW)
//
// ValidateBlock also validates and makes sure that any previous state (or present)
// state that might or might not be present is checked to make sure that fast
// sync has done it's job proper. This prevents the block validator form accepting
// sync has done it's job proper. This prevents the block validator from accepting
// false positives where a header is present but the state is not.
func (v *BlockValidator) ValidateBlock(block *types.Block) error {
if v.bc.HasBlock(block.Hash()) {
@ -139,7 +139,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
// error if any of the included uncle headers were invalid. It returns an error
// if the validation failed.
func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
// validate that there at most 2 uncles included in this block
// validate that there are at most 2 uncles included in this block
if len(block.Uncles()) > 2 {
return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles()))
}
@ -193,7 +193,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
if parent == nil {
return ParentError(header.ParentHash)
}
// Short circuit if the header's already known or its parent missing
// Short circuit if the header's already known or its parent is missing
if v.bc.HasHeader(header.Hash()) {
return nil
}
@ -247,7 +247,8 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
}
}
return nil
// If all checks passed, validate the extra-data field for hard forks
return ValidateDAOHeaderExtraData(config, header)
}
// CalcDifficulty is the difficulty adjustment algorithm. It returns

View File

@ -777,14 +777,13 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
if ptd == nil {
return NonStatTy, ParentError(block.ParentHash())
}
localTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd)
// Make sure no inconsistent state is leaked during insertion
self.mu.Lock()
defer self.mu.Unlock()
localTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd)
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf

View File

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
)
@ -35,7 +36,11 @@ import (
// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
func MakeChainConfig() *ChainConfig {
return &ChainConfig{HomesteadBlock: big.NewInt(0)}
return &ChainConfig{
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
}
}
// FakePow is a non-validating proof of work implementation.
@ -173,10 +178,27 @@ func (b *BlockGen) OffsetTime(seconds int64) {
// Blocks created by GenerateChain do not contain valid proof of work
// values. Inserting them into BlockChain requires use of FakePow or
// a similar non-validating proof of work implementation.
func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
// Mutate the state and block according to any hard-fork specs
if config == nil {
config = MakeChainConfig()
}
if daoBlock := config.DAOForkBlock; daoBlock != nil {
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 {
if config.DAOForkSupport {
h.Extra = common.CopyBytes(params.DAOForkBlockExtra)
}
}
}
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 {
ApplyDAOHardFork(statedb)
}
// Execute any user modifications to the block and finalize it
if gen != nil {
gen(i, b)
}
@ -261,7 +283,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
blocks, _ := GenerateChain(parent, db, n, func(i int, b *BlockGen) {
blocks, _ := GenerateChain(nil, parent, db, n, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
})
return blocks

View File

@ -31,16 +31,17 @@ var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general conf
// that any network, identified by its genesis block, can have its own
// set of configuration options.
type ChainConfig struct {
HomesteadBlock *big.Int // homestead switch block
HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead)
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
VmConfig vm.Config `json:"-"`
}
// IsHomestead returns whether num is either equal to the homestead block or greater.
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
if num == nil {
if c.HomesteadBlock == nil || num == nil {
return false
}
return num.Cmp(c.HomesteadBlock) >= 0
}

74
src/vendor/github.com/ethereum/go-ethereum/core/dao.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
// ValidateDAOHeaderExtraData validates the extra-data field of a block header to
// ensure it conforms to DAO hard-fork rules.
//
// DAO hard-fork extension to the header validity:
// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range
// with the fork specific extra-data set
// b) if the node is pro-fork, require blocks in the specific range to have the
// unique extra-data set.
func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error {
// Short circuit validation if the node doesn't care about the DAO fork
if config.DAOForkBlock == nil {
return nil
}
// Make sure the block is within the fork's modified extra-data range
limit := new(big.Int).Add(config.DAOForkBlock, params.DAOForkExtraRange)
if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 {
return nil
}
// Depending whether we support or oppose the fork, validate the extra-data contents
if config.DAOForkSupport {
if bytes.Compare(header.Extra, params.DAOForkBlockExtra) != 0 {
return ValidationError("DAO pro-fork bad block extra-data: 0x%x", header.Extra)
}
} else {
if bytes.Compare(header.Extra, params.DAOForkBlockExtra) == 0 {
return ValidationError("DAO no-fork bad block extra-data: 0x%x", header.Extra)
}
}
// All ok, header has the same extra-data we expect
return nil
}
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
// rules, transferring all balances of a set of DAO accounts to a single refund
// contract.
func ApplyDAOHardFork(statedb *state.StateDB) {
// Retrieve the contract to refund balances into
refund := statedb.GetOrNewStateObject(params.DAORefundContract)
// Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList {
if account := statedb.GetStateObject(addr); account != nil {
refund.AddBalance(account.Balance())
account.SetBalance(new(big.Int))
}
}
}

View File

@ -625,3 +625,30 @@ func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) {
return &config, nil
}
// FindCommonAncestor returns the last common ancestor of two block headers
func FindCommonAncestor(db ethdb.Database, a, b *types.Header) *types.Header {
for a.GetNumberU64() > b.GetNumberU64() {
a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1)
if a == nil {
return nil
}
}
for a.GetNumberU64() < b.GetNumberU64() {
b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1)
if b == nil {
return nil
}
}
for a.Hash() != b.Hash() {
a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1)
if a == nil {
return nil
}
b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1)
if b == nil {
return nil
}
}
return a
}

View File

@ -370,6 +370,27 @@ func (s *StateDB) IntermediateRoot() common.Hash {
return s.trie.Hash()
}
// DeleteSuicides flags the suicided objects for deletion so that it
// won't be referenced again when called / queried up on.
//
// DeleteSuicides should not be used for consensus related updates
// under any circumstances.
func (s *StateDB) DeleteSuicides() {
// Reset refund so that any used-gas calculations can use
// this method.
s.refund = new(big.Int)
for _, stateObject := range s.stateObjects {
if stateObject.dirty {
// If the object has been removed by a suicide
// flag the object as deleted.
if stateObject.remove {
stateObject.deleted = true
}
stateObject.dirty = false
}
}
}
// Commit commits all state changes to the database.
func (s *StateDB) Commit() (root common.Hash, err error) {
root, batch := s.CommitBatch()

View File

@ -65,7 +65,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
allLogs vm.Logs
gp = new(GasPool).AddGas(block.GasLimit())
)
// Mutate the the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
ApplyDAOHardFork(statedb)
}
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i)
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)

View File

@ -71,6 +71,7 @@ type Message interface {
Value() *big.Int
Nonce() uint64
CheckNonce() bool
Data() []byte
}
@ -208,8 +209,10 @@ func (self *StateTransition) preCheck() (err error) {
}
// Make sure this transaction's nonce is correct
if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() {
return NonceError(msg.Nonce(), n)
if msg.CheckNonce() {
if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() {
return NonceError(msg.Nonce(), n)
}
}
// Pre-pay gas

View File

@ -113,6 +113,7 @@ func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.Gas
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
func (tx *Transaction) CheckNonce() bool { return true }
func (tx *Transaction) To() *common.Address {
if tx.data.Recipient == nil {

View File

@ -421,7 +421,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
g = params.SstoreClearGas
} else {
g = params.SstoreClearGas
g = params.SstoreResetGas
}
gas.Set(g)
case SUICIDE:

View File

@ -1,4 +1,4 @@
// Copyright 2015 The go-ethereum Authors
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@ -14,20 +14,5 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package light
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type LightChainSplitEvent struct{ Header *types.Header }
type LightChainEvent struct {
Header *types.Header
Hash common.Hash
}
type LightChainSideEvent struct{ Header *types.Header }
type LightChainHeadEvent struct{ Header *types.Header }
// Package runtime provides a basic execution model for executing EVM code.
package runtime

View File

@ -0,0 +1,127 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package runtime
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
)
// Env is a basic runtime environment required for running the EVM.
type Env struct {
ruleSet vm.RuleSet
depth int
state *state.StateDB
origin common.Address
coinbase common.Address
number *big.Int
time *big.Int
difficulty *big.Int
gasLimit *big.Int
logs []vm.StructLog
getHashFn func(uint64) common.Hash
evm *vm.EVM
}
// NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
env := &Env{
ruleSet: cfg.RuleSet,
state: state,
origin: cfg.Origin,
coinbase: cfg.Coinbase,
number: cfg.BlockNumber,
time: cfg.Time,
difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
}
env.evm = vm.New(env, vm.Config{
Debug: cfg.Debug,
EnableJit: !cfg.DisableJit,
ForceJit: !cfg.DisableJit,
Logger: vm.LogConfig{
Collector: env,
},
})
return env
}
func (self *Env) StructLogs() []vm.StructLog {
return self.logs
}
func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin }
func (self *Env) BlockNumber() *big.Int { return self.number }
func (self *Env) Coinbase() common.Address { return self.coinbase }
func (self *Env) Time() *big.Int { return self.time }
func (self *Env) Difficulty() *big.Int { return self.difficulty }
func (self *Env) Db() vm.Database { return self.state }
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
func (self *Env) GetHash(n uint64) common.Hash {
return self.getHashFn(n)
}
func (self *Env) AddLog(log *vm.Log) {
self.state.AddLog(log)
}
func (self *Env) Depth() int { return self.depth }
func (self *Env) SetDepth(i int) { self.depth = i }
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *Env) MakeSnapshot() vm.Database {
return self.state.Copy()
}
func (self *Env) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB))
}
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
core.Transfer(from, to, amount)
}
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.Call(self, caller, addr, data, gas, price, value)
}
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.CallCode(self, caller, addr, data, gas, price, value)
}
func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
return core.DelegateCall(self, me, addr, data, gas, price)
}
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return core.Create(self, caller, data, gas, price, value)
}

View File

@ -0,0 +1,144 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package runtime
import (
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
)
// The default, always homestead, rule set for the vm env
type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true }
// Config is a basic type specifying certain configuration flags for running
// the EVM.
type Config struct {
RuleSet vm.RuleSet
Difficulty *big.Int
Origin common.Address
Coinbase common.Address
BlockNumber *big.Int
Time *big.Int
GasLimit *big.Int
GasPrice *big.Int
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
Debug bool
State *state.StateDB
GetHashFn func(n uint64) common.Hash
}
// sets defaults on the config
func setDefaults(cfg *Config) {
if cfg.RuleSet == nil {
cfg.RuleSet = ruleSet{}
}
if cfg.Difficulty == nil {
cfg.Difficulty = new(big.Int)
}
if cfg.Time == nil {
cfg.Time = big.NewInt(time.Now().Unix())
}
if cfg.GasLimit == nil {
cfg.GasLimit = new(big.Int).Set(common.MaxBig)
}
if cfg.GasPrice == nil {
cfg.GasPrice = new(big.Int)
}
if cfg.Value == nil {
cfg.Value = new(big.Int)
}
if cfg.BlockNumber == nil {
cfg.BlockNumber = new(big.Int)
}
if cfg.GetHashFn == nil {
cfg.GetHashFn = func(n uint64) common.Hash {
return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
}
}
}
// Execute executes the code using the input as call data during the execution.
// It returns the EVM's return value, the new state and an error if it failed.
//
// Executes sets up a in memory, temporarily, environment for the execution of
// the given code. It enabled the JIT by default and make sure that it's restored
// to it's original state afterwards.
func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
if cfg == nil {
cfg = new(Config)
}
setDefaults(cfg)
if cfg.State == nil {
db, _ := ethdb.NewMemDatabase()
cfg.State, _ = state.New(common.Hash{}, db)
}
var (
vmenv = NewEnv(cfg, cfg.State)
sender = cfg.State.CreateAccount(cfg.Origin)
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
)
// set the receiver's (the executing contract) code for execution.
receiver.SetCode(code)
// Call the code with the given configuration.
ret, err := vmenv.Call(
sender,
receiver.Address(),
input,
cfg.GasLimit,
cfg.GasPrice,
cfg.Value,
)
return ret, cfg.State, err
}
// Call executes the code given by the contract's address. It will return the
// EVM's return value or an error if it failed.
//
// Call, unlike Execute, requires a config and also requires the State field to
// be set.
func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
setDefaults(cfg)
vmenv := NewEnv(cfg, cfg.State)
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
// Call the code with the given configuration.
ret, err := vmenv.Call(
sender,
address,
input,
cfg.GasLimit,
cfg.GasPrice,
cfg.Value,
)
return ret, err
}

View File

@ -306,7 +306,7 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
g = params.SstoreClearGas
} else {
// non 0 => non 0 (or 0 => 0)
g = params.SstoreClearGas
g = params.SstoreResetGas
}
gas.Set(g)
case SUICIDE:

View File

@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*~

View File

@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*~

View File

@ -1,41 +0,0 @@
bench_inv
bench_ecdh
bench_sign
bench_verify
bench_schnorr_verify
bench_recover
bench_internal
tests
gen_context
*.exe
*.so
*.a
!.gitignore
Makefile
configure
.libs/
Makefile.in
aclocal.m4
autom4te.cache/
config.log
config.status
*.tar.gz
*.la
libtool
.deps/
.dirstamp
build-aux/
*.lo
*.o
*~
src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
m4/libtool.m4
m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/lt~obsolete.m4
src/stamp-h1
libsecp256k1.pc

View File

@ -1,62 +0,0 @@
language: c
sudo: false
addons:
apt:
packages: libgmp-dev
compiler:
- clang
- gcc
env:
global:
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=NO RECOVERY=NO
matrix:
- SCALAR=32bit RECOVERY=yes
- SCALAR=32bit FIELD=32bit ECDH=yes
- SCALAR=64bit
- FIELD=64bit RECOVERY=yes
- FIELD=64bit ENDOMORPHISM=yes
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes
- FIELD=64bit ASM=x86_64
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- FIELD=32bit SCHNORR=yes
- FIELD=32bit ENDOMORPHISM=yes
- BIGNUM=no
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck
- EXTRAFLAGS=CFLAGS=-DDETERMINISTIC
matrix:
fast_finish: true
include:
- compiler: clang
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
addons:
apt:
packages:
- gcc-multilib
- libgmp-dev:i386
- compiler: clang
env: HOST=i686-linux-gnu
addons:
apt:
packages:
- gcc-multilib
- compiler: gcc
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
addons:
apt:
packages:
- gcc-multilib
- compiler: gcc
env: HOST=i686-linux-gnu
addons:
apt:
packages:
- gcc-multilib
- libgmp-dev:i386
before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux

View File

@ -1,61 +0,0 @@
libsecp256k1
============
[![Build Status](https://travis-ci.org/bitcoin/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin/secp256k1)
Optimized C library for EC operations on curve secp256k1.
This library is a work in progress and is being used to research best practices. Use at your own risk.
Features:
* secp256k1 ECDSA signing/verification and key generation.
* Adding/multiplying private/public keys.
* Serialization/parsing of private keys, public keys, signatures.
* Constant time, constant memory access signing and pubkey generation.
* Derandomized DSA (via RFC6979 or with a caller provided function.)
* Very efficient implementation.
Implementation details
----------------------
* General
* No runtime heap allocation.
* Extensive testing infrastructure.
* Structured to facilitate review and analysis.
* Intended to be portable to any system with a C89 compiler and uint64_t support.
* Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.")
* Field operations
* Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1).
* Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys).
* Using 10 26-bit limbs.
* Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman).
* Scalar operations
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
* Using 4 64-bit limbs (relying on __int128 support in the compiler).
* Using 8 32-bit limbs.
* Group operations
* Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7).
* Use addition between points in Jacobian and affine coordinates where possible.
* Use a unified addition/doubling formula where necessary to avoid data-dependent branches.
* Point/x comparison without a field inversion by comparison in the Jacobian coordinate space.
* Point multiplication for verification (a*P + b*G).
* Use wNAF notation for point multiplicands.
* Use a much larger window for multiples of G, using precomputed multiples.
* Use Shamir's trick to do the multiplication with the public key and the generator simultaneously.
* Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
* Point multiplication for signing
* Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
* Access the table with branch-free conditional moves so memory access is uniform.
* No data-dependent branches
* The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally.
Build steps
-----------
libsecp256k1 is built using autotools:
$ ./autogen.sh
$ ./configure
$ make
$ ./tests
$ sudo make install # optional

View File

@ -0,0 +1,110 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package errs
import (
"fmt"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
/*
Errors implements an error handler providing standardised errors for a package.
Fields:
Errors:
a map from error codes to description
Package:
name of the package/component
Level:
a function mapping error code to logger.LogLevel (severity)
if not given, errors default to logger.InfoLevel
*/
type Errors struct {
Errors map[int]string
Package string
Level func(code int) logger.LogLevel
}
/*
Error implements the standard go error interface.
errors.New(code, format, params ...interface{})
Prints as:
[package] description: details
where details is fmt.Sprintf(self.format, self.params...)
*/
type Error struct {
Code int
Name string
Package string
level logger.LogLevel
message string
format string
params []interface{}
}
func (self *Errors) New(code int, format string, params ...interface{}) *Error {
name, ok := self.Errors[code]
if !ok {
panic("invalid error code")
}
level := logger.InfoLevel
if self.Level != nil {
level = self.Level(code)
}
return &Error{
Code: code,
Name: name,
Package: self.Package,
level: level,
format: format,
params: params,
}
}
func (self Error) Error() (message string) {
if len(message) == 0 {
self.message = fmt.Sprintf("[%s] ERROR: %s", self.Package, self.Name)
if self.format != "" {
self.message += ": " + fmt.Sprintf(self.format, self.params...)
}
}
return self.message
}
func (self Error) Log(v glog.Verbose) {
if v {
v.Infoln(self)
}
}
/*
err.Fatal() is true if err's severity level is 0 or 1 (logger.ErrorLevel or logger.Silence)
*/
func (self *Error) Fatal() (fatal bool) {
if self.level < logger.WarnLevel {
fatal = true
}
return
}

View File

@ -40,41 +40,41 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
// PublicFullEthereumAPI provides an API to access Ethereum full node-related
// PublicEthereumAPI provides an API to access Ethereum full node-related
// information.
type PublicFullEthereumAPI struct {
e *FullNodeService
type PublicEthereumAPI struct {
e *Ethereum
}
// NewPublicFullEthereumAPI creates a new Etheruem protocol API for full nodes.
func NewPublicFullEthereumAPI(e *FullNodeService) *PublicFullEthereumAPI {
return &PublicFullEthereumAPI{e}
// NewPublicEthereumAPI creates a new Etheruem protocol API for full nodes.
func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI {
return &PublicEthereumAPI{e}
}
// Etherbase is the address that mining rewards will be send to
func (s *PublicFullEthereumAPI) Etherbase() (common.Address, error) {
func (s *PublicEthereumAPI) Etherbase() (common.Address, error) {
return s.e.Etherbase()
}
// Coinbase is the address that mining rewards will be send to (alias for Etherbase)
func (s *PublicFullEthereumAPI) Coinbase() (common.Address, error) {
func (s *PublicEthereumAPI) Coinbase() (common.Address, error) {
return s.Etherbase()
}
// Hashrate returns the POW hashrate
func (s *PublicFullEthereumAPI) Hashrate() *rpc.HexNumber {
func (s *PublicEthereumAPI) Hashrate() *rpc.HexNumber {
return rpc.NewHexNumber(s.e.Miner().HashRate())
}
// PublicMinerAPI provides an API to control the miner.
// It offers only methods that operate on data that pose no security risk when it is publicly accessible.
type PublicMinerAPI struct {
e *FullNodeService
e *Ethereum
agent *miner.RemoteAgent
}
// NewPublicMinerAPI create a new PublicMinerAPI instance.
func NewPublicMinerAPI(e *FullNodeService) *PublicMinerAPI {
func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI {
agent := miner.NewRemoteAgent()
e.Miner().Register(agent)
@ -120,11 +120,11 @@ func (s *PublicMinerAPI) SubmitHashrate(hashrate rpc.HexNumber, id common.Hash)
// PrivateMinerAPI provides private RPC methods to control the miner.
// These methods can be abused by external users and must be considered insecure for use by untrusted users.
type PrivateMinerAPI struct {
e *FullNodeService
e *Ethereum
}
// NewPrivateMinerAPI create a new RPC service which controls the miner of this node.
func NewPrivateMinerAPI(e *FullNodeService) *PrivateMinerAPI {
func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
return &PrivateMinerAPI{e: e}
}
@ -191,20 +191,20 @@ func (s *PrivateMinerAPI) MakeDAG(blockNr rpc.BlockNumber) (bool, error) {
return true, nil
}
// PrivateFullAdminAPI is the collection of Etheruem full node-related APIs
// PrivateAdminAPI is the collection of Etheruem full node-related APIs
// exposed over the private admin endpoint.
type PrivateFullAdminAPI struct {
eth *FullNodeService
type PrivateAdminAPI struct {
eth *Ethereum
}
// NewPrivateAdminAPI creates a new API definition for the full node private
// admin methods of the Ethereum service.
func NewPrivateFullAdminAPI(eth *FullNodeService) *PrivateFullAdminAPI {
return &PrivateFullAdminAPI{eth: eth}
func NewPrivateAdminAPI(eth *Ethereum) *PrivateAdminAPI {
return &PrivateAdminAPI{eth: eth}
}
// ExportChain exports the current blockchain into a local file.
func (api *PrivateFullAdminAPI) ExportChain(file string) (bool, error) {
func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) {
// Make sure we can create the file to export into
out, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
@ -230,7 +230,7 @@ func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
}
// ImportChain imports a blockchain from a local file.
func (api *PrivateFullAdminAPI) ImportChain(file string) (bool, error) {
func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
// Make sure the can access the file to import
in, err := os.Open(file)
if err != nil {
@ -271,20 +271,20 @@ func (api *PrivateFullAdminAPI) ImportChain(file string) (bool, error) {
return true, nil
}
// PublicFullDebugAPI is the collection of Etheruem full node APIs exposed
// PublicDebugAPI is the collection of Etheruem full node APIs exposed
// over the public debugging endpoint.
type PublicFullDebugAPI struct {
eth *FullNodeService
type PublicDebugAPI struct {
eth *Ethereum
}
// NewPublicFullDebugAPI creates a new API definition for the full node-
// NewPublicDebugAPI creates a new API definition for the full node-
// related public debug methods of the Ethereum service.
func NewPublicFullDebugAPI(eth *FullNodeService) *PublicFullDebugAPI {
return &PublicFullDebugAPI{eth: eth}
func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI {
return &PublicDebugAPI{eth: eth}
}
// DumpBlock retrieves the entire state of the database at a given block.
func (api *PublicFullDebugAPI) DumpBlock(number uint64) (state.World, error) {
func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) {
block := api.eth.BlockChain().GetBlockByNumber(number)
if block == nil {
return state.World{}, fmt.Errorf("block #%d not found", number)
@ -296,17 +296,17 @@ func (api *PublicFullDebugAPI) DumpBlock(number uint64) (state.World, error) {
return stateDb.RawDump(), nil
}
// PrivateFullDebugAPI is the collection of Etheruem full node APIs exposed over
// PrivateDebugAPI is the collection of Etheruem full node APIs exposed over
// the private debugging endpoint.
type PrivateFullDebugAPI struct {
type PrivateDebugAPI struct {
config *core.ChainConfig
eth *FullNodeService
eth *Ethereum
}
// NewPrivateFullDebugAPI creates a new API definition for the full node-related
// NewPrivateDebugAPI creates a new API definition for the full node-related
// private debug methods of the Ethereum service.
func NewPrivateFullDebugAPI(config *core.ChainConfig, eth *FullNodeService) *PrivateFullDebugAPI {
return &PrivateFullDebugAPI{config: config, eth: eth}
func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI {
return &PrivateDebugAPI{config: config, eth: eth}
}
// BlockTraceResult is the returned value when replaying a block to check for
@ -319,7 +319,7 @@ type BlockTraceResult struct {
// TraceBlock processes the given block's RLP but does not import the block in to
// the chain.
func (api *PrivateFullDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) BlockTraceResult {
func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) BlockTraceResult {
var block types.Block
err := rlp.Decode(bytes.NewReader(blockRlp), &block)
if err != nil {
@ -336,7 +336,7 @@ func (api *PrivateFullDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) B
// TraceBlockFromFile loads the block's RLP from the given file name and attempts to
// process it but does not import the block in to the chain.
func (api *PrivateFullDebugAPI) TraceBlockFromFile(file string, config *vm.Config) BlockTraceResult {
func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) BlockTraceResult {
blockRlp, err := ioutil.ReadFile(file)
if err != nil {
return BlockTraceResult{Error: fmt.Sprintf("could not read file: %v", err)}
@ -345,7 +345,7 @@ func (api *PrivateFullDebugAPI) TraceBlockFromFile(file string, config *vm.Confi
}
// TraceBlockByNumber processes the block by canonical block number.
func (api *PrivateFullDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult {
func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult {
// Fetch the block that we aim to reprocess
block := api.eth.BlockChain().GetBlockByNumber(number)
if block == nil {
@ -361,7 +361,7 @@ func (api *PrivateFullDebugAPI) TraceBlockByNumber(number uint64, config *vm.Con
}
// TraceBlockByHash processes the block by hash.
func (api *PrivateFullDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config) BlockTraceResult {
func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config) BlockTraceResult {
// Fetch the block that we aim to reprocess
block := api.eth.BlockChain().GetBlockByHash(hash)
if block == nil {
@ -389,7 +389,7 @@ func (t *TraceCollector) AddStructLog(slog vm.StructLog) {
}
// traceBlock processes the given block but does not save the state.
func (api *PrivateFullDebugAPI) traceBlock(block *types.Block, config *vm.Config) (bool, []vm.StructLog, error) {
func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (bool, []vm.StructLog, error) {
// Validate and reprocess the block
var (
blockchain = api.eth.BlockChain()
@ -424,7 +424,6 @@ func (api *PrivateFullDebugAPI) traceBlock(block *types.Block, config *vm.Config
// callmsg is the message type used for call transations.
type callmsg struct {
addr common.Address
nonce uint64
to *common.Address
gas, gasPrice *big.Int
value *big.Int
@ -434,7 +433,8 @@ type callmsg struct {
// accessor boilerplate to implement core.Message
func (m callmsg) From() (common.Address, error) { return m.addr, nil }
func (m callmsg) FromFrontier() (common.Address, error) { return m.addr, nil }
func (m callmsg) Nonce() uint64 { return m.nonce }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.to }
func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
func (m callmsg) Gas() *big.Int { return m.gas }
@ -452,7 +452,7 @@ func formatError(err error) string {
// TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object.
func (api *PrivateFullDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ethapi.ExecutionResult, error) {
func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ethapi.ExecutionResult, error) {
if logger == nil {
logger = new(vm.LogConfig)
}
@ -496,6 +496,7 @@ func (api *PrivateFullDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.
if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err)
}
stateDb.DeleteSuicides()
continue
}
// Otherwise trace the transaction and return

View File

@ -36,7 +36,7 @@ import (
// EthApiBackend implements ethapi.Backend for full nodes
type EthApiBackend struct {
eth *FullNodeService
eth *Ethereum
gpo *gasprice.GasPriceOracle
}
@ -44,17 +44,17 @@ func (b *EthApiBackend) SetHead(number uint64) {
b.eth.blockchain.SetHead(number)
}
func (b *EthApiBackend) HeaderByNumber(blockNr rpc.BlockNumber) *types.Header {
func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
// Pending block is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, _ := b.eth.miner.Pending()
return block.Header()
return block.Header(), nil
}
// Otherwise resolve and return the block
if blockNr == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentBlock().Header()
return b.eth.blockchain.CurrentBlock().Header(), nil
}
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr))
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil
}
func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
@ -70,16 +70,16 @@ func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumb
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
}
func (b *EthApiBackend) StateAndHeaderByNumber(blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
// Pending state is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, state := b.eth.miner.Pending()
return EthApiState{state}, block.Header(), nil
}
// Otherwise resolve the block number and return its state
header := b.HeaderByNumber(blockNr)
if header == nil {
return nil, nil, nil
header, err := b.HeaderByNumber(ctx, blockNr)
if header == nil || err != nil {
return nil, nil, err
}
stateDb, err := state.New(header.Root, b.eth.chainDb)
return EthApiState{stateDb}, header, err

View File

@ -71,6 +71,7 @@ type Config struct {
Genesis string // Genesis JSON to seed the chain database with
FastSync bool // Enables the state download based fast synchronisation algorithm
LightMode bool // Running in light client mode
NoDefSrv bool // No default LES server
LightServ int // Maximum percentage of time allowed for serving LES requests
LightPeers int // Maximum number of LES client peers
@ -112,8 +113,8 @@ type LesServer interface {
Protocols() []p2p.Protocol
}
// FullNodeService implements the Ethereum full node service.
type FullNodeService struct {
// Ethereum implements the Ethereum full node service.
type Ethereum struct {
chainConfig *core.ChainConfig
// Channel for shutting down the service
shutdownChan chan bool // Channel for shutting down the ethereum
@ -150,13 +151,13 @@ type FullNodeService struct {
netRPCService *ethapi.PublicNetAPI
}
func (s *FullNodeService) AddLesServer(ls LesServer) {
func (s *Ethereum) AddLesServer(ls LesServer) {
s.ls = ls
}
// New creates a new FullNodeService object (including the
// New creates a new Ethereum object (including the
// initialisation of the common Ethereum object)
func New(ctx *node.ServiceContext, config *Config) (*FullNodeService, error) {
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
chainDb, dappDb, err := CreateDBs(ctx, config, "chaindata")
if err != nil {
return nil, err
@ -170,7 +171,7 @@ func New(ctx *node.ServiceContext, config *Config) (*FullNodeService, error) {
return nil, err
}
eth := &FullNodeService{
eth := &Ethereum{
chainDb: chainDb,
dappDb: dappDb,
eventMux: ctx.EventMux,
@ -219,6 +220,8 @@ func New(ctx *node.ServiceContext, config *Config) (*FullNodeService, error) {
if config.ChainConfig == nil {
return nil, errors.New("missing chain config")
}
core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig)
eth.chainConfig = config.ChainConfig
eth.chainConfig.VmConfig = vm.Config{
EnableJit: config.EnableJit,
@ -317,12 +320,12 @@ func CreatePoW(config *Config) (*ethash.Ethash, error) {
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *FullNodeService) APIs() []rpc.API {
func (s *Ethereum) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.ApiBackend, &s.solcPath, &s.solc), []rpc.API{
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicFullEthereumAPI(s),
Service: NewPublicEthereumAPI(s),
Public: true,
}, {
Namespace: "eth",
@ -347,16 +350,16 @@ func (s *FullNodeService) APIs() []rpc.API {
}, {
Namespace: "admin",
Version: "1.0",
Service: NewPrivateFullAdminAPI(s),
Service: NewPrivateAdminAPI(s),
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPublicFullDebugAPI(s),
Service: NewPublicDebugAPI(s),
Public: true,
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateFullDebugAPI(s.chainConfig, s),
Service: NewPrivateDebugAPI(s.chainConfig, s),
}, {
Namespace: "net",
Version: "1.0",
@ -370,11 +373,11 @@ func (s *FullNodeService) APIs() []rpc.API {
}...)
}
func (s *FullNodeService) ResetWithGenesisBlock(gb *types.Block) {
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
s.blockchain.ResetWithGenesisBlock(gb)
}
func (s *FullNodeService) Etherbase() (eb common.Address, err error) {
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
eb = s.etherbase
if (eb == common.Address{}) {
firstAccount, err := s.AccountManager().AccountByIndex(0)
@ -387,30 +390,30 @@ func (s *FullNodeService) Etherbase() (eb common.Address, err error) {
}
// set in js console via admin interface or wrapper from cli flags
func (self *FullNodeService) SetEtherbase(etherbase common.Address) {
func (self *Ethereum) SetEtherbase(etherbase common.Address) {
self.etherbase = etherbase
self.miner.SetEtherbase(etherbase)
}
func (s *FullNodeService) StopMining() { s.miner.Stop() }
func (s *FullNodeService) IsMining() bool { return s.miner.Mining() }
func (s *FullNodeService) Miner() *miner.Miner { return s.miner }
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 }
func (s *FullNodeService) AccountManager() *accounts.Manager { return s.accountManager }
func (s *FullNodeService) BlockChain() *core.BlockChain { return s.blockchain }
func (s *FullNodeService) TxPool() *core.TxPool { return s.txPool }
func (s *FullNodeService) EventMux() *event.TypeMux { return s.eventMux }
func (s *FullNodeService) Pow() *ethash.Ethash { return s.pow }
func (s *FullNodeService) ChainDb() ethdb.Database { return s.chainDb }
func (s *FullNodeService) DappDb() ethdb.Database { return s.dappDb }
func (s *FullNodeService) IsListening() bool { return true } // Always listening
func (s *FullNodeService) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *FullNodeService) NetVersion() int { return s.netVersionId }
func (s *FullNodeService) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *Ethereum) NetVersion() int { return s.netVersionId }
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Protocols implements node.Service, returning all the currently configured
// network protocols to start.
func (s *FullNodeService) Protocols() []p2p.Protocol {
func (s *Ethereum) Protocols() []p2p.Protocol {
if s.ls == nil {
return s.protocolManager.SubProtocols
} else {
@ -419,8 +422,8 @@ func (s *FullNodeService) Protocols() []p2p.Protocol {
}
// Start implements node.Service, starting all internal goroutines needed by the
// FullNodeService protocol implementation.
func (s *FullNodeService) Start(srvr *p2p.Server) error {
// Ethereum protocol implementation.
func (s *Ethereum) Start(srvr *p2p.Server) error {
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
if s.AutoDAG {
s.StartAutoDAG()
@ -434,7 +437,7 @@ func (s *FullNodeService) Start(srvr *p2p.Server) error {
// Stop implements node.Service, terminating all internal goroutines used by the
// Ethereum protocol.
func (s *FullNodeService) Stop() error {
func (s *Ethereum) Stop() error {
if s.stopDbUpgrade != nil {
s.stopDbUpgrade()
}
@ -457,7 +460,7 @@ func (s *FullNodeService) Stop() error {
}
// This function will wait for a shutdown and resumes main thread execution
func (s *FullNodeService) WaitForShutdown() {
func (s *Ethereum) WaitForShutdown() {
<-s.shutdownChan
}
@ -470,7 +473,7 @@ func (s *FullNodeService) WaitForShutdown() {
// stop any number of times.
// For any more sophisticated pattern of DAG generation, use CLI subcommand
// makedag
func (self *FullNodeService) StartAutoDAG() {
func (self *Ethereum) StartAutoDAG() {
if self.autodagquit != nil {
return // already started
}
@ -516,7 +519,7 @@ func (self *FullNodeService) StartAutoDAG() {
}
// stopAutoDAG stops automatic DAG pregeneration by quitting the loop
func (self *FullNodeService) StopAutoDAG() {
func (self *Ethereum) StopAutoDAG() {
if self.autodagquit != nil {
close(self.autodagquit)
self.autodagquit = nil
@ -526,7 +529,7 @@ func (self *FullNodeService) StopAutoDAG() {
// HTTPClient returns the light http client used for fetching offchain docs
// (natspec, source for verification)
func (self *FullNodeService) HTTPClient() *httpclient.HTTPClient {
func (self *Ethereum) HTTPClient() *httpclient.HTTPClient {
return self.httpclient
}

View File

@ -28,7 +28,7 @@ import (
const disabledInfo = "Set GO_OPENCL and re-build to enable."
func (s *FullNodeService) StartMining(threads int, gpus string) error {
func (s *Ethereum) StartMining(threads int, gpus string) error {
eb, err := s.Etherbase()
if err != nil {
err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)

View File

@ -1874,7 +1874,7 @@ func (d *Downloader) processContent() error {
}
if err != nil {
glog.V(logger.Debug).Infof("Result #%d [%x…] processing failed: %v", results[index].Header.Number, results[index].Header.Hash().Bytes()[:4], err)
return err
return errInvalidChain
}
// Shift the results to the next batch
results = results[items:]

View File

@ -80,7 +80,8 @@ func (self *Filter) SetTopics(topics [][]common.Hash) {
// Run filters logs with the current parameters set
func (self *Filter) Find(ctx context.Context) (vm.Logs, error) {
headBlockNumber := self.apiBackend.HeaderByNumber(rpc.LatestBlockNumber).Number.Uint64()
head, _ := self.apiBackend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
headBlockNumber := head.Number.Uint64()
var beginBlockNo uint64 = uint64(self.begin)
if self.begin == -1 {
@ -132,9 +133,9 @@ func (self *Filter) Find(ctx context.Context) (vm.Logs, error) {
func (self *Filter) getLogs(ctx context.Context, start, end uint64) (logs vm.Logs, err error) {
for i := start; i <= end; i++ {
header := self.apiBackend.HeaderByNumber(rpc.BlockNumber(i))
if header == nil {
return logs, nil
header, err := self.apiBackend.HeaderByNumber(ctx, rpc.BlockNumber(i))
if header == nil || err != nil {
return logs, err
}
// Use bloom filtering to see if this block is interesting given the

View File

@ -60,7 +60,7 @@ func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error
lastPrice := self.lastPrice
self.cacheLock.RUnlock()
head := self.backend.HeaderByNumber(rpc.LatestBlockNumber)
head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
headHash := head.Hash()
if headHash == lastHead {
return lastPrice, nil

View File

@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/miner"
)
func (s *FullNodeService) StartMining(threads int, gpus string) error {
func (s *Ethereum) StartMining(threads int, gpus string) error {
eb, err := s.Etherbase()
if err != nil {
err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)

View File

@ -45,6 +45,10 @@ const (
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
)
var (
daoChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the DAO handshake challenge
)
// errIncompatibleConfig is returned if the requested protocols and configs are
// not compatible (low protocol version restrictions and high requirements).
var errIncompatibleConfig = errors.New("incompatible configuration")
@ -62,9 +66,10 @@ type ProtocolManager struct {
fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)
synced uint32 // Flag whether we're considered synchronised (enables transaction processing)
txpool txPool
blockchain *core.BlockChain
chaindb ethdb.Database
txpool txPool
blockchain *core.BlockChain
chaindb ethdb.Database
chainconfig *core.ChainConfig
downloader *downloader.Downloader
fetcher *fetcher.Fetcher
@ -99,6 +104,7 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int,
txpool: txpool,
blockchain: blockchain,
chaindb: chaindb,
chainconfig: config,
peers: newPeerSet(),
newPeerCh: make(chan *peer),
noMorePeers: make(chan struct{}),
@ -251,8 +257,8 @@ func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *p
func (pm *ProtocolManager) handle(p *peer) error {
if pm.peers.Len() >= 20 {
return p2p.DiscTooManyPeers
}
}
glog.V(logger.Debug).Infof("%v: peer connected [%s]", p, p.Name())
// Execute the Ethereum handshake
@ -282,6 +288,18 @@ func (pm *ProtocolManager) handle(p *peer) error {
// after this will be sent via broadcasts.
pm.syncTransactions(p)
// If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork
if daoBlock := pm.chainconfig.DAOForkBlock; daoBlock != nil {
// Request the peer's DAO fork header for extra-data validation
if err := p.RequestHeadersByNumber(daoBlock.Uint64(), 1, 0, false); err != nil {
return err
}
// Start a timer to disconnect if the peer doesn't reply in time
p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() {
glog.V(logger.Warn).Infof("%v: timed out DAO fork-check, dropping", p)
pm.removePeer(p.id)
})
}
// main loop. handle incoming messages.
for {
if err := pm.handleMsg(p); err != nil {
@ -485,9 +503,43 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&headers); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// If no headers were received, but we're expending a DAO fork check, maybe it's that
if len(headers) == 0 && p.forkDrop != nil {
// Possibly an empty reply to the fork header checks, sanity check TDs
verifyDAO := true
// If we already have a DAO header, we can check the peer's TD against it. If
// the peer's ahead of this, it too must have a reply to the DAO check
if daoHeader := pm.blockchain.GetHeaderByNumber(pm.chainconfig.DAOForkBlock.Uint64()); daoHeader != nil {
if p.Td().Cmp(pm.blockchain.GetTd(daoHeader.Hash(), daoHeader.Number.Uint64())) >= 0 {
verifyDAO = false
}
}
// If we're seemingly on the same chain, disable the drop timer
if verifyDAO {
glog.V(logger.Debug).Infof("%v: seems to be on the same side of the DAO fork", p)
p.forkDrop.Stop()
p.forkDrop = nil
return nil
}
}
// Filter out any explicitly requested headers, deliver the rest to the downloader
filter := len(headers) == 1
if filter {
// If it's a potential DAO fork check, validate against the rules
if p.forkDrop != nil && pm.chainconfig.DAOForkBlock.Cmp(headers[0].Number) == 0 {
// Disable the fork drop timer
p.forkDrop.Stop()
p.forkDrop = nil
// Validate the header and either drop the peer or continue
if err := core.ValidateDAOHeaderExtraData(pm.chainconfig, headers[0]); err != nil {
glog.V(logger.Debug).Infof("%v: verified to be on the other side of the DAO fork, dropping", p)
return err
}
glog.V(logger.Debug).Infof("%v: verified to be on the same side of the DAO fork", p)
}
// Irrelevant of the fork checks, send the header to the fetcher just in case
headers = pm.fetcher.FilterHeaders(headers, time.Now())
}
if len(headers) > 0 || !filter {

View File

@ -59,10 +59,12 @@ type peer struct {
*p2p.Peer
rw p2p.MsgReadWriter
version int // Protocol version negotiated
head common.Hash
td *big.Int
lock sync.RWMutex
version int // Protocol version negotiated
forkDrop *time.Timer // Timed connection dropper if forks aren't validated in time
head common.Hash
td *big.Int
lock sync.RWMutex
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
knownBlocks *set.Set // Set of block hashes known to be known by this peer

View File

@ -37,10 +37,10 @@ const (
var ProtocolName = "eth"
// Supported versions of the eth protocol (first is primary).
var ProtocolVersions = []uint{eth63, eth62, eth61}
var ProtocolVersions = []uint{eth63, eth62}
// Number of implemented message corresponding to different protocol versions.
var ProtocolLengths = []uint64{17, 8, 9}
var ProtocolLengths = []uint64{17, 8}
const (
NetworkId = 1

View File

@ -1,12 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store

View File

@ -36,7 +36,7 @@ import (
var OpenFileLimit = 64
// cacheRatio specifies how the total alloted cache is distributed between the
// cacheRatio specifies how the total allotted cache is distributed between the
// various system databases.
var cacheRatio = map[string]float64{
"dapp": 0.0,
@ -44,7 +44,7 @@ var cacheRatio = map[string]float64{
"lightchaindata": 1.0,
}
// handleRatio specifies how the total alloted file descriptors is distributed
// handleRatio specifies how the total allotted file descriptors is distributed
// between the various system databases.
var handleRatio = map[string]float64{
"dapp": 0.0,
@ -81,7 +81,7 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) {
if handles < 16 {
handles = 16
}
glog.V(logger.Info).Infof("Alloted %dMB cache and %d file handles to %s", cache, handles, file)
glog.V(logger.Info).Infof("Allotted %dMB cache and %d file handles to %s", cache, handles, file)
// Open the db and recover any potential corruptions
db, err := leveldb.OpenFile(file, &opt.Options{

44
src/vendor/github.com/ethereum/go-ethereum/flags.dif generated vendored Normal file
View File

@ -0,0 +1,44 @@
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index fc58447..e9d9ef1 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/logger"
@@ -160,7 +161,7 @@ var (
Usage: "Enable light client mode",
}
NoDefSrvFlag = cli.BoolFlag{
- Name: "nodefsrv",
+ Name: "nodefsrv",
Usage: "Don't add default LES server (only for test version)",
}
LightServFlag = cli.IntFlag{
@@ -419,6 +420,13 @@ var (
}
)
+// DebugSetup sets up the debugging parameters such that logs can be retrieved when a
+// node is started via importing go-ethereum packages, as opposed to starting via CLI
+func DebugSetup(ctx *cli.Context) error {
+ err := debug.Setup(ctx)
+ return err
+}
+
// MustMakeDataDir retrieves the currently requested data directory, terminating
// if none (or the empty string) is specified. If the node is starting a testnet,
// the a subdirectory of the specified datadir will be used.
@@ -669,7 +677,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
DataDir: MustMakeDataDir(ctx),
PrivateKey: MakeNodeKey(ctx),
Name: MakeNodeName(name, version, ctx),
- NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // light client hack
+ NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // light client hack
BootstrapNodes: MakeBootstrapNodes(ctx),
ListenAddr: MakeListenAddress(ctx),
NAT: MakeNAT(ctx),

View File

@ -0,0 +1,81 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:generate go run defaults.go default.json defs.go
package main //build !none
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
)
func fatalf(str string, v ...interface{}) {
fmt.Fprintf(os.Stderr, str, v...)
os.Exit(1)
}
type setting struct {
Value int64 `json:"v"`
Comment string `json:"d"`
}
func main() {
if len(os.Args) < 3 {
fatalf("usage %s <input> <output>\n", os.Args[0])
}
content, err := ioutil.ReadFile(os.Args[1])
if err != nil {
fatalf("error reading file %v\n", err)
}
m := make(map[string]setting)
json.Unmarshal(content, &m)
filepath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "params", os.Args[2])
output, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fatalf("error opening file for writing %v\n", err)
}
output.WriteString(`// DO NOT EDIT!!!
// AUTOGENERATED FROM generators/defaults.go
package params
import "math/big"
var (
`)
for name, setting := range m {
output.WriteString(fmt.Sprintf("%s=big.NewInt(%d) // %s\n", strings.Title(name), setting.Value, setting.Comment))
}
output.WriteString(")\n")
output.Close()
cmd := exec.Command("gofmt", "-w", filepath)
if err := cmd.Run(); err != nil {
fatalf("gofmt failed: %v\n", err)
}
}

View File

@ -84,12 +84,6 @@ func (*HandlerT) GcStats() *debug.GCStats {
return s
}
// SetLogDir overwrites the default log file utilized by glog
func (h *HandlerT) SetLogDir(logDir string) error {
glog.SetLogDir(logDir)
return nil
}
// CpuProfile turns on CPU profiling for nsec seconds and writes
// profile data to file.
func (h *HandlerT) CpuProfile(file string, nsec uint) error {

View File

@ -43,10 +43,6 @@ var (
Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")",
Value: glog.GetTraceLocation(),
}
logdirFlag = cli.StringFlag{
Name: "logdir",
Usage: "Request that glog use the specified log dir location",
}
pprofFlag = cli.BoolFlag{
Name: "pprof",
Usage: "Enable the pprof HTTP server",
@ -77,7 +73,7 @@ var (
// Flags holds all command-line flags required for debugging.
var Flags = []cli.Flag{
verbosityFlag, vmoduleFlag, logdirFlag, backtraceAtFlag,
verbosityFlag, vmoduleFlag, backtraceAtFlag,
pprofFlag, pprofPortFlag,
memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag,
}
@ -85,17 +81,9 @@ var Flags = []cli.Flag{
// Setup initializes profiling and logging based on the CLI flags.
// It should be called as early as possible in the program.
func Setup(ctx *cli.Context) error {
// logging
if logDir := ctx.GlobalString(logdirFlag.Name); logDir != "" {
if err := Handler.SetLogDir(logDir); err != nil {
return err
}
glog.SetToStderr(false)
} else {
glog.SetToStderr(true)
}
glog.CopyStandardLogTo("INFO")
glog.SetToStderr(true)
// profiling, tracing
runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name)

View File

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@ -46,6 +47,7 @@ import (
)
const defaultGas = uint64(90000)
const defaultTxQueueCap = uint8(5)
// PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone.
@ -263,8 +265,8 @@ func (s *PrivateAccountAPI) ListAccounts() []common.Address {
}
// NewAccount will create a new account and returns the address for the new account.
func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) {
acc, err := s.am.NewAccount(password, true)
func (s *PrivateAccountAPI) NewAccount(password string, w bool) (common.Address, error) {
acc, err := s.am.NewAccount(password, w)
if err == nil {
return acc.Address, nil
}
@ -374,14 +376,15 @@ func (s *PublicBlockChainAPI) subscriptionLoop() {
// BlockNumber returns the block number of the chain head.
func (s *PublicBlockChainAPI) BlockNumber() *big.Int {
return s.b.HeaderByNumber(rpc.LatestBlockNumber).Number
header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
return header.Number
}
// GetBalance returns the amount of wei for the given address in the state of the
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
// block numbers are also allowed.
func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) {
state, _, err := s.b.StateAndHeaderByNumber(blockNr)
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return nil, err
}
@ -505,7 +508,7 @@ func (s *PublicBlockChainAPI) NewBlocks(ctx context.Context, args NewBlocksArgs)
// GetCode returns the code stored at the given address in the state for the given block number.
func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) {
state, _, err := s.b.StateAndHeaderByNumber(blockNr)
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return "", err
}
@ -520,7 +523,7 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Addres
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed.
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (string, error) {
state, _, err := s.b.StateAndHeaderByNumber(blockNr)
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return "0x", err
}
@ -534,7 +537,6 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
// callmsg is the message type used for call transations.
type callmsg struct {
addr common.Address
nonce uint64
to *common.Address
gas, gasPrice *big.Int
value *big.Int
@ -544,7 +546,8 @@ type callmsg struct {
// accessor boilerplate to implement core.Message
func (m callmsg) From() (common.Address, error) { return m.addr, nil }
func (m callmsg) FromFrontier() (common.Address, error) { return m.addr, nil }
func (m callmsg) Nonce() uint64 { return m.nonce }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.to }
func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
func (m callmsg) Gas() *big.Int { return m.gas }
@ -562,7 +565,7 @@ type CallArgs struct {
}
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) {
state, header, err := s.b.StateAndHeaderByNumber(blockNr)
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return "0x", common.Big0, err
}
@ -579,15 +582,10 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
} else {
addr = args.From
}
nonce, err := state.GetNonce(ctx, addr)
if err != nil {
return "0x", common.Big0, err
}
// Assemble the CALL invocation
msg := callmsg{
addr: addr,
nonce: nonce,
to: args.To,
gas: args.Gas.BigInt(),
gasPrice: args.GasPrice.BigInt(),
@ -687,7 +685,7 @@ func FormatLogs(structLogs []vm.StructLog) []StructLogRes {
// TraceCall executes a call and returns the amount of gas, created logs and optionally returned values.
func (s *PublicBlockChainAPI) TraceCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) {
state, header, err := s.b.StateAndHeaderByNumber(blockNr)
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return nil, err
}
@ -703,15 +701,10 @@ func (s *PublicBlockChainAPI) TraceCall(ctx context.Context, args CallArgs, bloc
} else {
addr = args.From
}
nonce, err := state.GetNonce(ctx, addr)
if err != nil {
return nil, err
}
// Assemble the CALL invocation
msg := callmsg{
addr: addr,
nonce: nonce,
to: args.To,
gas: args.Gas.BigInt(),
gasPrice: args.GasPrice.BigInt(),
@ -874,6 +867,7 @@ type PublicTransactionPoolAPI struct {
b Backend
muPendingTxSubs sync.Mutex
pendingTxSubs map[string]rpc.Subscription
txQueue chan QueuedTx
}
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
@ -881,6 +875,7 @@ func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI {
api := &PublicTransactionPoolAPI{
b: b,
pendingTxSubs: make(map[string]rpc.Subscription),
txQueue: make(chan QueuedTx, defaultTxQueueCap),
}
go api.subscriptionLoop()
@ -959,7 +954,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) {
state, _, err := s.b.StateAndHeaderByNumber(blockNr)
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return nil, err
}
@ -1137,9 +1132,41 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, si
return signedTx.Hash(), nil
}
// Queued Transaction is a container that holds context and arguments enough to complete transaction.
// SendTransaction() queues transactions, to be fulfilled by SendQueuedTransaction()
type QueuedTx struct {
Hash common.Hash
Context context.Context
Args SendTxArgs
}
func (s *PublicTransactionPoolAPI) GetTransactionQueue() (<-chan QueuedTx, error) {
return s.txQueue, nil
}
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
// transaction pool.
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
queuedTx := QueuedTx{
Hash: common.Hash{},
Context: ctx,
Args: args,
}
// populate transaction hash
hw := sha3.NewKeccak256()
rlp.Encode(hw, queuedTx)
hw.Sum(queuedTx.Hash[:0])
s.txQueue <- queuedTx
return queuedTx.Hash, nil
}
func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(queuedTx QueuedTx) (common.Hash, error) {
ctx, args := queuedTx.Context, queuedTx.Args
var err error
args, err = prepareSendTxArgs(ctx, args, s.b)
if err != nil {

View File

@ -45,9 +45,9 @@ type Backend interface {
AccountManager() *accounts.Manager
// BlockChain API
SetHead(number uint64)
HeaderByNumber(blockNr rpc.BlockNumber) *types.Header
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
StateAndHeaderByNumber(blockNr rpc.BlockNumber) (State, *types.Header, error)
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (State, *types.Header, error)
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetTd(blockHash common.Hash) *big.Int

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,74 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package jsre
import (
"sort"
"strings"
"github.com/robertkrimen/otto"
)
// CompleteKeywords returns potential continuations for the given line. Since line is
// evaluated, callers need to make sure that evaluating line does not have side effects.
func (jsre *JSRE) CompleteKeywords(line string) []string {
var results []string
jsre.Do(func(vm *otto.Otto) {
results = getCompletions(vm, line)
})
return results
}
func getCompletions(vm *otto.Otto, line string) (results []string) {
parts := strings.Split(line, ".")
objRef := "this"
prefix := line
if len(parts) > 1 {
objRef = strings.Join(parts[0:len(parts)-1], ".")
prefix = parts[len(parts)-1]
}
obj, _ := vm.Object(objRef)
if obj == nil {
return nil
}
iterOwnAndConstructorKeys(vm, obj, func(k string) {
if strings.HasPrefix(k, prefix) {
if objRef == "this" {
results = append(results, k)
} else {
results = append(results, strings.Join(parts[:len(parts)-1], ".")+"."+k)
}
}
})
// Append opening parenthesis (for functions) or dot (for objects)
// if the line itself is the only completion.
if len(results) == 1 && results[0] == line {
obj, _ := vm.Object(line)
if obj != nil {
if obj.Class() == "Function" {
results[0] += "("
} else {
results[0] += "."
}
}
}
sort.Strings(results)
return results
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,329 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package jsre provides execution environment for JavaScript.
package jsre
import (
crand "crypto/rand"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"math/rand"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/robertkrimen/otto"
)
/*
JSRE is a generic JS runtime environment embedding the otto JS interpreter.
It provides some helper functions to
- load code from files
- run code snippets
- require libraries
- bind native go objects
*/
type JSRE struct {
assetPath string
output io.Writer
evalQueue chan *evalReq
stopEventLoop chan bool
closed chan struct{}
}
// jsTimer is a single timer instance with a callback function
type jsTimer struct {
timer *time.Timer
duration time.Duration
interval bool
call otto.FunctionCall
}
// evalReq is a serialized vm execution request processed by runEventLoop.
type evalReq struct {
fn func(vm *otto.Otto)
done chan bool
}
// runtime must be stopped with Stop() after use and cannot be used after stopping
func New(assetPath string, output io.Writer) *JSRE {
re := &JSRE{
assetPath: assetPath,
output: output,
closed: make(chan struct{}),
evalQueue: make(chan *evalReq),
stopEventLoop: make(chan bool),
}
go re.runEventLoop()
re.Set("loadScript", re.loadScript)
re.Set("inspect", prettyPrintJS)
return re
}
// randomSource returns a pseudo random value generator.
func randomSource() *rand.Rand {
bytes := make([]byte, 8)
seed := time.Now().UnixNano()
if _, err := crand.Read(bytes); err == nil {
seed = int64(binary.LittleEndian.Uint64(bytes))
}
src := rand.NewSource(seed)
return rand.New(src)
}
// This function runs the main event loop from a goroutine that is started
// when JSRE is created. Use Stop() before exiting to properly stop it.
// The event loop processes vm access requests from the evalQueue in a
// serialized way and calls timer callback functions at the appropriate time.
// Exported functions always access the vm through the event queue. You can
// call the functions of the otto vm directly to circumvent the queue. These
// functions should be used if and only if running a routine that was already
// called from JS through an RPC call.
func (self *JSRE) runEventLoop() {
defer close(self.closed)
vm := otto.New()
r := randomSource()
vm.SetRandomSource(r.Float64)
registry := map[*jsTimer]*jsTimer{}
ready := make(chan *jsTimer)
newTimer := func(call otto.FunctionCall, interval bool) (*jsTimer, otto.Value) {
delay, _ := call.Argument(1).ToInteger()
if 0 >= delay {
delay = 1
}
timer := &jsTimer{
duration: time.Duration(delay) * time.Millisecond,
call: call,
interval: interval,
}
registry[timer] = timer
timer.timer = time.AfterFunc(timer.duration, func() {
ready <- timer
})
value, err := call.Otto.ToValue(timer)
if err != nil {
panic(err)
}
return timer, value
}
setTimeout := func(call otto.FunctionCall) otto.Value {
_, value := newTimer(call, false)
return value
}
setInterval := func(call otto.FunctionCall) otto.Value {
_, value := newTimer(call, true)
return value
}
clearTimeout := func(call otto.FunctionCall) otto.Value {
timer, _ := call.Argument(0).Export()
if timer, ok := timer.(*jsTimer); ok {
timer.timer.Stop()
delete(registry, timer)
}
return otto.UndefinedValue()
}
vm.Set("_setTimeout", setTimeout)
vm.Set("_setInterval", setInterval)
vm.Run(`var setTimeout = function(args) {
if (arguments.length < 1) {
throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present.");
}
return _setTimeout.apply(this, arguments);
}`)
vm.Run(`var setInterval = function(args) {
if (arguments.length < 1) {
throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present.");
}
return _setInterval.apply(this, arguments);
}`)
vm.Set("clearTimeout", clearTimeout)
vm.Set("clearInterval", clearTimeout)
var waitForCallbacks bool
loop:
for {
select {
case timer := <-ready:
// execute callback, remove/reschedule the timer
var arguments []interface{}
if len(timer.call.ArgumentList) > 2 {
tmp := timer.call.ArgumentList[2:]
arguments = make([]interface{}, 2+len(tmp))
for i, value := range tmp {
arguments[i+2] = value
}
} else {
arguments = make([]interface{}, 1)
}
arguments[0] = timer.call.ArgumentList[0]
_, err := vm.Call(`Function.call.call`, nil, arguments...)
if err != nil {
fmt.Println("js error:", err, arguments)
}
_, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it
if timer.interval && inreg {
timer.timer.Reset(timer.duration)
} else {
delete(registry, timer)
if waitForCallbacks && (len(registry) == 0) {
break loop
}
}
case req := <-self.evalQueue:
// run the code, send the result back
req.fn(vm)
close(req.done)
if waitForCallbacks && (len(registry) == 0) {
break loop
}
case waitForCallbacks = <-self.stopEventLoop:
if !waitForCallbacks || (len(registry) == 0) {
break loop
}
}
}
for _, timer := range registry {
timer.timer.Stop()
delete(registry, timer)
}
}
// Do executes the given function on the JS event loop.
func (self *JSRE) Do(fn func(*otto.Otto)) {
done := make(chan bool)
req := &evalReq{fn, done}
self.evalQueue <- req
<-done
}
// stops the event loop before exit, optionally waits for all timers to expire
func (self *JSRE) Stop(waitForCallbacks bool) {
select {
case <-self.closed:
case self.stopEventLoop <- waitForCallbacks:
<-self.closed
}
}
// Exec(file) loads and runs the contents of a file
// if a relative path is given, the jsre's assetPath is used
func (self *JSRE) Exec(file string) error {
code, err := ioutil.ReadFile(common.AbsolutePath(self.assetPath, file))
if err != nil {
return err
}
var script *otto.Script
self.Do(func(vm *otto.Otto) {
script, err = vm.Compile(file, code)
if err != nil {
return
}
_, err = vm.Run(script)
})
return err
}
// Bind assigns value v to a variable in the JS environment
// This method is deprecated, use Set.
func (self *JSRE) Bind(name string, v interface{}) error {
return self.Set(name, v)
}
// Run runs a piece of JS code.
func (self *JSRE) Run(code string) (v otto.Value, err error) {
self.Do(func(vm *otto.Otto) { v, err = vm.Run(code) })
return v, err
}
// Get returns the value of a variable in the JS environment.
func (self *JSRE) Get(ns string) (v otto.Value, err error) {
self.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
return v, err
}
// Set assigns value v to a variable in the JS environment.
func (self *JSRE) Set(ns string, v interface{}) (err error) {
self.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
return err
}
// loadScript executes a JS script from inside the currently executing JS code.
func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
file, err := call.Argument(0).ToString()
if err != nil {
// TODO: throw exception
return otto.FalseValue()
}
file = common.AbsolutePath(self.assetPath, file)
source, err := ioutil.ReadFile(file)
if err != nil {
// TODO: throw exception
return otto.FalseValue()
}
if _, err := compileAndRun(call.Otto, file, source); err != nil {
// TODO: throw exception
fmt.Println("err:", err)
return otto.FalseValue()
}
// TODO: return evaluation result
return otto.TrueValue()
}
// Evaluate executes code and pretty prints the result to the specified output
// stream.
func (self *JSRE) Evaluate(code string, w io.Writer) error {
var fail error
self.Do(func(vm *otto.Otto) {
val, err := vm.Run(code)
if err != nil {
prettyError(vm, err, w)
} else {
prettyPrint(vm, val, w)
}
fmt.Fprintln(w)
})
return fail
}
// Compile compiles and then runs a piece of JS code.
func (self *JSRE) Compile(filename string, src interface{}) (err error) {
self.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
return err
}
func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) {
script, err := vm.Compile(filename, src)
if err != nil {
return otto.Value{}, err
}
return vm.Run(script)
}

View File

@ -0,0 +1,280 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package jsre
import (
"fmt"
"io"
"sort"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/robertkrimen/otto"
)
const (
maxPrettyPrintLevel = 3
indentString = " "
)
var (
FunctionColor = color.New(color.FgMagenta).SprintfFunc()
SpecialColor = color.New(color.Bold).SprintfFunc()
NumberColor = color.New(color.FgRed).SprintfFunc()
StringColor = color.New(color.FgGreen).SprintfFunc()
ErrorColor = color.New(color.FgHiRed).SprintfFunc()
)
// these fields are hidden when printing objects.
var boringKeys = map[string]bool{
"valueOf": true,
"toString": true,
"toLocaleString": true,
"hasOwnProperty": true,
"isPrototypeOf": true,
"propertyIsEnumerable": true,
"constructor": true,
}
// prettyPrint writes value to standard output.
func prettyPrint(vm *otto.Otto, value otto.Value, w io.Writer) {
ppctx{vm: vm, w: w}.printValue(value, 0, false)
}
// prettyError writes err to standard output.
func prettyError(vm *otto.Otto, err error, w io.Writer) {
failure := err.Error()
if ottoErr, ok := err.(*otto.Error); ok {
failure = ottoErr.String()
}
fmt.Fprint(w, ErrorColor("%s", failure))
}
// jsErrorString adds a backtrace to errors generated by otto.
func jsErrorString(err error) string {
if ottoErr, ok := err.(*otto.Error); ok {
return ottoErr.String()
}
return err.Error()
}
func prettyPrintJS(call otto.FunctionCall, w io.Writer) otto.Value {
for _, v := range call.ArgumentList {
prettyPrint(call.Otto, v, w)
fmt.Fprintln(w)
}
return otto.UndefinedValue()
}
type ppctx struct {
vm *otto.Otto
w io.Writer
}
func (ctx ppctx) indent(level int) string {
return strings.Repeat(indentString, level)
}
func (ctx ppctx) printValue(v otto.Value, level int, inArray bool) {
switch {
case v.IsObject():
ctx.printObject(v.Object(), level, inArray)
case v.IsNull():
fmt.Fprint(ctx.w, SpecialColor("null"))
case v.IsUndefined():
fmt.Fprint(ctx.w, SpecialColor("undefined"))
case v.IsString():
s, _ := v.ToString()
fmt.Fprint(ctx.w, StringColor("%q", s))
case v.IsBoolean():
b, _ := v.ToBoolean()
fmt.Fprint(ctx.w, SpecialColor("%t", b))
case v.IsNaN():
fmt.Fprint(ctx.w, NumberColor("NaN"))
case v.IsNumber():
s, _ := v.ToString()
fmt.Fprint(ctx.w, NumberColor("%s", s))
default:
fmt.Fprint(ctx.w, "<unprintable>")
}
}
func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) {
switch obj.Class() {
case "Array":
lv, _ := obj.Get("length")
len, _ := lv.ToInteger()
if len == 0 {
fmt.Fprintf(ctx.w, "[]")
return
}
if level > maxPrettyPrintLevel {
fmt.Fprint(ctx.w, "[...]")
return
}
fmt.Fprint(ctx.w, "[")
for i := int64(0); i < len; i++ {
el, err := obj.Get(strconv.FormatInt(i, 10))
if err == nil {
ctx.printValue(el, level+1, true)
}
if i < len-1 {
fmt.Fprintf(ctx.w, ", ")
}
}
fmt.Fprint(ctx.w, "]")
case "Object":
// Print values from bignumber.js as regular numbers.
if ctx.isBigNumber(obj) {
fmt.Fprint(ctx.w, NumberColor("%s", toString(obj)))
return
}
// Otherwise, print all fields indented, but stop if we're too deep.
keys := ctx.fields(obj)
if len(keys) == 0 {
fmt.Fprint(ctx.w, "{}")
return
}
if level > maxPrettyPrintLevel {
fmt.Fprint(ctx.w, "{...}")
return
}
fmt.Fprintln(ctx.w, "{")
for i, k := range keys {
v, _ := obj.Get(k)
fmt.Fprintf(ctx.w, "%s%s: ", ctx.indent(level+1), k)
ctx.printValue(v, level+1, false)
if i < len(keys)-1 {
fmt.Fprintf(ctx.w, ",")
}
fmt.Fprintln(ctx.w)
}
if inArray {
level--
}
fmt.Fprintf(ctx.w, "%s}", ctx.indent(level))
case "Function":
// Use toString() to display the argument list if possible.
if robj, err := obj.Call("toString"); err != nil {
fmt.Fprint(ctx.w, FunctionColor("function()"))
} else {
desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n")
desc = strings.Replace(desc, " (", "(", 1)
fmt.Fprint(ctx.w, FunctionColor("%s", desc))
}
case "RegExp":
fmt.Fprint(ctx.w, StringColor("%s", toString(obj)))
default:
if v, _ := obj.Get("toString"); v.IsFunction() && level <= maxPrettyPrintLevel {
s, _ := obj.Call("toString")
fmt.Fprintf(ctx.w, "<%s %s>", obj.Class(), s.String())
} else {
fmt.Fprintf(ctx.w, "<%s>", obj.Class())
}
}
}
func (ctx ppctx) fields(obj *otto.Object) []string {
var (
vals, methods []string
seen = make(map[string]bool)
)
add := func(k string) {
if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") {
return
}
seen[k] = true
if v, _ := obj.Get(k); v.IsFunction() {
methods = append(methods, k)
} else {
vals = append(vals, k)
}
}
iterOwnAndConstructorKeys(ctx.vm, obj, add)
sort.Strings(vals)
sort.Strings(methods)
return append(vals, methods...)
}
func iterOwnAndConstructorKeys(vm *otto.Otto, obj *otto.Object, f func(string)) {
seen := make(map[string]bool)
iterOwnKeys(vm, obj, func(prop string) {
seen[prop] = true
f(prop)
})
if cp := constructorPrototype(obj); cp != nil {
iterOwnKeys(vm, cp, func(prop string) {
if !seen[prop] {
f(prop)
}
})
}
}
func iterOwnKeys(vm *otto.Otto, obj *otto.Object, f func(string)) {
Object, _ := vm.Object("Object")
rv, _ := Object.Call("getOwnPropertyNames", obj.Value())
gv, _ := rv.Export()
switch gv := gv.(type) {
case []interface{}:
for _, v := range gv {
f(v.(string))
}
case []string:
for _, v := range gv {
f(v)
}
default:
panic(fmt.Errorf("Object.getOwnPropertyNames returned unexpected type %T", gv))
}
}
func (ctx ppctx) isBigNumber(v *otto.Object) bool {
// Handle numbers with custom constructor.
if v, _ := v.Get("constructor"); v.Object() != nil {
if strings.HasPrefix(toString(v.Object()), "function BigNumber") {
return true
}
}
// Handle default constructor.
BigNumber, _ := ctx.vm.Object("BigNumber.prototype")
if BigNumber == nil {
return false
}
bv, _ := BigNumber.Call("isPrototypeOf", v)
b, _ := bv.ToBoolean()
return b
}
func toString(obj *otto.Object) string {
s, _ := obj.Call("toString")
return s.String()
}
func constructorPrototype(obj *otto.Object) *otto.Object {
if v, _ := obj.Get("constructor"); v.Object() != nil {
if v, _ = v.Object().Get("prototype"); v.Object() != nil {
return v.Object()
}
}
return nil
}

View File

@ -0,0 +1,484 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// package web3ext contains geth specific web3.js extensions.
package web3ext
var Modules = map[string]string{
"admin": Admin_JS,
"debug": Debug_JS,
"eth": Eth_JS,
"miner": Miner_JS,
"net": Net_JS,
"personal": Personal_JS,
"rpc": RPC_JS,
"shh": Shh_JS,
"txpool": TxPool_JS,
}
const Admin_JS = `
web3._extend({
property: 'admin',
methods:
[
new web3._extend.Method({
name: 'addPeer',
call: 'admin_addPeer',
params: 1
}),
new web3._extend.Method({
name: 'exportChain',
call: 'admin_exportChain',
params: 1,
inputFormatter: [null]
}),
new web3._extend.Method({
name: 'importChain',
call: 'admin_importChain',
params: 1
}),
new web3._extend.Method({
name: 'sleepBlocks',
call: 'admin_sleepBlocks',
params: 2
}),
new web3._extend.Method({
name: 'setSolc',
call: 'admin_setSolc',
params: 1
}),
new web3._extend.Method({
name: 'startRPC',
call: 'admin_startRPC',
params: 4,
inputFormatter: [null, null, null, null]
}),
new web3._extend.Method({
name: 'stopRPC',
call: 'admin_stopRPC'
}),
new web3._extend.Method({
name: 'startWS',
call: 'admin_startWS',
params: 4,
inputFormatter: [null, null, null, null]
}),
new web3._extend.Method({
name: 'stopWS',
call: 'admin_stopWS'
}),
new web3._extend.Method({
name: 'setGlobalRegistrar',
call: 'admin_setGlobalRegistrar',
params: 2
}),
new web3._extend.Method({
name: 'setHashReg',
call: 'admin_setHashReg',
params: 2
}),
new web3._extend.Method({
name: 'setUrlHint',
call: 'admin_setUrlHint',
params: 2
}),
new web3._extend.Method({
name: 'saveInfo',
call: 'admin_saveInfo',
params: 2
}),
new web3._extend.Method({
name: 'register',
call: 'admin_register',
params: 3
}),
new web3._extend.Method({
name: 'registerUrl',
call: 'admin_registerUrl',
params: 3
}),
new web3._extend.Method({
name: 'httpGet',
call: 'admin_httpGet',
params: 2
})
],
properties:
[
new web3._extend.Property({
name: 'nodeInfo',
getter: 'admin_nodeInfo'
}),
new web3._extend.Property({
name: 'peers',
getter: 'admin_peers'
}),
new web3._extend.Property({
name: 'datadir',
getter: 'admin_datadir'
})
]
});
`
const Debug_JS = `
web3._extend({
property: 'debug',
methods:
[
new web3._extend.Method({
name: 'printBlock',
call: 'debug_printBlock',
params: 1
}),
new web3._extend.Method({
name: 'getBlockRlp',
call: 'debug_getBlockRlp',
params: 1
}),
new web3._extend.Method({
name: 'setHead',
call: 'debug_setHead',
params: 1
}),
new web3._extend.Method({
name: 'traceBlock',
call: 'debug_traceBlock',
params: 1
}),
new web3._extend.Method({
name: 'traceBlockByFile',
call: 'debug_traceBlockByFile',
params: 1
}),
new web3._extend.Method({
name: 'traceBlockByNumber',
call: 'debug_traceBlockByNumber',
params: 1
}),
new web3._extend.Method({
name: 'traceBlockByHash',
call: 'debug_traceBlockByHash',
params: 1
}),
new web3._extend.Method({
name: 'seedHash',
call: 'debug_seedHash',
params: 1
}),
new web3._extend.Method({
name: 'dumpBlock',
call: 'debug_dumpBlock',
params: 1
}),
new web3._extend.Method({
name: 'chaindbProperty',
call: 'debug_chaindbProperty',
params: 1,
outputFormatter: console.log
}),
new web3._extend.Method({
name: 'metrics',
call: 'debug_metrics',
params: 1
}),
new web3._extend.Method({
name: 'verbosity',
call: 'debug_verbosity',
params: 1
}),
new web3._extend.Method({
name: 'vmodule',
call: 'debug_vmodule',
params: 1
}),
new web3._extend.Method({
name: 'backtraceAt',
call: 'debug_backtraceAt',
params: 1,
}),
new web3._extend.Method({
name: 'stacks',
call: 'debug_stacks',
params: 0,
outputFormatter: console.log
}),
new web3._extend.Method({
name: 'memStats',
call: 'debug_memStats',
params: 0,
}),
new web3._extend.Method({
name: 'gcStats',
call: 'debug_gcStats',
params: 0,
}),
new web3._extend.Method({
name: 'cpuProfile',
call: 'debug_cpuProfile',
params: 2
}),
new web3._extend.Method({
name: 'startCPUProfile',
call: 'debug_startCPUProfile',
params: 1
}),
new web3._extend.Method({
name: 'stopCPUProfile',
call: 'debug_stopCPUProfile',
params: 0
}),
new web3._extend.Method({
name: 'goTrace',
call: 'debug_goTrace',
params: 2
}),
new web3._extend.Method({
name: 'startGoTrace',
call: 'debug_startGoTrace',
params: 1
}),
new web3._extend.Method({
name: 'stopGoTrace',
call: 'debug_stopGoTrace',
params: 0
}),
new web3._extend.Method({
name: 'blockProfile',
call: 'debug_blockProfile',
params: 2
}),
new web3._extend.Method({
name: 'setBlockProfileRate',
call: 'debug_setBlockProfileRate',
params: 1
}),
new web3._extend.Method({
name: 'writeBlockProfile',
call: 'debug_writeBlockProfile',
params: 1
}),
new web3._extend.Method({
name: 'writeMemProfile',
call: 'debug_writeMemProfile',
params: 1
}),
new web3._extend.Method({
name: 'traceTransaction',
call: 'debug_traceTransaction',
params: 1
})
],
properties: []
});
`
const Eth_JS = `
web3._extend({
property: 'eth',
methods:
[
new web3._extend.Method({
name: 'sign',
call: 'eth_sign',
params: 2,
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null]
}),
new web3._extend.Method({
name: 'resend',
call: 'eth_resend',
params: 3,
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal]
}),
new web3._extend.Method({
name: 'getNatSpec',
call: 'eth_getNatSpec',
params: 1,
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
}),
new web3._extend.Method({
name: 'signTransaction',
call: 'eth_signTransaction',
params: 1,
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
}),
new web3._extend.Method({
name: 'submitTransaction',
call: 'eth_submitTransaction',
params: 1,
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
})
],
properties:
[
new web3._extend.Property({
name: 'pendingTransactions',
getter: 'eth_pendingTransactions',
outputFormatter: function(txs) {
var formatted = [];
for (var i = 0; i < txs.length; i++) {
formatted.push(web3._extend.formatters.outputTransactionFormatter(txs[i]));
formatted[i].blockHash = null;
}
return formatted;
}
})
]
});
`
const Miner_JS = `
web3._extend({
property: 'miner',
methods:
[
new web3._extend.Method({
name: 'start',
call: 'miner_start',
params: 1,
inputFormatter: [null]
}),
new web3._extend.Method({
name: 'stop',
call: 'miner_stop'
}),
new web3._extend.Method({
name: 'setEtherbase',
call: 'miner_setEtherbase',
params: 1,
inputFormatter: [web3._extend.formatters.inputAddressFormatter]
}),
new web3._extend.Method({
name: 'setExtra',
call: 'miner_setExtra',
params: 1
}),
new web3._extend.Method({
name: 'setGasPrice',
call: 'miner_setGasPrice',
params: 1,
inputFormatter: [web3._extend.utils.fromDecimal]
}),
new web3._extend.Method({
name: 'startAutoDAG',
call: 'miner_startAutoDAG',
params: 0
}),
new web3._extend.Method({
name: 'stopAutoDAG',
call: 'miner_stopAutoDAG',
params: 0
}),
new web3._extend.Method({
name: 'makeDAG',
call: 'miner_makeDAG',
params: 1,
inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter]
})
],
properties: []
});
`
const Net_JS = `
web3._extend({
property: 'net',
methods: [],
properties:
[
new web3._extend.Property({
name: 'version',
getter: 'net_version'
})
]
});
`
const Personal_JS = `
web3._extend({
property: 'personal',
methods:
[
new web3._extend.Method({
name: 'importRawKey',
call: 'personal_importRawKey',
params: 2
}),
new web3._extend.Method({
name: 'signAndSendTransaction',
call: 'personal_signAndSendTransaction',
params: 2,
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null]
})
]
});
`
const RPC_JS = `
web3._extend({
property: 'rpc',
methods: [],
properties:
[
new web3._extend.Property({
name: 'modules',
getter: 'rpc_modules'
})
]
});
`
const Shh_JS = `
web3._extend({
property: 'shh',
methods: [],
properties:
[
new web3._extend.Property({
name: 'version',
getter: 'shh_version',
outputFormatter: web3._extend.utils.toDecimal
})
]
});
`
const TxPool_JS = `
web3._extend({
property: 'txpool',
methods: [],
properties:
[
new web3._extend.Property({
name: 'content',
getter: 'txpool_content'
}),
new web3._extend.Property({
name: 'inspect',
getter: 'txpool_inspect'
}),
new web3._extend.Property({
name: 'status',
getter: 'txpool_status',
outputFormatter: function(status) {
status.pending = web3._extend.utils.toDecimal(status.pending);
status.queued = web3._extend.utils.toDecimal(status.queued);
return status;
}
})
]
});
`

View File

@ -26,43 +26,43 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/light"
rpc "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
)
type LesApiBackend struct {
eth *LightNodeService
gpo *gasprice.LightPriceOracle
eth *LightEthereum
gpo *gasprice.LightPriceOracle
}
func (b *LesApiBackend) SetHead(number uint64) {
b.eth.blockchain.SetHead(number)
}
func (b *LesApiBackend) HeaderByNumber(blockNr rpc.BlockNumber) *types.Header {
func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber {
return b.eth.blockchain.CurrentHeader()
return b.eth.blockchain.CurrentHeader(), nil
}
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr))
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr))
}
func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
header := b.HeaderByNumber(blockNr)
if header == nil {
return nil, nil
header, err := b.HeaderByNumber(ctx, blockNr)
if header == nil || err != nil {
return nil, err
}
return b.GetBlock(ctx, header.Hash())
}
func (b *LesApiBackend) StateAndHeaderByNumber(blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
header := b.HeaderByNumber(blockNr)
if header == nil {
return nil, nil, nil
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
header, err := b.HeaderByNumber(ctx, blockNr)
if header == nil || err != nil {
return nil, nil, err
}
return light.NewLightState(light.StateTrieID(header), b.eth.odr), header, nil
}

View File

@ -33,16 +33,16 @@ import (
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
rpc "github.com/ethereum/go-ethereum/rpc"
)
type LightNodeService struct {
type LightEthereum struct {
odr *LesOdr
relay *LesTxRelay
chainConfig *core.ChainConfig
@ -62,8 +62,8 @@ type LightNodeService struct {
pow *ethash.Ethash
httpclient *httpclient.HTTPClient
accountManager *accounts.Manager
solcPath string
solc *compiler.Solidity
solcPath string
solc *compiler.Solidity
NatSpec bool
PowTest bool
@ -71,7 +71,7 @@ type LightNodeService struct {
netRPCService *ethapi.PublicNetAPI
}
func New(ctx *node.ServiceContext, config *eth.Config) (*LightNodeService, error) {
func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
chainDb, dappDb, err := eth.CreateDBs(ctx, config, "lightchaindata")
if err != nil {
return nil, err
@ -86,7 +86,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightNodeService, error
odr := NewLesOdr(chainDb)
relay := NewLesTxRelay()
eth := &LightNodeService{
eth := &LightEthereum{
odr: odr,
relay: relay,
chainDb: chainDb,
@ -130,7 +130,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightNodeService, error
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *LightNodeService) APIs() []rpc.API {
func (s *LightEthereum) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.ApiBackend, &s.solcPath, &s.solc), []rpc.API{
{
Namespace: "eth",
@ -151,24 +151,24 @@ func (s *LightNodeService) APIs() []rpc.API {
}...)
}
func (s *LightNodeService) ResetWithGenesisBlock(gb *types.Block) {
func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) {
s.blockchain.ResetWithGenesisBlock(gb)
}
func (s *LightNodeService) BlockChain() *light.LightChain { return s.blockchain }
func (s *LightNodeService) TxPool() *light.TxPool { return s.txPool }
func (s *LightNodeService) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *LightNodeService) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain }
func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool }
func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Protocols implements node.Service, returning all the currently configured
// network protocols to start.
func (s *LightNodeService) Protocols() []p2p.Protocol {
func (s *LightEthereum) Protocols() []p2p.Protocol {
return s.protocolManager.SubProtocols
}
// Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation.
func (s *LightNodeService) Start(srvr *p2p.Server) error {
func (s *LightEthereum) Start(srvr *p2p.Server) error {
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId)
s.protocolManager.Start()
return nil
@ -176,7 +176,7 @@ func (s *LightNodeService) Start(srvr *p2p.Server) error {
// Stop implements node.Service, terminating all internal goroutines used by the
// Ethereum protocol.
func (s *LightNodeService) Stop() error {
func (s *LightEthereum) Stop() error {
s.odr.Stop()
s.blockchain.Stop()
s.protocolManager.Stop()

View File

@ -18,40 +18,128 @@
package les
import (
"fmt"
//"fmt"
"math/big"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
)
type lightFetcher struct{
pm *ProtocolManager
odr *LesOdr
chain BlockChain
reqMu sync.RWMutex
requested map[uint64]chan *types.Header
syncPoolMu sync.Mutex
syncPool map[*peer]struct{}
syncPoolNotify chan struct{}
syncPoolNotified uint32
}
type lightFetcher struct {
pm *ProtocolManager
odr *LesOdr
chain BlockChain
headAnnouncedMu sync.Mutex
headAnnouncedBy map[common.Hash][]*peer
currentTd *big.Int
deliverChn chan fetchResponse
reqMu sync.RWMutex
requested map[uint64]fetchRequest
timeoutChn chan uint64
notifyChn chan bool // true if initiated from outside
syncing bool
syncDone chan struct{}
}
type fetchRequest struct {
hash common.Hash
amount uint64
peer *peer
}
type fetchResponse struct {
reqID uint64
headers []*types.Header
}
func newLightFetcher(pm *ProtocolManager) *lightFetcher {
f := &lightFetcher{
pm: pm,
chain: pm.blockchain,
odr: pm.odr,
requested: make(map[uint64]chan *types.Header),
syncPool: make(map[*peer]struct{}),
syncPoolNotify: make(chan struct{}),
pm: pm,
chain: pm.blockchain,
odr: pm.odr,
headAnnouncedBy: make(map[common.Hash][]*peer),
deliverChn: make(chan fetchResponse, 100),
requested: make(map[uint64]fetchRequest),
timeoutChn: make(chan uint64),
notifyChn: make(chan bool, 100),
syncDone: make(chan struct{}),
currentTd: big.NewInt(0),
}
go f.syncLoop()
return f
}
func (f *lightFetcher) notify(p *peer, head *newBlockHashData) {
var headHash common.Hash
if head == nil {
// initial notify
headHash = p.Head()
} else {
if !p.addNotify(head) {
//fmt.Println("addNotify fail")
f.pm.removePeer(p.id)
}
headHash = head.Hash
}
f.headAnnouncedMu.Lock()
f.headAnnouncedBy[headHash] = append(f.headAnnouncedBy[headHash], p)
f.headAnnouncedMu.Unlock()
f.notifyChn <- true
}
func (f *lightFetcher) gotHeader(header *types.Header) {
f.headAnnouncedMu.Lock()
defer f.headAnnouncedMu.Unlock()
hash := header.Hash()
peerList := f.headAnnouncedBy[hash]
if peerList == nil {
return
}
number := header.GetNumberU64()
td := core.GetTd(f.pm.chainDb, hash, number)
for _, peer := range peerList {
peer.lock.Lock()
ok := peer.gotHeader(hash, number, td)
peer.lock.Unlock()
if !ok {
//fmt.Println("gotHeader fail")
f.pm.removePeer(peer.id)
}
}
delete(f.headAnnouncedBy, hash)
}
func (f *lightFetcher) nextRequest() (*peer, *newBlockHashData) {
var bestPeer *peer
bestTd := f.currentTd
for _, peer := range f.pm.peers.AllPeers() {
peer.lock.RLock()
if !peer.headInfo.requested && (peer.headInfo.Td.Cmp(bestTd) > 0 ||
(bestPeer != nil && peer.headInfo.Td.Cmp(bestTd) == 0 && peer.headInfo.haveHeaders > bestPeer.headInfo.haveHeaders)) {
bestPeer = peer
bestTd = peer.headInfo.Td
}
peer.lock.RUnlock()
}
if bestPeer == nil {
return nil, nil
}
bestPeer.lock.Lock()
res := bestPeer.headInfo
res.requested = true
bestPeer.lock.Unlock()
return bestPeer, res
}
func (f *lightFetcher) deliverHeaders(reqID uint64, headers []*types.Header) {
f.deliverChn <- fetchResponse{reqID: reqID, headers: headers}
}
func (f *lightFetcher) requestedID(reqID uint64) bool {
f.reqMu.RLock()
_, ok := f.requested[reqID]
@ -59,166 +147,137 @@ func (f *lightFetcher) requestedID(reqID uint64) bool {
return ok
}
func (f *lightFetcher) deliverHeaders(reqID uint64, headers []*types.Header) {
f.reqMu.Lock()
chn := f.requested[reqID]
if len(headers) == 1 {
chn <- headers[0]
} else {
chn <- nil
}
close(chn)
delete(f.requested, reqID)
f.reqMu.Unlock()
}
func (f *lightFetcher) notify(p *peer, block blockInfo) {
p.lock.Lock()
if block.Td.Cmp(p.headInfo.Td) <= 0 {
p.lock.Unlock()
func (f *lightFetcher) request(p *peer, block *newBlockHashData) {
//fmt.Println("request", block.Number, block.haveHeaders)
amount := block.Number-block.haveHeaders
if amount == 0 {
return
}
p.headInfo = block
p.lock.Unlock()
head := f.pm.blockchain.CurrentHeader()
currentTd := core.GetTd(f.pm.chainDb, head.Hash(), head.Number.Uint64())
if block.Td.Cmp(currentTd) > 0 {
f.syncPoolMu.Lock()
f.syncPool[p] = struct{}{}
f.syncPoolMu.Unlock()
if atomic.SwapUint32(&f.syncPoolNotified, 1) == 0 {
f.syncPoolNotify <- struct{}{}
}
if amount > 100 {
f.syncing = true
go func() {
//fmt.Println("f.pm.synchronise(p)")
f.pm.synchronise(p)
//fmt.Println("sync done")
f.syncDone <- struct{}{}
}()
return
}
reqID := f.odr.getNextReqID()
f.reqMu.Lock()
f.requested[reqID] = fetchRequest{hash: block.Hash, amount: amount, peer: p}
f.reqMu.Unlock()
cost := p.GetRequestCost(GetBlockHeadersMsg, int(amount))
p.fcServer.SendRequest(reqID, cost)
go p.RequestHeadersByHash(reqID, cost, block.Hash, int(amount), 0, true)
go func() {
time.Sleep(hardRequestTimeout)
f.timeoutChn <- reqID
}()
}
func (f *lightFetcher) fetchBestFromPool() *peer {
head := f.pm.blockchain.CurrentHeader()
currentTd := core.GetTd(f.pm.chainDb, head.Hash(), head.Number.Uint64())
func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) bool {
if uint64(len(resp.headers)) != req.amount || resp.headers[0].Hash() != req.hash {
return false
}
headers := make([]*types.Header, req.amount)
for i, header := range resp.headers {
headers[int(req.amount)-1-i] = header
}
if _, err := f.chain.InsertHeaderChain(headers, 1); err != nil {
return false
}
for _, header := range headers {
td := core.GetTd(f.pm.chainDb, header.Hash(), header.GetNumberU64())
if td == nil {
return false
}
if td.Cmp(f.currentTd) > 0 {
f.currentTd = td
}
f.gotHeader(header)
}
return true
}
f.syncPoolMu.Lock()
var best *peer
for p, _ := range f.syncPool {
td := p.Td()
if td.Cmp(currentTd) <= 0 {
delete(f.syncPool, p)
} else {
if best == nil || td.Cmp(best.Td()) > 0 {
best = p
func (f *lightFetcher) checkSyncedHeaders() {
//fmt.Println("checkSyncedHeaders()")
for _, peer := range f.pm.peers.AllPeers() {
peer.lock.Lock()
h := peer.firstHeadInfo
remove := false
loop:
for h != nil {
if td := core.GetTd(f.pm.chainDb, h.Hash, h.Number); td != nil {
//fmt.Println(" found", h.Number)
ok := peer.gotHeader(h.Hash, h.Number, td)
if !ok {
remove = true
break loop
}
if td.Cmp(f.currentTd) > 0 {
f.currentTd = td
}
}
h = h.next
}
peer.lock.Unlock()
if remove {
//fmt.Println("checkSync fail")
f.pm.removePeer(peer.id)
}
}
if best != nil {
delete(f.syncPool, best)
}
f.syncPoolMu.Unlock()
return best
}
func (f *lightFetcher) syncLoop() {
f.pm.wg.Add(1)
defer f.pm.wg.Done()
srtoNotify := false
for {
select {
select {
case <-f.pm.quitSync:
return
case <-f.syncPoolNotify:
atomic.StoreUint32(&f.syncPoolNotified, 0)
chn := f.pm.getSyncLock(false)
if chn != nil {
if atomic.SwapUint32(&f.syncPoolNotified, 1) == 0 {
case ext := <-f.notifyChn:
//fmt.Println("<-f.notifyChn", f.syncing, ext, srtoNotify)
s := srtoNotify
srtoNotify = false
if !f.syncing && !(ext && s) {
if p, r := f.nextRequest(); r != nil {
srtoNotify = true
go func() {
<-chn
f.syncPoolNotify <- struct{}{}
time.Sleep(softRequestTimeout)
f.notifyChn <- false
}()
}
} else {
if p := f.fetchBestFromPool(); p != nil {
go f.syncWithPeer(p)
if atomic.SwapUint32(&f.syncPoolNotified, 1) == 0 {
go func() {
time.Sleep(softRequestTimeout)
f.syncPoolNotify <- struct{}{}
}()
}
f.request(p, r)
}
}
}
}
}
func (f *lightFetcher) syncWithPeer(p *peer) bool {
f.pm.wg.Add(1)
defer f.pm.wg.Done()
headNum := f.chain.CurrentHeader().Number.Uint64()
peerHead := p.headBlockInfo()
if !f.pm.needToSync(peerHead) {
return true
}
if peerHead.Number <= headNum+1 {
var header *types.Header
reqID, chn := f.request(p, peerHead)
select {
case header = <-chn:
if header == nil || header.Hash() != peerHead.Hash ||
header.Number.Uint64() != peerHead.Number {
// missing or wrong header returned
fmt.Println("removePeer 1")
f.pm.removePeer(p.id)
return false
}
case <-time.After(hardRequestTimeout):
if !disableClientRemovePeer {
fmt.Println("removePeer 2")
f.pm.removePeer(p.id)
}
case reqID := <-f.timeoutChn:
f.reqMu.Lock()
close(f.requested[reqID])
delete(f.requested, reqID)
f.reqMu.Unlock()
return false
}
// got the header, try to insert
f.chain.InsertHeaderChain([]*types.Header{header}, 1)
defer func() {
// check header td at the end of syncing, drop peer if it was fake
headerTd := core.GetTd(f.pm.chainDb, header.Hash(), header.Number.Uint64())
if headerTd != nil && headerTd.Cmp(peerHead.Td) != 0 {
fmt.Println("removePeer 3")
f.pm.removePeer(p.id)
req, ok := f.requested[reqID]
if ok {
delete(f.requested, reqID)
}
}()
if !f.pm.needToSync(peerHead) {
return true
f.reqMu.Unlock()
if ok {
//fmt.Println("hard timeout")
f.pm.removePeer(req.peer.id)
}
case resp := <-f.deliverChn:
//fmt.Println("<-f.deliverChn", f.syncing)
f.reqMu.Lock()
req, ok := f.requested[resp.reqID]
delete(f.requested, resp.reqID)
f.reqMu.Unlock()
if !ok || !(f.syncing || f.processResponse(req, resp)) {
//fmt.Println("processResponse fail")
f.pm.removePeer(req.peer.id)
}
case <-f.syncDone:
//fmt.Println("<-f.syncDone", f.syncing)
f.checkSyncedHeaders()
f.syncing = false
}
}
f.pm.waitSyncLock()
if !f.pm.needToSync(peerHead) {
// synced up by the one we've been waiting to end
f.pm.releaseSyncLock()
return true
}
f.pm.syncWithLockAcquired(p)
return !f.pm.needToSync(peerHead)
}
func (f *lightFetcher) request(p *peer, block blockInfo) (uint64, chan *types.Header) {
reqID := f.odr.getNextReqID()
f.reqMu.Lock()
chn := make(chan *types.Header, 1)
f.requested[reqID] = chn
f.reqMu.Unlock()
cost := p.GetRequestCost(GetBlockHeadersMsg, 1)
p.fcServer.SendRequest(reqID, cost)
p.RequestHeadersByHash(reqID, cost, block.Hash, 1, 0, false)
return reqID, chn
}

View File

@ -80,7 +80,7 @@ func (peer *ClientNode) RequestProcessed(cost uint64) (bv, realCost uint64) {
defer peer.lock.Unlock()
time := getTime()
peer.recalcBV(getTime())
peer.recalcBV(time)
peer.bufValue -= cost
peer.recalcBV(time)
rcValue, rcost := peer.cm.processed(peer.cmNode, time)

View File

@ -18,6 +18,7 @@
package les
import (
"encoding/binary"
"errors"
"fmt"
"math/big"
@ -46,12 +47,14 @@ const (
ethVersion = 63 // equivalent eth version for the downloader
MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request
MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request
MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request
MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request
MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
MaxHeaderProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
MaxTxSend = 64 // Amount of transactions to be send per request
disableClientRemovePeer = true
)
@ -86,19 +89,19 @@ type txPool interface {
}
type ProtocolManager struct {
lightSync bool
txpool txPool
txrelay *LesTxRelay
networkId int
lightSync bool
txpool txPool
txrelay *LesTxRelay
networkId int
chainConfig *core.ChainConfig
blockchain BlockChain
chainDb ethdb.Database
odr *LesOdr
server *LesServer
blockchain BlockChain
chainDb ethdb.Database
odr *LesOdr
server *LesServer
downloader *downloader.Downloader
fetcher *lightFetcher
peers *peerSet
peers *peerSet
SubProtocols []p2p.Protocol
@ -109,9 +112,9 @@ type ProtocolManager struct {
quitSync chan struct{}
noMorePeers chan struct{}
syncMu sync.Mutex
syncing bool
syncDone chan struct{}
syncMu sync.Mutex
syncing bool
syncDone chan struct{}
// wait group is used for graceful shutdowns during downloading
// and processing
@ -172,7 +175,7 @@ func NewProtocolManager(chainConfig *core.ChainConfig, lightSync bool, networkId
return nil, errIncompatibleConfig
}
removePeer := manager.removePeer
removePeer := manager.removePeer
if disableClientRemovePeer {
removePeer = func(id string) {}
}
@ -232,7 +235,8 @@ func (pm *ProtocolManager) Start() {
go pm.syncer()
} else {
go func() {
for range pm.newPeerCh {}
for range pm.newPeerCh {
}
}()
}
}
@ -316,20 +320,37 @@ func (pm *ProtocolManager) handle(p *peer) error {
if pm.txrelay != nil {
pm.txrelay.addPeer(p)
}
pm.fetcher.notify(p, nil)
}
stop := make(chan struct{})
defer close(stop)
go func() {
// new block announce loop
for {
select {
case announce := <- p.newBlockHashChn:
p.SendNewBlockHash(announce)
//fmt.Println(" BROADCAST sent")
case <-stop:
return
}
}
}()
// main loop. handle incoming messages.
for {
if err := pm.handleMsg(p); err != nil {
glog.V(logger.Debug).Infof("%v: message handling failed: %v", p, err)
fmt.Println("handleMsg err:", err)
//fmt.Println("handleMsg err:", err)
return err
}
}
return nil
}
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg}
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg, GetHeaderProofsMsg}
// handleMsg is invoked whenever an inbound message is received from a remote
// peer. The remote connection is torn down upon returning any error.
@ -343,6 +364,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
var costs *requestCosts
var reqCnt, maxReqs int
//fmt.Println("MSG", msg.Code, msg.Size)
if rc, ok := p.fcCosts[msg.Code]; ok { // check if msg is a supported request type
costs = rc
if p.fcClient == nil {
@ -352,11 +374,12 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if !ok || bv < costs.baseCost {
return errResp(ErrRequestRejected, "")
}
d := bv - costs.baseCost
if d/10000 < costs.reqCost {
maxReqs = int(d / costs.reqCost)
} else {
maxReqs = 10000
maxReqs = 10000
if bv < pm.server.defParams.BufLimit {
d := bv - costs.baseCost
if d/10000 < costs.reqCost {
maxReqs = int(d / costs.reqCost)
}
}
}
@ -375,16 +398,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrExtraStatusMsg, "uncontrolled status message")
// Block header query, collect the requested headers and reply
case NewBlockHashesMsg:
var req newBlockHashesData
case NewBlockHashMsg:
var req newBlockHashData
if err := msg.Decode(&req); err != nil {
return errResp(ErrDecode, "%v: %v", msg, err)
}
//fmt.Println("RECEIVED", req[0].Number, req[0].Hash, req[0].Td)
for _, r := range req {
pm.fetcher.notify(p, r)
}
//fmt.Println("RECEIVED", req.Number, req.Hash, req.Td, req.ReorgDepth)
pm.fetcher.notify(p, &req)
case GetBlockHeadersMsg:
glog.V(logger.Debug).Infof("LES: received GetBlockHeadersMsg from peer %v", p.id)
// Decode the complex header query
@ -395,7 +416,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&req); err != nil {
return errResp(ErrDecode, "%v: %v", msg, err)
}
query := req.Query
if query.Amount > uint64(maxReqs) || query.Amount > MaxHeaderFetch {
return errResp(ErrRequestRejected, "")
}
hashMode := query.Origin.Hash != (common.Hash{})
@ -405,7 +430,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
headers []*types.Header
unknown bool
)
for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit && len(headers) < downloader.MaxHeaderFetch {
for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit {
// Retrieve the next header satisfying the query
var origin *types.Header
if hashMode {
@ -457,8 +482,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
query.Origin.Number += (query.Skip + 1)
}
}
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + query.Amount * costs.reqCost)
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + query.Amount*costs.reqCost)
pm.server.fcCostStats.update(msg.Code, query.Amount, rcost)
return p.SendBlockHeaders(req.ReqID, bv, headers)
@ -502,11 +527,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
bodies []rlp.RawValue
)
reqCnt = len(req.Hashes)
if reqCnt > maxReqs {
if reqCnt > maxReqs || reqCnt > MaxBodyFetch {
return errResp(ErrRequestRejected, "")
}
for _, hash := range req.Hashes {
if bytes >= softResponseLimit || len(bodies) >= MaxBodyFetch {
if bytes >= softResponseLimit {
break
}
// Retrieve the requested block body, stopping if enough was found
@ -556,13 +581,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
data [][]byte
)
reqCnt = len(req.Reqs)
if reqCnt > maxReqs {
if reqCnt > maxReqs || reqCnt > MaxCodeFetch {
return errResp(ErrRequestRejected, "")
}
for _, req := range req.Reqs {
if len(data) >= MaxCodeFetch {
break
}
// Retrieve the requested state entry, stopping if enough was found
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
if trie, _ := trie.New(header.Root, pm.chainDb); trie != nil {
@ -619,11 +641,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
receipts []rlp.RawValue
)
reqCnt = len(req.Hashes)
if reqCnt > maxReqs {
if reqCnt > maxReqs || reqCnt > MaxReceiptFetch {
return errResp(ErrRequestRejected, "")
}
for _, hash := range req.Hashes {
if bytes >= softResponseLimit || len(receipts) >= MaxReceiptFetch {
if bytes >= softResponseLimit {
break
}
// Retrieve the requested block's receipts, skipping if unknown to us
@ -682,11 +704,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
proofs proofsData
)
reqCnt = len(req.Reqs)
if reqCnt > maxReqs {
if reqCnt > maxReqs || reqCnt > MaxProofsFetch {
return errResp(ErrRequestRejected, "")
}
for _, req := range req.Reqs {
if bytes >= softResponseLimit || len(proofs) >= MaxProofsFetch {
if bytes >= softResponseLimit {
break
}
// Retrieve the requested state entry, stopping if enough was found
@ -732,6 +754,66 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
Obj: resp.Data,
}
case GetHeaderProofsMsg:
glog.V(logger.Debug).Infof("LES: received GetHeaderProofsMsg from peer %v", p.id)
// Decode the retrieval message
var req struct {
ReqID uint64
Reqs []ChtReq
}
if err := msg.Decode(&req); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// Gather state data until the fetch or network limits is reached
var (
bytes int
proofs []ChtResp
)
reqCnt = len(req.Reqs)
if reqCnt > maxReqs || reqCnt > MaxHeaderProofsFetch {
return errResp(ErrRequestRejected, "")
}
for _, req := range req.Reqs {
if bytes >= softResponseLimit {
break
}
if header := pm.blockchain.GetHeaderByNumber(req.BlockNum); header != nil {
if root := getChtRoot(pm.chainDb, req.ChtNum); root != (common.Hash{}) {
if tr, _ := trie.New(root, pm.chainDb); tr != nil {
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], req.BlockNum)
proof := tr.Prove(encNumber[:])
proofs = append(proofs, ChtResp{Header: header, Proof: proof})
bytes += len(proof) + estHeaderRlpSize
}
}
}
}
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
return p.SendHeaderProofs(req.ReqID, bv, proofs)
case HeaderProofsMsg:
if pm.odr == nil {
return errResp(ErrUnexpectedResponse, "")
}
glog.V(logger.Debug).Infof("LES: received HeaderProofsMsg from peer %v", p.id)
var resp struct {
ReqID, BV uint64
Data []ChtResp
}
if err := msg.Decode(&resp); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
p.fcServer.GotReply(resp.ReqID, resp.BV)
deliverMsg = &Msg{
MsgType: MsgHeaderProofs,
ReqID: resp.ReqID,
Obj: resp.Data,
}
case SendTxMsg:
if pm.txpool == nil {
return errResp(ErrUnexpectedResponse, "")
@ -742,7 +824,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
reqCnt = len(txs)
if reqCnt > maxReqs {
if reqCnt > maxReqs || reqCnt > MaxTxSend {
return errResp(ErrRequestRejected, "")
}
pm.txpool.AddTransactions(txs)
@ -770,29 +852,3 @@ func (self *ProtocolManager) NodeInfo() *eth.EthNodeInfo {
Head: self.blockchain.LastBlockHash(),
}
}
func (pm *ProtocolManager) broadcastBlockLoop() {
sub := pm.eventMux.Subscribe( core.ChainHeadEvent{})
go func() {
for {
select {
case ev := <-sub.Chan():
peers := pm.peers.AllPeers()
if len(peers) > 0 {
header := ev.Data.(core.ChainHeadEvent).Block.Header()
hash := header.Hash()
number := header.Number.Uint64()
td := core.GetTd(pm.chainDb, hash, number)
//fmt.Println("BROADCAST", number, hash, td)
announce := newBlockHashesData{{Hash: hash, Number: number, Td: td}}
for _, p := range peers {
p.SendNewBlockHashes(announce)
}
}
case <-pm.quitSync:
sub.Unsubscribe()
return
}
}
}()
}

View File

@ -91,6 +91,7 @@ const (
MsgCode
MsgReceipts
MsgProofs
MsgHeaderProofs
)
// Msg encodes a LES message that delivers reply data for a request
@ -140,7 +141,7 @@ func (self *LesOdr) requestPeer(req *sentReq, peer *peer, delivered, timeout cha
select {
case <-delivered:
servTime := uint64(mclock.Now()-stime)
servTime := uint64(mclock.Now() - stime)
self.peers.updateTimeout(peer, false)
self.peers.updateServTime(peer, servTime)
return
@ -155,7 +156,7 @@ func (self *LesOdr) requestPeer(req *sentReq, peer *peer, delivered, timeout cha
select {
case <-delivered:
servTime := uint64(mclock.Now()-stime)
servTime := uint64(mclock.Now() - stime)
self.peers.updateServTime(peer, servTime)
return
case <-time.After(hardRequestTimeout):

View File

@ -20,6 +20,7 @@ package les
import (
"bytes"
"encoding/binary"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
@ -49,6 +50,8 @@ func LesRequest(req light.OdrRequest) LesOdrRequest {
return (*TrieRequest)(r)
case *light.CodeRequest:
return (*CodeRequest)(r)
case *light.ChtRequest:
return (*ChtRequest)(r)
default:
return nil
}
@ -156,7 +159,7 @@ func (self *ReceiptsRequest) Valid(db ethdb.Database, msg *Msg) bool {
type ProofReq struct {
BHash common.Hash
AccKey, Key []byte
FromLevel uint
FromLevel uint
}
// ODR request type for state/storage trie entries, see LesOdrRequest interface
@ -252,3 +255,71 @@ func (self *CodeRequest) Valid(db ethdb.Database, msg *Msg) bool {
glog.V(logger.Debug).Infof("ODR: validation successful")
return true
}
type ChtReq struct {
ChtNum, BlockNum, FromLevel uint64
}
type ChtResp struct {
Header *types.Header
Proof []rlp.RawValue
}
// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
type ChtRequest light.ChtRequest
// GetCost returns the cost of the given ODR request according to the serving
// peer's cost table (implementation of LesOdrRequest)
func (self *ChtRequest) GetCost(peer *peer) uint64 {
return peer.GetRequestCost(GetHeaderProofsMsg, 1)
}
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
func (self *ChtRequest) Request(reqID uint64, peer *peer) error {
glog.V(logger.Debug).Infof("ODR: requesting CHT #%d block #%d from peer %v", self.ChtNum, self.BlockNum, peer.id)
req := &ChtReq{
ChtNum: self.ChtNum,
BlockNum: self.BlockNum,
}
return peer.RequestHeaderProofs(reqID, self.GetCost(peer), []*ChtReq{req})
}
// Valid processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest)
func (self *ChtRequest) Valid(db ethdb.Database, msg *Msg) bool {
glog.V(logger.Debug).Infof("ODR: validating CHT #%d block #%d", self.ChtNum, self.BlockNum)
if msg.MsgType != MsgHeaderProofs {
glog.V(logger.Debug).Infof("ODR: invalid message type")
return false
}
proofs := msg.Obj.([]ChtResp)
if len(proofs) != 1 {
glog.V(logger.Debug).Infof("ODR: invalid number of entries: %d", len(proofs))
return false
}
proof := proofs[0]
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], self.BlockNum)
value, err := trie.VerifyProof(self.ChtRoot, encNumber[:], proof.Proof)
if err != nil {
glog.V(logger.Debug).Infof("ODR: CHT merkle proof verification error: %v", err)
return false
}
var node light.ChtNode
if err := rlp.DecodeBytes(value, &node); err != nil {
glog.V(logger.Debug).Infof("ODR: error decoding CHT node: %v", err)
return false
}
if node.Hash != proof.Header.Hash() {
glog.V(logger.Debug).Infof("ODR: CHT header hash does not match")
return false
}
self.Proof = proof.Proof
self.Header = proof.Header
self.Td = node.Td
glog.V(logger.Debug).Infof("ODR: validation successful")
return true
}

View File

@ -31,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
"gopkg.in/fatih/set.v0"
)
var (
@ -40,9 +39,7 @@ var (
errNotRegistered = errors.New("peer is not registered")
)
const (
maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
)
const maxHeadInfoLen = 20
type peer struct {
*p2p.Peer
@ -54,15 +51,17 @@ type peer struct {
id string
headInfo blockInfo
number uint64
lock sync.RWMutex
firstHeadInfo, headInfo *newBlockHashData
headInfoLen int
lock sync.RWMutex
knownBlocks *set.Set // Set of block hashes known to be known by this peer
newBlockHashChn chan newBlockHashData
fcClient *flowcontrol.ClientNode // nil if the peer is server only
fcServer *flowcontrol.ServerNode // nil if the peer is client only
fcCosts requestCostTable
fcClient *flowcontrol.ClientNode // nil if the peer is server only
fcServer *flowcontrol.ServerNode // nil if the peer is client only
fcServerParams *flowcontrol.ServerParams
fcCosts requestCostTable
}
func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
@ -74,7 +73,7 @@ func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
version: version,
network: network,
id: fmt.Sprintf("%x", id[:8]),
knownBlocks: set.New(),
newBlockHashChn: make(chan newBlockHashData, 20),
}
}
@ -100,7 +99,66 @@ func (p *peer) headBlockInfo() blockInfo {
p.lock.RLock()
defer p.lock.RUnlock()
return p.headInfo
return blockInfo{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td}
}
func (p *peer) addNotify(announce *newBlockHashData) bool {
p.lock.Lock()
defer p.lock.Unlock()
if announce.Td.Cmp(p.headInfo.Td) < 1 {
return false
}
if p.headInfoLen >= maxHeadInfoLen {
//return false
p.firstHeadInfo = p.firstHeadInfo.next
p.headInfoLen--
}
hh := p.headInfo.Number - announce.ReorgDepth
if p.headInfo.haveHeaders < hh {
hh = p.headInfo.haveHeaders
}
announce.haveHeaders = hh
p.headInfo.next = announce
p.headInfo = announce
p.headInfoLen++
return true
}
func (p *peer) gotHeader(hash common.Hash, number uint64, td *big.Int) bool {
h := p.firstHeadInfo
ptr := 0
for h != nil {
if h.Hash == hash {
if h.Number != number || h.Td.Cmp(td) != 0 {
return false
}
h.headKnown = true
h.haveHeaders = h.Number
p.firstHeadInfo = h
p.headInfoLen -= ptr
last := h
h = h.next
// propagate haveHeaders through the chain
for h != nil {
hh := last.Number-h.ReorgDepth
if last.haveHeaders < hh {
hh = last.haveHeaders
}
if hh > h.haveHeaders {
h.haveHeaders = hh
} else {
return true
}
last = h
h = h.next
}
return true
}
h = h.next
ptr++
}
return true
}
// Td retrieves the current total difficulty of a peer.
@ -128,13 +186,17 @@ func sendResponse(w p2p.MsgWriter, msgcode, reqID, bv uint64, data interface{})
}
func (p *peer) GetRequestCost(msgcode uint64, amount int) uint64 {
return p.fcCosts[msgcode].baseCost + p.fcCosts[msgcode].reqCost*uint64(amount)
cost := p.fcCosts[msgcode].baseCost + p.fcCosts[msgcode].reqCost*uint64(amount)
if cost > p.fcServerParams.BufLimit {
cost = p.fcServerParams.BufLimit
}
return cost
}
// SendNewBlockHashes announces the availability of a number of blocks through
// SendNewBlockHash announces the availability of a number of blocks through
// a hash notification.
func (p *peer) SendNewBlockHashes(request newBlockHashesData) error {
return p2p.Send(p.rw, NewBlockHashesMsg, request)
func (p *peer) SendNewBlockHash(request newBlockHashData) error {
return p2p.Send(p.rw, NewBlockHashMsg, request)
}
// SendBlockHeaders sends a batch of block headers to the remote peer.
@ -165,6 +227,11 @@ func (p *peer) SendProofs(reqID, bv uint64, proofs proofsData) error {
return sendResponse(p.rw, ProofsMsg, reqID, bv, proofs)
}
// SendHeaderProofs sends a batch of header proofs, corresponding to the ones requested.
func (p *peer) SendHeaderProofs(reqID, bv uint64, proofs []ChtResp) error {
return sendResponse(p.rw, HeaderProofsMsg, reqID, bv, proofs)
}
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
// specified header query, based on the hash of an origin block.
func (p *peer) RequestHeadersByHash(reqID, cost uint64, origin common.Hash, amount int, skip int, reverse bool) error {
@ -205,14 +272,20 @@ func (p *peer) RequestProofs(reqID, cost uint64, reqs []*ProofReq) error {
return sendRequest(p.rw, GetProofsMsg, reqID, cost, reqs)
}
// RequestHeaderProofs fetches a batch of header merkle proofs from a remote node.
func (p *peer) RequestHeaderProofs(reqID, cost uint64, reqs []*ChtReq) error {
glog.V(logger.Debug).Infof("%v fetching %v header proofs", p, len(reqs))
return sendRequest(p.rw, GetHeaderProofsMsg, reqID, cost, reqs)
}
func (p *peer) SendTxs(cost uint64, txs types.Transactions) error {
glog.V(logger.Debug).Infof("%v relaying %v txs", p, len(txs))
p.fcServer.SendRequest(0, cost)
return p2p.Send(p.rw, SendTxMsg, txs)
}
type keyValueEntry struct{
Key string
type keyValueEntry struct {
Key string
Value rlp.RawValue
}
type keyValueList []keyValueEntry
@ -307,7 +380,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
return err
}
recv := recvList.decode()
var rGenesis, rHash common.Hash
var rVersion, rNetwork, rNum uint64
var rTd *big.Int
@ -330,7 +403,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
if err := recv.get("genesisHash", &rGenesis); err != nil {
return err
}
if rGenesis != genesis {
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", rGenesis, genesis)
}
@ -366,11 +439,15 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
if err := recv.get("flowControl/MRC", &MRC); err != nil {
return err
}
p.fcServerParams = params
p.fcServer = flowcontrol.NewServerNode(params)
p.fcServerParams = params
p.fcCosts = MRC.decode()
}
// Configure the remote peer, and sanity check out handshake too
p.headInfo.Td, p.headInfo.Hash, p.headInfo.Number = rTd, rHash, rNum
p.firstHeadInfo = &newBlockHashData{Td: rTd, Hash: rHash, Number: rNum}
p.headInfo = p.firstHeadInfo
p.headInfoLen = 1
return nil
}

View File

@ -47,7 +47,7 @@ const (
const (
// Protocol messages belonging to LPV1
StatusMsg = 0x00
NewBlockHashesMsg = 0x01
NewBlockHashMsg = 0x01
GetBlockHeadersMsg = 0x02
BlockHeadersMsg = 0x03
GetBlockBodiesMsg = 0x04
@ -59,8 +59,8 @@ const (
GetCodeMsg = 0x0a
CodeMsg = 0x0b
SendTxMsg = 0x0c
GetTxHashesMsg = 0x0d
TxHashesMsg = 0x0e
GetHeaderProofsMsg = 0x0d
HeaderProofsMsg = 0x0e
)
type errCode int
@ -102,7 +102,7 @@ var errorToString = map[int]string{
ErrUnexpectedResponse: "Unexpected response",
ErrInvalidResponse: "Invalid response",
ErrTooManyTimeouts: "Too many request timeouts",
ErrHandshakeMissingKey: "Key missing from handshake message",
ErrHandshakeMissingKey: "Key missing from handshake message",
}
type chainManager interface {
@ -123,8 +123,18 @@ type statusData struct {
MRC RequestCostList
}
// newBlockHashesData is the network packet for the block announcements.
type newBlockHashesData []blockInfo
// newBlockHashData is the network packet for the block announcements.
type newBlockHashData struct{
Hash common.Hash // Hash of one particular block being announced
Number uint64 // Number of one particular block being announced
Td *big.Int // Total difficulty of one particular block being announced
ReorgDepth uint64
haveHeaders uint64 // we have the headers of the remote peer's chain up to this number
headKnown bool
requested bool
next *newBlockHashData
}
type blockInfo struct {
Hash common.Hash // Hash of one particular block being announced
Number uint64 // Number of one particular block being announced

View File

@ -18,13 +18,22 @@
package les
import (
"encoding/binary"
"fmt"
"math"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/flowcontrol"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
type LesServer struct {
@ -34,12 +43,12 @@ type LesServer struct {
defParams *flowcontrol.ServerParams
}
func NewLesServer(eth *eth.FullNodeService, config *eth.Config) (*LesServer, error) {
func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, eth.EventMux(), eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil)
if err != nil {
return nil, err
}
pm.broadcastBlockLoop()
pm.blockLoop()
srv := &LesServer{protocolManager: pm}
pm.server = srv
@ -101,39 +110,105 @@ func (table requestCostTable) encode() RequestCostList {
return list
}
type requestCostStats struct {
lock sync.RWMutex
db ethdb.Database
avg requestCostTable
baseCost uint64
type linReg struct {
sumX, sumY, sumXX, sumXY float64
cnt uint64
}
var rcStatsKey = []byte("requestCostStats")
const linRegMaxCnt = 100000
func (l *linReg) add(x, y float64) {
if l.cnt >= linRegMaxCnt {
sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt
l.sumX -= l.sumX * sub
l.sumY -= l.sumY * sub
l.sumXX -= l.sumXX * sub
l.sumXY -= l.sumXY * sub
l.cnt = linRegMaxCnt - 1
}
l.cnt++
l.sumX += x
l.sumY += y
l.sumXX += x * x
l.sumXY += x * y
}
func (l *linReg) calc() (b, m float64) {
if l.cnt == 0 {
return 0, 0
}
cnt := float64(l.cnt)
d := cnt*l.sumXX - l.sumX*l.sumX
if d < 0.001 {
return l.sumY / cnt, 0
}
m = (cnt*l.sumXY - l.sumX*l.sumY) / d
b = (l.sumY / cnt) - (m * l.sumX / cnt)
return b, m
}
func (l *linReg) toBytes() []byte {
var arr [40]byte
binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX))
binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY))
binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX))
binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY))
binary.BigEndian.PutUint64(arr[32:40], l.cnt)
return arr[:]
}
func linRegFromBytes(data []byte) *linReg {
if len(data) != 40 {
return nil
}
l := &linReg{}
l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8]))
l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16]))
l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24]))
l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32]))
l.cnt = binary.BigEndian.Uint64(data[32:40])
return l
}
type requestCostStats struct {
lock sync.RWMutex
db ethdb.Database
stats map[uint64]*linReg
}
type requestCostStatsRlp []struct {
MsgCode uint64
Data []byte
}
var rcStatsKey = []byte("_requestCostStats")
func newCostStats(db ethdb.Database) *requestCostStats {
table := make(requestCostTable)
stats := make(map[uint64]*linReg)
for _, code := range reqList {
table[code] = &requestCosts{0, 100000}
stats[code] = &linReg{cnt: 100}
}
/* if db != nil {
var cl RequestCostList
if db != nil {
data, err := db.Get(rcStatsKey)
var statsRlp requestCostStatsRlp
if err == nil {
err = rlp.DecodeBytes(data, &cl)
err = rlp.DecodeBytes(data, &statsRlp)
}
if err == nil {
t := cl.decode()
for code, entry := range t {
table[code] = entry
for _, r := range statsRlp {
if stats[r.MsgCode] != nil {
if l := linRegFromBytes(r.Data); l != nil {
stats[r.MsgCode] = l
}
}
}
}
}*/
}
return &requestCostStats{
db: db,
avg: table,
baseCost: 100000,
db: db,
stats: stats,
}
}
@ -141,8 +216,13 @@ func (s *requestCostStats) store() {
s.lock.Lock()
defer s.lock.Unlock()
list := s.avg.encode()
if data, err := rlp.EncodeToBytes(list); err == nil {
statsRlp := make(requestCostStatsRlp, len(reqList))
for i, code := range reqList {
statsRlp[i].MsgCode = code
statsRlp[i].Data = s.stats[code].toBytes()
}
if data, err := rlp.EncodeToBytes(statsRlp); err == nil {
s.db.Put(rcStatsKey, data)
}
}
@ -151,11 +231,22 @@ func (s *requestCostStats) getCurrentList() RequestCostList {
s.lock.Lock()
defer s.lock.Unlock()
list := make(RequestCostList, len(s.avg))
list := make(RequestCostList, len(reqList))
//fmt.Println("RequestCostList")
for idx, code := range reqList {
b, m := s.stats[code].calc()
//fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000)
if m < 0 {
b += m
m = 0
}
if b < 0 {
b = 0
}
list[idx].MsgCode = code
list[idx].BaseCost = s.baseCost
list[idx].ReqCost = s.avg[code].reqCost * 2
list[idx].BaseCost = uint64(b * 2)
list[idx].ReqCost = uint64(m * 2)
}
return list
}
@ -164,14 +255,147 @@ func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) {
s.lock.Lock()
defer s.lock.Unlock()
c, ok := s.avg[msgCode]
c, ok := s.stats[msgCode]
if !ok || reqCnt == 0 {
return
}
cost = cost / reqCnt
if cost > c.reqCost {
c.reqCost += (cost - c.reqCost) / 10
} else {
c.reqCost -= (c.reqCost - cost) / 100
}
c.add(float64(reqCnt), float64(cost))
}
func (pm *ProtocolManager) blockLoop() {
pm.wg.Add(1)
sub := pm.eventMux.Subscribe(core.ChainHeadEvent{})
newCht := make(chan struct{}, 10)
newCht <- struct{}{}
go func() {
var mu sync.Mutex
var lastHead *types.Header
lastBroadcastTd := common.Big0
for {
select {
case ev := <-sub.Chan():
peers := pm.peers.AllPeers()
if len(peers) > 0 {
header := ev.Data.(core.ChainHeadEvent).Block.Header()
hash := header.Hash()
number := header.GetNumberU64()
td := core.GetTd(pm.chainDb, hash, number)
if td != nil && td.Cmp(lastBroadcastTd) > 0 {
var reorg uint64
if lastHead != nil {
reorg = lastHead.GetNumberU64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).GetNumberU64()
}
lastHead = header
lastBroadcastTd = td
//fmt.Println("BROADCAST", number, hash, td, reorg)
announce := newBlockHashData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
for _, p := range peers {
select {
case p.newBlockHashChn <- announce:
default:
pm.removePeer(p.id)
}
}
}
}
newCht <- struct{}{}
case <-newCht:
go func() {
mu.Lock()
more := makeCht(pm.chainDb)
mu.Unlock()
if more {
time.Sleep(time.Millisecond * 10)
newCht <- struct{}{}
}
}()
case <-pm.quitSync:
sub.Unsubscribe()
pm.wg.Done()
return
}
}
}()
}
var (
lastChtKey = []byte("LastChtNumber") // chtNum (uint64 big endian)
chtPrefix = []byte("cht") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
chtConfirmations = light.ChtFrequency / 2
)
func getChtRoot(db ethdb.Database, num uint64) common.Hash {
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], num)
data, _ := db.Get(append(chtPrefix, encNumber[:]...))
return common.BytesToHash(data)
}
func storeChtRoot(db ethdb.Database, num uint64, root common.Hash) {
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], num)
db.Put(append(chtPrefix, encNumber[:]...), root[:])
}
func makeCht(db ethdb.Database) bool {
headHash := core.GetHeadBlockHash(db)
headNum := core.GetBlockNumber(db, headHash)
var newChtNum uint64
if headNum > chtConfirmations {
newChtNum = (headNum - chtConfirmations) / light.ChtFrequency
}
var lastChtNum uint64
data, _ := db.Get(lastChtKey)
if len(data) == 8 {
lastChtNum = binary.BigEndian.Uint64(data[:])
}
if newChtNum <= lastChtNum {
return false
}
var t *trie.Trie
if lastChtNum > 0 {
var err error
t, err = trie.New(getChtRoot(db, lastChtNum), db)
if err != nil {
lastChtNum = 0
}
}
if lastChtNum == 0 {
t, _ = trie.New(common.Hash{}, db)
}
for num := lastChtNum * light.ChtFrequency; num < (lastChtNum+1)*light.ChtFrequency; num++ {
hash := core.GetCanonicalHash(db, num)
if hash == (common.Hash{}) {
panic("Canonical hash not found")
}
td := core.GetTd(db, hash, num)
if td == nil {
panic("TD not found")
}
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], num)
var node light.ChtNode
node.Hash = hash
node.Td = td
data, _ := rlp.EncodeToBytes(node)
t.Update(encNumber[:], data)
}
root, err := t.Commit()
if err != nil {
lastChtNum = 0
} else {
lastChtNum++
fmt.Printf("CHT %d %064x\n", lastChtNum, root)
storeChtRoot(db, lastChtNum, root)
var data [8]byte
binary.BigEndian.PutUint64(data[:], lastChtNum)
db.Put(lastChtKey, data[:])
}
return newChtNum > lastChtNum
}

View File

@ -21,11 +21,13 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/light"
"golang.org/x/net/context"
)
const (
forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available
minDesiredPeerCount = 5 // Amount of peers desired to start syncing
//forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available
minDesiredPeerCount = 5 // Amount of peers desired to start syncing
)
// syncer is responsible for periodically synchronising with the network, both
@ -37,20 +39,20 @@ func (pm *ProtocolManager) syncer() {
defer pm.downloader.Terminate()
// Wait for different events to fire synchronisation operations
forceSync := time.Tick(forceSyncCycle)
//forceSync := time.Tick(forceSyncCycle)
for {
select {
case <-pm.newPeerCh:
// Make sure we have peers to select from, then sync
/* // Make sure we have peers to select from, then sync
if pm.peers.Len() < minDesiredPeerCount {
break
}
go pm.synchronise(pm.peers.BestPeer())
case <-forceSync:
// Force a sync even if not enough peers are present
go pm.synchronise(pm.peers.BestPeer())
*/
/*case <-forceSync:
// Force a sync even if not enough peers are present
go pm.synchronise(pm.peers.BestPeer())
*/
case <-pm.noMorePeers:
return
}
@ -69,54 +71,14 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
if peer == nil {
return
}
// Make sure the peer's TD is higher than our own.
if !pm.needToSync(peer.headBlockInfo()) {
return
}
pm.waitSyncLock()
pm.syncWithLockAcquired(peer)
}
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
pm.blockchain.(*light.LightChain).SyncCht(ctx)
func (pm *ProtocolManager) waitSyncLock() {
for {
chn := pm.getSyncLock(true)
if chn == nil {
break
}
<-chn
}
}
// getSyncLock either acquires the sync lock and returns nil or returns a channel
// which is closed when the lock is free again
func (pm *ProtocolManager) getSyncLock(acquire bool) chan struct{} {
pm.syncMu.Lock()
defer pm.syncMu.Unlock()
if pm.syncing {
if pm.syncDone == nil {
pm.syncDone = make(chan struct{})
}
return pm.syncDone
} else {
pm.syncing = acquire
return nil
}
}
func (pm *ProtocolManager) releaseSyncLock() {
pm.syncMu.Lock()
pm.syncing = false
if pm.syncDone != nil {
close(pm.syncDone)
pm.syncDone = nil
}
pm.syncMu.Unlock()
}
func (pm *ProtocolManager) syncWithLockAcquired(peer *peer) {
pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync)
pm.releaseSyncLock()
}

View File

@ -101,6 +101,27 @@ func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *e
}
glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block")
}
if bc.genesisBlock.Hash() == (common.Hash{212, 229, 103, 64, 248, 118, 174, 248, 192, 16, 184, 106, 64, 213, 245, 103, 69, 161, 24, 208, 144, 106, 52, 230, 154, 236, 140, 13, 177, 203, 143, 163}) {
// add trusted CHT
WriteTrustedCht(bc.chainDb, TrustedCht{
Number: 459,
Root: common.HexToHash("9f14334360d1e48ed49a3d6b0b9e38a1706732cebc6a9c47bdde8d9824cd6d6b"),
})
glog.V(logger.Info).Infoln("Added trusted CHT for mainnet")
} else {
if bc.genesisBlock.Hash() == (common.Hash{12, 215, 134, 162, 66, 93, 22, 241, 82, 198, 88, 49, 108, 66, 62, 108, 225, 24, 30, 21, 195, 41, 88, 38, 215, 201, 144, 76, 186, 156, 227, 3}) {
// add trusted CHT for testnet
WriteTrustedCht(bc.chainDb, TrustedCht{
Number: 319,
Root: common.HexToHash("43b679ff9b4918b0b19e6256f20e35877365ec3e20b38e3b2a02cef5606176dc"),
})
glog.V(logger.Info).Infoln("Added trusted CHT for testnet")
} else {
DeleteTrustedCht(bc.chainDb)
}
}
if err := bc.loadLastState(); err != nil {
return nil, err
}
@ -298,9 +319,9 @@ func (self *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*
// GetBlockByNumber retrieves a block from the database or ODR service by
// number, caching it (associated with its hash) if found.
func (self *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) {
hash := core.GetCanonicalHash(self.chainDb, number)
if hash == (common.Hash{}) {
return nil, nil
hash, err := GetCanonicalHash(ctx, self.odr, number)
if hash == (common.Hash{}) || err != nil {
return nil, err
}
return self.GetBlock(ctx, hash, number)
}
@ -448,3 +469,30 @@ func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []c
func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header {
return self.hc.GetHeaderByNumber(number)
}
// GetHeaderByNumberOdr retrieves a block header from the database or network
// by number, caching it (associated with its hash) if found.
func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) {
if header := self.hc.GetHeaderByNumber(number); header != nil {
return header, nil
}
return GetHeaderByNumber(ctx, self.odr, number)
}
func (self *LightChain) SyncCht(ctx context.Context) bool {
headNum := self.CurrentHeader().Number.Uint64()
cht := GetTrustedCht(self.chainDb)
if headNum+1 < cht.Number*ChtFrequency {
num := cht.Number*ChtFrequency - 1
header, err := GetHeaderByNumber(ctx, self.odr, num)
if header != nil && err == nil {
self.mu.Lock()
if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
self.hc.SetCurrentHeader(header)
}
self.mu.Unlock()
return true
}
}
return false
}

View File

@ -19,6 +19,8 @@
package light
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
@ -97,7 +99,7 @@ func storeProof(db ethdb.Database, proof []rlp.RawValue) {
// CodeRequest is the ODR request type for retrieving contract code
type CodeRequest struct {
OdrRequest
Id *TrieID
Id *TrieID
Hash common.Hash
Data []byte
}
@ -110,9 +112,9 @@ func (req *CodeRequest) StoreResult(db ethdb.Database) {
// BlockRequest is the ODR request type for retrieving block bodies
type BlockRequest struct {
OdrRequest
Hash common.Hash
Hash common.Hash
Number uint64
Rlp []byte
Rlp []byte
}
// StoreResult stores the retrieved data in local database
@ -124,7 +126,7 @@ func (req *BlockRequest) StoreResult(db ethdb.Database) {
type ReceiptsRequest struct {
OdrRequest
Hash common.Hash
Number uint64
Number uint64
Receipts types.Receipts
}
@ -132,3 +134,23 @@ type ReceiptsRequest struct {
func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
core.WriteBlockReceipts(db, req.Hash, req.Number, req.Receipts)
}
// TrieRequest is the ODR request type for state/storage trie entries
type ChtRequest struct {
OdrRequest
ChtNum, BlockNum uint64
ChtRoot common.Hash
Header *types.Header
Td *big.Int
Proof []rlp.RawValue
}
// StoreResult stores the retrieved data in local database
func (req *ChtRequest) StoreResult(db ethdb.Database) {
// if there is a canonical hash, there is a header too
core.WriteHeader(db, req.Header)
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
core.WriteTd(db, hash, num, req.Td)
core.WriteCanonicalHash(db, hash, num)
//storeProof(db, req.Proof)
}

View File

@ -18,11 +18,13 @@ package light
import (
"bytes"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
@ -31,7 +33,78 @@ import (
var sha3_nil = crypto.Keccak256Hash(nil)
var ErrNoHeader = errors.New("Block header not found")
var (
ErrNoTrustedCht = errors.New("No trusted canonical hash trie")
ErrNoHeader = errors.New("Header not found")
ChtFrequency = uint64(4096)
trustedChtKey = []byte("TrustedCHT")
)
type ChtNode struct {
Hash common.Hash
Td *big.Int
}
type TrustedCht struct {
Number uint64
Root common.Hash
}
func GetTrustedCht(db ethdb.Database) TrustedCht {
data, _ := db.Get(trustedChtKey)
var res TrustedCht
if err := rlp.DecodeBytes(data, &res); err != nil {
return TrustedCht{0, common.Hash{}}
}
return res
}
func WriteTrustedCht(db ethdb.Database, cht TrustedCht) {
data, _ := rlp.EncodeToBytes(cht)
db.Put(trustedChtKey, data)
}
func DeleteTrustedCht(db ethdb.Database) {
db.Delete(trustedChtKey)
}
func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) {
db := odr.Database()
hash := core.GetCanonicalHash(db, number)
if (hash != common.Hash{}) {
// if there is a canonical hash, there is a header too
header := core.GetHeader(db, hash, number)
if header == nil {
panic("Canonical hash present but header not found")
}
return header, nil
}
cht := GetTrustedCht(db)
if number >= cht.Number*ChtFrequency {
return nil, ErrNoTrustedCht
}
r := &ChtRequest{ChtRoot: cht.Root, ChtNum: cht.Number, BlockNum: number}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
} else {
return r.Header, nil
}
}
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
hash := core.GetCanonicalHash(odr.Database(), number)
if (hash != common.Hash{}) {
return hash, nil
}
header, err := GetHeaderByNumber(ctx, odr, number)
if header != nil {
return header.Hash(), nil
}
return common.Hash{}, err
}
// retrieveContractCode tries to retrieve the contract code of the given account
// with the given hash from the network (id points to the storage trie belonging

View File

@ -42,7 +42,7 @@ var txPermanent = uint64(500)
// always receive all locally signed transactions in the same order as they are
// created.
type TxPool struct {
config *core.ChainConfig
config *core.ChainConfig
quit chan bool
eventMux *event.TypeMux
events event.Subscription
@ -78,7 +78,7 @@ type TxRelayBackend interface {
// NewTxPool creates a new light transaction pool
func NewTxPool(config *core.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool {
pool := &TxPool{
config: config,
config: config,
nonce: make(map[common.Address]uint64),
pending: make(map[common.Hash]*types.Transaction),
mined: make(map[common.Hash][]*types.Transaction),
@ -128,14 +128,14 @@ type txBlockData struct {
// storeTxBlockData stores the block position of a mined tx in the local db
func (pool *TxPool) storeTxBlockData(txh common.Hash, tbd txBlockData) {
//fmt.Println("storeTxBlockData", txh, tbd)
//fmt.Println("storeTxBlockData", txh, tbd)
data, _ := rlp.EncodeToBytes(tbd)
pool.chainDb.Put(append(txh[:], byte(1)), data)
}
// removeTxBlockData removes the stored block position of a rolled back tx
func (pool *TxPool) removeTxBlockData(txh common.Hash) {
//fmt.Println("removeTxBlockData", txh)
//fmt.Println("removeTxBlockData", txh)
pool.chainDb.Delete(append(txh[:], byte(1)))
}
@ -169,26 +169,26 @@ func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Has
// and marks them as mined if necessary. It also stores block position in the db
// and adds them to the received txStateChanges map.
func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, idx uint64, txc txStateChanges) error {
//fmt.Println("checkMinedTxs")
//fmt.Println("checkMinedTxs")
if len(pool.pending) == 0 {
return nil
}
//fmt.Println("len(pool) =", len(pool.pending))
//fmt.Println("len(pool) =", len(pool.pending))
block, err := GetBlock(ctx, pool.odr, hash, idx)
var receipts types.Receipts
if err != nil {
//fmt.Println(err)
//fmt.Println(err)
return err
}
//fmt.Println("len(block.Transactions()) =", len(block.Transactions()))
//fmt.Println("len(block.Transactions()) =", len(block.Transactions()))
list := pool.mined[hash]
for i, tx := range block.Transactions() {
txHash := tx.Hash()
//fmt.Println(" txHash:", txHash)
//fmt.Println(" txHash:", txHash)
if tx, ok := pool.pending[txHash]; ok {
//fmt.Println("TX FOUND")
//fmt.Println("TX FOUND")
if receipts == nil {
receipts, err = GetBlockReceipts(ctx, pool.odr, hash, idx)
if err != nil {
@ -199,7 +199,7 @@ func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, idx uin
}
core.SetReceiptsData(block, receipts)
}
//fmt.Println("WriteReceipt", receipts[i].TxHash)
//fmt.Println("WriteReceipt", receipts[i].TxHash)
core.WriteReceipt(pool.chainDb, receipts[i])
pool.storeTxBlockData(txHash, txBlockData{hash, idx, uint64(i)})
delete(pool.pending, txHash)
@ -248,6 +248,10 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx
if oldh.GetNumberU64() < newh.GetNumberU64() {
newHashes = append(newHashes, newh.Hash())
newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1)
if newh == nil {
// happens when CHT syncing, nothing to do
newh = oldh
}
}
}
if oldh.GetNumberU64() < pool.clearIdx {
@ -450,7 +454,7 @@ func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
if err := self.add(ctx, tx); err != nil {
return err
}
//fmt.Println("Send", tx.Hash())
//fmt.Println("Send", tx.Hash())
self.relay.Send(types.Transactions{tx})
self.chainDb.Put(tx.Hash().Bytes(), data)

Some files were not shown because too many files have changed in this diff Show More