diff --git a/Makefile b/Makefile index ca759685f..ef04230d1 100644 --- a/Makefile +++ b/Makefile @@ -81,6 +81,11 @@ test-extkeys: @build/env.sh go tool cover -html=coverage.out -o coverage.html @build/env.sh go tool cover -func=coverage.out +test-cmd: + build/env.sh go test -v -coverprofile=coverage.out ./cmd/status + @build/env.sh go tool cover -html=coverage.out -o coverage.html + @build/env.sh go tool cover -func=coverage.out + clean: rm -fr build/bin/* rm coverage.out coverage-all.out coverage.html diff --git a/build/mainnet-flags.sh b/build/mainnet-flags.sh index b078b9fb7..4a0e67539 100755 --- a/build/mainnet-flags.sh +++ b/build/mainnet-flags.sh @@ -9,5 +9,5 @@ fi # set gitCommit when running from a Git checkout. if [ -f ".git/HEAD" ]; then - echo "-ldflags '-X github.com/status-im/status-go/geth.UseTestnet=false -X main.buildStamp=`date -u '+%Y-%m-%d.%H:%M:%S'` -X main.gitCommit=$(git rev-parse HEAD)'"; + echo "-ldflags '-X github.com/status-im/status-go/geth.UseTestnetFlag=false -X main.buildStamp=`date -u '+%Y-%m-%d.%H:%M:%S'` -X main.gitCommit=$(git rev-parse HEAD)'"; fi diff --git a/build/testnet-flags.sh b/build/testnet-flags.sh index 2b04db662..e833f571d 100755 --- a/build/testnet-flags.sh +++ b/build/testnet-flags.sh @@ -9,5 +9,5 @@ fi # set gitCommit when running from a Git checkout. if [ -f ".git/HEAD" ]; then - echo "-ldflags '-X github.com/status-im/status-go/geth.UseTestnet=true -X main.buildStamp=`date -u '+%Y-%m-%d.%H:%M:%S'` -X main.gitCommit=$(git rev-parse HEAD)'"; + echo "-ldflags '-X github.com/status-im/status-go/geth.UseTestnetFlag=true -X main.buildStamp=`date -u '+%Y-%m-%d.%H:%M:%S'` -X main.gitCommit=$(git rev-parse HEAD)'"; fi diff --git a/cmd/status/library.go b/cmd/status/library.go index f14c19e36..c81e2e8b1 100644 --- a/cmd/status/library.go +++ b/cmd/status/library.go @@ -235,7 +235,7 @@ func Call(chatId *C.char, path *C.char, params *C.char) *C.char { //export AddPeer func AddPeer(url *C.char) *C.char { - success, err := geth.GetNodeManager().AddPeer(C.GoString(url)) + success, err := geth.NodeManagerInstance().AddPeer(C.GoString(url)) errString := "" if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/cmd/status/main.go b/cmd/status/main.go index 2c8721bb2..3bdeee006 100644 --- a/cmd/status/main.go +++ b/cmd/status/main.go @@ -8,23 +8,18 @@ import ( var ( gitCommit = "rely on linker: -ldflags -X main.GitCommit" buildStamp = "rely on linker: -ldflags -X main.buildStamp" - - versionMajor = 1 // Major version component of the current release - versionMinor = 0 // Minor version component of the current release - versionPatch = 0 // Patch version component of the current release - versionMeta = "stable" // Version metadata to append to the version string ) func main() { - verString := fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch) - if versionMeta != "" { - verString += "-" + versionMeta + verString := fmt.Sprintf("%d.%d.%d", geth.VersionMajor, geth.VersionMinor, geth.VersionPatch) + if geth.VersionMeta != "" { + verString += "-" + geth.VersionMeta } if gitCommit != "" { verString += "-" + gitCommit[:8] } netVersion := "mainnet" - if geth.UseTestnet == "true" { + if geth.UseTestnet { netVersion = "testnet" } fmt.Printf("Status\nGit Commit: %s\nBuild Time: %s\nVersion: %s\nNetwork: %s\n", diff --git a/cmd/status/utils.go b/cmd/status/utils.go index 44609cc07..e75679859 100644 --- a/cmd/status/utils.go +++ b/cmd/status/utils.go @@ -11,7 +11,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/les/status" @@ -90,7 +89,7 @@ func testExportedAPI(t *testing.T, done chan struct{}) { func testCreateChildAccount(t *testing.T) bool { geth.Logout() // to make sure that we start with empty account (which might get populated during previous tests) - accountManager, err := geth.GetNodeManager().AccountManager() + accountManager, err := geth.NodeManagerInstance().AccountManager() if err != nil { t.Error(err) return false @@ -112,7 +111,7 @@ func testCreateChildAccount(t *testing.T) bool { address, pubKey, mnemonic := createAccountResponse.Address, createAccountResponse.PubKey, createAccountResponse.Mnemonic t.Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) - account, err := utils.MakeAddress(accountManager, address) + account, err := geth.ParseAccountString(accountManager, address) if err != nil { t.Errorf("can not get account from address: %v", err) return false @@ -221,7 +220,7 @@ func testCreateChildAccount(t *testing.T) bool { } func testRecoverAccount(t *testing.T) bool { - accountManager, _ := geth.GetNodeManager().AccountManager() + accountManager, _ := geth.NodeManagerInstance().AccountManager() // create an account address, pubKey, mnemonic, err := geth.CreateAccount(newAccountPassword) @@ -250,7 +249,7 @@ func testRecoverAccount(t *testing.T) bool { } // now test recovering, but make sure that account/key file is removed i.e. simulate recovering on a new device - account, err := utils.MakeAddress(accountManager, address) + account, err := geth.ParseAccountString(accountManager, address) if err != nil { t.Errorf("can not get account from address: %v", err) } @@ -312,7 +311,7 @@ func testRecoverAccount(t *testing.T) bool { } // time to login with recovered data - whisperService, err := geth.GetNodeManager().WhisperService() + whisperService, err := geth.NodeManagerInstance().WhisperService() if err != nil { t.Errorf("whisper service not running: %v", err) } @@ -335,7 +334,7 @@ func testRecoverAccount(t *testing.T) bool { func testAccountSelect(t *testing.T) bool { // test to see if the account was injected in whisper - whisperService, err := geth.GetNodeManager().WhisperService() + whisperService, err := geth.NodeManagerInstance().WhisperService() if err != nil { t.Errorf("whisper service not running: %v", err) } @@ -418,7 +417,7 @@ func testAccountSelect(t *testing.T) bool { } func testAccountLogout(t *testing.T) bool { - whisperService, err := geth.GetNodeManager().WhisperService() + whisperService, err := geth.NodeManagerInstance().WhisperService() if err != nil { t.Errorf("whisper service not running: %v", err) return false @@ -472,7 +471,7 @@ func testAccountLogout(t *testing.T) bool { func testCompleteTransaction(t *testing.T) bool { // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return false @@ -551,7 +550,7 @@ func testCompleteTransaction(t *testing.T) bool { func testCompleteMultipleQueuedTransactions(t *testing.T) bool { // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return false @@ -680,7 +679,7 @@ func testCompleteMultipleQueuedTransactions(t *testing.T) bool { func testDiscardTransaction(t *testing.T) bool { // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return false @@ -794,7 +793,7 @@ func testDiscardTransaction(t *testing.T) bool { func testDiscardMultipleQueuedTransactions(t *testing.T) bool { // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return false diff --git a/data/static-nodes.json b/data/static-nodes.json index 00f81dbb2..0efaf8b69 100644 --- a/data/static-nodes.json +++ b/data/static-nodes.json @@ -1,4 +1,5 @@ [ + "enode://ebdf43b6fbca48141d08eef70e5735241445e7f2d2937dfd1cb808b598a94fb1e9834372b9f59b1f72e01a38d4102767cc40144f7f4ff17a9a6808b2202559e4@162.243.63.248:30303", "enode://e19d89e6faf2772e2f250e9625478ee7f313fcc0bb5e9310d5d407371496d9d7d73ccecd9f226cc2a8be34484525f72ba9db9d26f0222f4efc3c6d9d995ee224@198.199.105.122:30303", "enode://5f23bf4913dd005ce945648cb12d3ef970069818d8563a3fe054e5e1dc3898b9cb83e0af1f51b2dce75eaffc76e93f996caf538e21c5b64db5fa324958d59630@95.85.40.211:30303", "enode://b9de2532421f15ac55da9d9a7cddc0dc08b0d646d631fd7ab2a170bd2163fb86b095dd8bde66b857592812f7cd9539f2919b6c64bc1a784a1d1c6ec8137681ed@188.166.229.119:30303", diff --git a/geth/accounts.go b/geth/accounts.go index 4812c7692..3a048d59d 100644 --- a/geth/accounts.go +++ b/geth/accounts.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/status-im/status-go/extkeys" @@ -20,6 +19,7 @@ var ( ErrWhisperNoIdentityFound = errors.New("failed to locate identity previously injected into Whisper") ErrNoAccountSelected = errors.New("no account has been selected, please login") ErrInvalidMasterKeyCreated = errors.New("can not create master extended key") + ErrInvalidAccountAddressOrKey = errors.New("cannot parse address or key to valid account address") ) // CreateAccount creates an internal geth account @@ -53,7 +53,7 @@ func CreateAccount(password string) (address, pubKey, mnemonic string, err error // CKD#2 is used as root for master accounts (when parentAddress is ""). // Otherwise (when parentAddress != ""), child is derived directly from parent. func CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) { - nodeManager := GetNodeManager() + nodeManager := NodeManagerInstance() accountManager, err := nodeManager.AccountManager() if err != nil { return "", "", err @@ -67,7 +67,7 @@ func CreateChildAccount(parentAddress, password string) (address, pubKey string, return "", "", ErrNoAccountSelected } - account, err := utils.MakeAddress(accountManager, parentAddress) + account, err := ParseAccountString(accountManager, parentAddress) if err != nil { return "", "", ErrAddressToAccountMappingFailure } @@ -128,13 +128,13 @@ func RecoverAccount(password, mnemonic string) (address, pubKey string, err erro // using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity, // all previous identities are removed). func SelectAccount(address, password string) error { - nodeManager := GetNodeManager() + nodeManager := NodeManagerInstance() accountManager, err := nodeManager.AccountManager() if err != nil { return err } - account, err := utils.MakeAddress(accountManager, address) + account, err := ParseAccountString(accountManager, address) if err != nil { return ErrAddressToAccountMappingFailure } @@ -169,7 +169,7 @@ func SelectAccount(address, password string) error { // Logout clears whisper identities func Logout() error { - nodeManager := GetNodeManager() + nodeManager := NodeManagerInstance() whisperService, err := nodeManager.WhisperService() if err != nil { return err @@ -195,7 +195,7 @@ func UnlockAccount(address, password string, seconds int) error { // importExtendedKey processes incoming extended key, extracts required info and creates corresponding account key. // Once account key is formed, that key is put (if not already) into keystore i.e. key is *encoded* into key file. func importExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) { - accountManager, err := GetNodeManager().AccountManager() + accountManager, err := NodeManagerInstance().AccountManager() if err != nil { return "", "", err } @@ -218,7 +218,7 @@ func importExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, p } func onAccountsListRequest(entities []accounts.Account) []accounts.Account { - nodeManager := GetNodeManager() + nodeManager := NodeManagerInstance() if nodeManager.SelectedAccount == nil { return []accounts.Account{} @@ -246,7 +246,7 @@ func onAccountsListRequest(entities []accounts.Account) []accounts.Account { // refreshSelectedAccount re-populates list of sub-accounts of the currently selected account (if any) func refreshSelectedAccount() { - nodeManager := GetNodeManager() + nodeManager := NodeManagerInstance() if nodeManager.SelectedAccount == nil { return @@ -273,7 +273,7 @@ func refreshSelectedAccount() { // that belong to the currently selected account. // The extKey is CKD#2 := root of sub-accounts of the main account func findSubAccounts(extKey *extkeys.ExtendedKey, subAccountIndex uint32) ([]accounts.Account, error) { - nodeManager := GetNodeManager() + nodeManager := NodeManagerInstance() accountManager, err := nodeManager.AccountManager() if err != nil { return []accounts.Account{}, err diff --git a/geth/accounts_test.go b/geth/accounts_test.go index 4f269d60c..8258f43fa 100644 --- a/geth/accounts_test.go +++ b/geth/accounts_test.go @@ -5,7 +5,6 @@ import ( "reflect" "testing" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/status-im/status-go/geth" @@ -18,7 +17,7 @@ func TestAccountsList(t *testing.T) { return } - les, err := geth.GetNodeManager().LightEthereumService() + les, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("expected LES service: %v", err) } @@ -130,7 +129,7 @@ func TestCreateChildAccount(t *testing.T) { geth.Logout() // to make sure that we start with empty account (which might get populated during previous tests) - accountManager, err := geth.GetNodeManager().AccountManager() + accountManager, err := geth.NodeManagerInstance().AccountManager() if err != nil { t.Error(err) return @@ -144,7 +143,7 @@ func TestCreateChildAccount(t *testing.T) { } t.Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) - account, err := utils.MakeAddress(accountManager, address) + account, err := geth.ParseAccountString(accountManager, address) if err != nil { t.Errorf("can not get account from address: %v", err) return @@ -217,7 +216,7 @@ func TestRecoverAccount(t *testing.T) { return } - accountManager, _ := geth.GetNodeManager().AccountManager() + accountManager, _ := geth.NodeManagerInstance().AccountManager() // create an account address, pubKey, mnemonic, err := geth.CreateAccount(newAccountPassword) @@ -238,7 +237,7 @@ func TestRecoverAccount(t *testing.T) { } // now test recovering, but make sure that account/key file is removed i.e. simulate recovering on a new device - account, err := utils.MakeAddress(accountManager, address) + account, err := geth.ParseAccountString(accountManager, address) if err != nil { t.Errorf("can not get account from address: %v", err) } @@ -284,7 +283,7 @@ func TestRecoverAccount(t *testing.T) { } // time to login with recovered data - whisperService, err := geth.GetNodeManager().WhisperService() + whisperService, err := geth.NodeManagerInstance().WhisperService() if err != nil { t.Errorf("whisper service not running: %v", err) } @@ -312,7 +311,7 @@ func TestAccountSelect(t *testing.T) { } // test to see if the account was injected in whisper - whisperService, err := geth.GetNodeManager().WhisperService() + whisperService, err := geth.NodeManagerInstance().WhisperService() if err != nil { t.Errorf("whisper service not running: %v", err) } @@ -377,7 +376,7 @@ func TestAccountLogout(t *testing.T) { return } - whisperService, err := geth.GetNodeManager().WhisperService() + whisperService, err := geth.NodeManagerInstance().WhisperService() if err != nil { t.Errorf("whisper service not running: %v", err) } diff --git a/geth/node.go b/geth/node.go index 1dcd090cd..5c02dfade 100644 --- a/geth/node.go +++ b/geth/node.go @@ -1,304 +1,249 @@ package geth -/* -#include -#include -extern bool StatusServiceSignalEvent( const char *jsonEvent ); -*/ -import "C" import ( - "encoding/json" "errors" - "flag" "fmt" - "io/ioutil" + "io" + "math/big" + "os" "path/filepath" + "reflect" "runtime" - "sync" + "strings" + "syscall" - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" - "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 + ClientIdentifier = "StatusIM" // Client identifier to advertise over the network + VersionMajor = 1 // Major version component of the current release + VersionMinor = 1 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release + VersionMeta = "stable" // Version metadata to append to the version string - RPCPort = 8545 // RPC port (replaced in unit tests) - NetworkPort = 30303 + RPCPort = 8545 // RPC port (replaced in unit tests) + NetworkPort = 30303 + MaxPeers = 25 + MaxLightPeers = 20 + MaxPendingPeers = 0 + + ProcessFileDescriptorLimit = uint64(2048) + DatabaseCacheSize = 128 // Megabytes of memory allocated to internal caching (min 16MB / database forced) EventNodeStarted = "node.started" ) +// Gas price settings var ( - ErrDataDirPreprocessingFailed = errors.New("failed to pre-process data directory") - ErrInvalidGethNode = errors.New("no running geth node detected") - ErrInvalidAccountManager = errors.New("could not retrieve account manager") - ErrInvalidWhisperService = errors.New("whisper service is unavailable") - ErrInvalidLightEthereumService = errors.New("can not retrieve LES service") - ErrInvalidClient = errors.New("RPC client is not properly initialized") - ErrInvalidJailedRequestQueue = errors.New("Jailed request queue is not properly initialized") - ErrNodeStartFailure = errors.New("could not create the in-memory node object") + GasPrice = new(big.Int).Mul(big.NewInt(20), common.Shannon) // Minimal gas price to accept for mining a transactions + GpoMinGasPrice = new(big.Int).Mul(big.NewInt(20), common.Shannon) // Minimum suggested gas price + GpoMaxGasPrice = new(big.Int).Mul(big.NewInt(500), common.Shannon) // Maximum suggested gas price + GpoFullBlockRatio = 80 // Full block threshold for gas price calculation (%) + GpobaseStepDown = 10 // Suggested gas price base step down ratio (1/1000) + GpobaseStepUp = 100 // Suggested gas price base step up ratio (1/1000) + GpobaseCorrectionFactor = 110 // Suggested gas price base correction factor (%) ) -type SelectedExtKey struct { - Address common.Address - AccountKey *accounts.Key - SubAccounts []accounts.Account -} - -type NodeManager struct { - currentNode *node.Node // currently running geth node - ctx *cli.Context // the CLI context used to start the geth node - lightEthereum *les.LightEthereum // LES service - accountManager *accounts.Manager // the account manager attached to the currentNode - jailedRequestQueue *JailedRequestQueue // bridge via which jail notifies node of incoming requests - SelectedAccount *SelectedExtKey // account that was processed during the last call to SelectAccount() - whisperService *whisper.Whisper // Whisper service - client *rpc.Client // RPC client - nodeStarted chan struct{} // channel to wait for node to start -} - +// default node configuration options var ( - UseTestnet = "true" // can be overridden via -ldflags '-X geth.UseTestnet' - nodeManagerInstance *NodeManager - createOnce sync.Once + UseTestnetFlag = "true" // to be overridden via -ldflags '-X geth.UseTestnetFlag' + UseTestnet = false ) -func NewNodeManager(datadir string, rpcport int) *NodeManager { - createOnce.Do(func() { - nodeManagerInstance = &NodeManager{ - jailedRequestQueue: NewJailedRequestsQueue(), - } - nodeManagerInstance.MakeNode(datadir, rpcport) - }) - - return nodeManagerInstance -} - -func GetNodeManager() *NodeManager { - return nodeManagerInstance -} - -// createAndStartNode creates a node entity and starts the -// node running locally exposing given RPC port -func CreateAndRunNode(datadir string, rpcport int) error { - nodeManager := NewNodeManager(datadir, rpcport) - - if nodeManager.HasNode() { - nodeManager.RunNode() - - <-nodeManager.nodeStarted // block until node is ready - return nil +func init() { + if UseTestnetFlag == "true" { // set at compile time, here we make sure to set corresponding boolean flag + UseTestnet = true } +} - return ErrNodeStartFailure +// node-related errors +var ( + ErrRLimitRaiseFailure = errors.New("failed to register the whisper service") + ErrDatabaseAccessFailure = errors.New("could not open database") + ErrChainConfigurationFailure = errors.New("could not make chain configuration") + ErrEthServiceRegistrationFailure = errors.New("failed to register the Ethereum service") + ErrSshServiceRegistrationFailure = errors.New("failed to register the Whisper service") + ErrLightEthRegistrationFailure = errors.New("failed to register the LES service") +) + +type Node struct { + geth *node.Node // reference to the running Geth node + started chan struct{} // channel to wait for node to start +} + +// Inited checks whether status node has been properly initialized +func (n *Node) Inited() bool { + return n != nil && n.geth != nil } // MakeNode create a geth node entity -func (m *NodeManager) MakeNode(datadir string, rpcport int) *node.Node { - // TODO remove admin rpcapi flag - set := flag.NewFlagSet("test", 0) - set.Bool("lightkdf", true, "Reduce key-derivation RAM & CPU usage at some expense of KDF strength") - set.Bool("shh", true, "whisper") - set.Bool("light", true, "disable eth") - if UseTestnet == "true" { - set.Bool("testnet", true, "light test network") - } - set.Bool("rpc", true, "enable rpc") - set.String("rpcaddr", "localhost", "host for RPC") - set.Int("rpcport", rpcport, "rpc port") - set.String("rpccorsdomain", "*", "allow all domains") - set.String("verbosity", "3", "verbosity level") - set.String("rpcapi", "db,eth,net,web3,shh,personal,admin", "rpc api(s)") - set.String("datadir", datadir, "data directory for geth") - set.String("logdir", datadir, "log dir for glog") - set.Int("port", NetworkPort, "network listening port") - m.ctx = cli.NewContext(nil, set, nil) +func MakeNode(dataDir string, rpcPort int) *Node { + glog.CopyStandardLogTo("INFO") + glog.SetToStderr(true) - utils.DebugSetup(m.ctx) - - // create node and start requested protocols - m.currentNode = utils.MakeNode(m.ctx, clientIdentifier, "") - utils.RegisterEthService(m.ctx, m.currentNode, makeDefaultExtra()) - - // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. - shhEnabled := m.ctx.GlobalBool(utils.WhisperEnabledFlag.Name) - shhAutoEnabled := !m.ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && m.ctx.GlobalIsSet(utils.DevModeFlag.Name) - if shhEnabled || shhAutoEnabled { - utils.RegisterShhService(m.currentNode) + bootstrapNodes := params.MainnetBootnodes + if UseTestnet { + dataDir = filepath.Join(dataDir, "testnet") + bootstrapNodes = params.TestnetBootnodes } - m.accountManager = m.currentNode.AccountManager() - m.nodeStarted = make(chan struct{}) - - return m.currentNode -} - -// StartNode starts a geth node entity -func (m *NodeManager) RunNode() { - go func() { - utils.StartNode(m.currentNode) - - if m.currentNode.AccountManager() == nil { - glog.V(logger.Warn).Infoln("cannot get account manager") - } - if err := m.currentNode.Service(&m.whisperService); err != nil { - glog.V(logger.Warn).Infoln("cannot get whisper service:", err) - } - if err := m.currentNode.Service(&m.lightEthereum); err != nil { - glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err) - } - - // setup handlers - m.lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest) - m.lightEthereum.StatusBackend.SetAccountsFilterHandler(onAccountsListRequest) - m.lightEthereum.StatusBackend.SetTransactionReturnHandler(onSendTransactionReturn) - - var err error - m.client, err = m.currentNode.Attach() - if err != nil { - glog.V(logger.Warn).Infoln("cannot get RPC client service:", ErrInvalidClient) - } - - // @TODO Remove after LES supports discover out of box - m.populateStaticPeers() - - m.onNodeStarted() // node started, notify listeners - m.currentNode.Wait() - }() -} - -func (m *NodeManager) onNodeStarted() { - // notify local listener - m.nodeStarted <- struct{}{} - close(m.nodeStarted) - - // send signal up to native app - event := GethEvent{ - Type: EventNodeStarted, - Event: struct{}{}, + // configure required node (should you need to update node's config, e.g. add bootstrap nodes, see node.Config) + config := &node.Config{ + DataDir: dataDir, + UseLightweightKDF: true, + Name: ClientIdentifier, + Version: fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionMeta), + NoDiscovery: true, + DiscoveryV5: true, + DiscoveryV5Addr: fmt.Sprintf(":%d", NetworkPort+1), + BootstrapNodes: bootstrapNodes, + BootstrapNodesV5: params.DiscoveryV5Bootnodes, + ListenAddr: fmt.Sprintf(":%d", NetworkPort), + MaxPeers: MaxPeers, + MaxPendingPeers: MaxPendingPeers, + HTTPHost: node.DefaultHTTPHost, + HTTPPort: rpcPort, + HTTPCors: "*", + HTTPModules: strings.Split("db,eth,net,web3,shh,personal,admin", ","), // TODO remove "admin" on main net } - body, _ := json.Marshal(&event) - C.StatusServiceSignalEvent(C.CString(string(body))) -} - -func (m *NodeManager) AddPeer(url string) (bool, error) { - if m == nil || !m.HasNode() { - return false, ErrInvalidGethNode - } - - server := m.currentNode.Server() - if server == nil { - return false, errors.New("node not started") - } - // Try to add the url as a static peer and return - parsedNode, err := discover.ParseNode(url) + stack, err := node.New(config) if err != nil { - return false, fmt.Errorf("invalid enode: %v", err) - } - server.AddPeer(parsedNode) - - return true, nil -} - -func (m *NodeManager) HasNode() bool { - return m != nil && m.currentNode != nil -} - -func (m *NodeManager) HasAccountManager() bool { - return m.accountManager != nil -} - -func (m *NodeManager) AccountManager() (*accounts.Manager, error) { - if m == nil || !m.HasNode() { - return nil, ErrInvalidGethNode + Fatalf(ErrNodeMakeFailure) } - if !m.HasAccountManager() { - return nil, ErrInvalidAccountManager + // start Ethereum service + if err := activateEthService(stack, makeDefaultExtra()); err != nil { + Fatalf(fmt.Errorf("%v: %v", ErrEthServiceRegistrationFailure, err)) } - return m.accountManager, nil + // start Whisper service + if err := activateShhService(stack); err != nil { + Fatalf(fmt.Errorf("%v: %v", ErrSshServiceRegistrationFailure, err)) + } + + return &Node{ + geth: stack, + started: make(chan struct{}), + } } -func (m *NodeManager) HasWhisperService() bool { - return m.whisperService != nil +// activateEthService configures and registers the eth.Ethereum service with a given node. +func activateEthService(stack *node.Node, extra []byte) error { + ethConf := ð.Config{ + Etherbase: common.Address{}, + ChainConfig: makeChainConfig(stack), + FastSync: false, + LightMode: true, + LightServ: 60, + LightPeers: MaxLightPeers, + MaxPeers: MaxPeers, + DatabaseCache: DatabaseCacheSize, + DatabaseHandles: makeDatabaseHandles(), + NetworkId: 1, // Olympic + MinerThreads: runtime.NumCPU(), + GasPrice: GasPrice, + GpoMinGasPrice: GpoMinGasPrice, + GpoMaxGasPrice: GpoMaxGasPrice, + GpoFullBlockRatio: GpoFullBlockRatio, + GpobaseStepDown: GpobaseStepDown, + GpobaseStepUp: GpobaseStepUp, + GpobaseCorrectionFactor: GpobaseCorrectionFactor, + SolcPath: "solc", + AutoDAG: false, + } + + if UseTestnet { + ethConf.NetworkId = 3 + ethConf.Genesis = core.DefaultTestnetGenesisBlock() + } + + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return les.New(ctx, ethConf) + }); err != nil { + return fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err) + } + + return nil } -func (m *NodeManager) WhisperService() (*whisper.Whisper, error) { - if m == nil || !m.HasNode() { - return nil, ErrInvalidGethNode +// activateShhService configures Whisper and adds it to the given node. +func activateShhService(stack *node.Node) error { + serviceConstructor := func(*node.ServiceContext) (node.Service, error) { + return whisper.New(), nil + } + if err := stack.Register(serviceConstructor); err != nil { + return err } - if !m.HasWhisperService() { - return nil, ErrInvalidWhisperService - } - - return m.whisperService, nil + return nil } -func (m *NodeManager) HasLightEthereumService() bool { - return m.lightEthereum != nil +// makeChainConfig reads the chain configuration from the database in the datadir. +func makeChainConfig(stack *node.Node) *params.ChainConfig { + config := new(params.ChainConfig) + + if UseTestnet { + config = params.TestnetChainConfig + } else { + // Homestead fork + config.HomesteadBlock = params.MainNetHomesteadBlock + // DAO fork + config.DAOForkBlock = params.MainNetDAOForkBlock + config.DAOForkSupport = true + + // DoS reprice fork + config.EIP150Block = params.MainNetHomesteadGasRepriceBlock + config.EIP150Hash = params.MainNetHomesteadGasRepriceHash + + // DoS state cleanup fork + config.EIP155Block = params.MainNetSpuriousDragon + config.EIP158Block = params.MainNetSpuriousDragon + config.ChainId = params.MainNetChainID + } + + return config } -func (m *NodeManager) LightEthereumService() (*les.LightEthereum, error) { - if m == nil || !m.HasNode() { - return nil, ErrInvalidGethNode +// makeDatabaseHandles makes sure that enough file descriptors are available to the process +// (and returns half of them for node's database to use) +func makeDatabaseHandles() int { + // current limit + var limit syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + Fatalf(err) } - if !m.HasLightEthereumService() { - return nil, ErrInvalidLightEthereumService + // increase limit + limit.Cur = limit.Max + if limit.Cur > ProcessFileDescriptorLimit { + limit.Cur = ProcessFileDescriptorLimit + } + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + Fatalf(err) } - return m.lightEthereum, nil -} - -func (m *NodeManager) HasRPCClient() bool { - return m.client != nil -} - -func (m *NodeManager) RPCClient() (*rpc.Client, error) { - if m == nil || !m.HasNode() { - return nil, ErrInvalidGethNode + // re-query limit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + Fatalf(err) } - if !m.HasRPCClient() { - return nil, ErrInvalidClient + // cap limit + if limit.Cur > ProcessFileDescriptorLimit { + limit.Cur = ProcessFileDescriptorLimit } - return m.client, nil -} - -func (m *NodeManager) HasJailedRequestQueue() bool { - return m.jailedRequestQueue != nil -} - -func (m *NodeManager) JailedRequestQueue() (*JailedRequestQueue, error) { - if m == nil || !m.HasNode() { - return nil, ErrInvalidGethNode - } - - if !m.HasJailedRequestQueue() { - return nil, ErrInvalidJailedRequestQueue - } - - return m.jailedRequestQueue, nil + return int(limit.Cur) / 2 } func makeDefaultExtra() []byte { @@ -307,7 +252,7 @@ func makeDefaultExtra() []byte { Name string GoVersion string Os string - }{uint(versionMajor<<16 | versionMinor<<8 | versionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} + }{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) @@ -322,18 +267,22 @@ func makeDefaultExtra() []byte { return extra } -func (m *NodeManager) populateStaticPeers() { - // manually add static nodes (LES auto-discovery is not stable yet) - configFile, err := ioutil.ReadFile(filepath.Join("../data", "static-nodes.json")) - if err != nil { - return - } - var enodes []string - if err = json.Unmarshal(configFile, &enodes); err != nil { - return +func Fatalf(reason interface{}, args ...interface{}) { + // decide on output stream + w := io.MultiWriter(os.Stdout, os.Stderr) + outf, _ := os.Stdout.Stat() + errf, _ := os.Stderr.Stat() + if outf != nil && errf != nil && os.SameFile(outf, errf) { + w = os.Stderr } - for _, enode := range enodes { - m.AddPeer(enode) + // find out whether error or string has been passed as a reason + r := reflect.ValueOf(reason) + if r.Kind() == reflect.String { + fmt.Fprintf(w, "Fatal Failure: "+reason.(string)+"\n", args) + } else { + fmt.Fprintf(w, "Fatal Failure: %v\n", reason.(error)) } + + os.Exit(1) } diff --git a/geth/node_manager.go b/geth/node_manager.go new file mode 100644 index 000000000..79c3f80a7 --- /dev/null +++ b/geth/node_manager.go @@ -0,0 +1,289 @@ +package geth + +/* +#include +#include +extern bool StatusServiceSignalEvent( const char *jsonEvent ); +*/ +import "C" +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "os/signal" + "path/filepath" + "sync" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/rpc" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" +) + +// SelectedExtKey is a container for currently selected (logged in) account +type SelectedExtKey struct { + Address common.Address + AccountKey *accounts.Key + SubAccounts []accounts.Account +} + +// NodeManager manages Status node (which abstracts contained geth node) +type NodeManager struct { + node *Node // reference to Status node + services *NodeServiceStack // default stack of services running on geth node + SelectedAccount *SelectedExtKey // account that was processed during the last call to SelectAccount() +} + +// NodeServiceStack contains "standard" node services (which are always available) +type NodeServiceStack struct { + lightEthereum *les.LightEthereum // LES service + whisperService *whisper.Whisper // Whisper service + rpcClient *rpc.Client // RPC client + jailedRequestQueue *JailedRequestQueue // bridge via which jail notifies node of incoming requests +} + +var ( + ErrDataDirPreprocessingFailed = errors.New("failed to pre-process data directory") + ErrInvalidGethNode = errors.New("no running geth node detected") + ErrInvalidAccountManager = errors.New("could not retrieve account manager") + ErrInvalidWhisperService = errors.New("whisper service is unavailable") + ErrInvalidLightEthereumService = errors.New("can not retrieve LES service") + ErrInvalidClient = errors.New("RPC client is not properly initialized") + ErrInvalidJailedRequestQueue = errors.New("jailed request queue is not properly initialized") + ErrNodeMakeFailure = errors.New("error creating p2p node") + ErrNodeStartFailure = errors.New("error starting p2p node") +) + +var ( + nodeManagerInstance *NodeManager + createOnce sync.Once +) + +// CreateAndRunNode creates and starts running Geth node locally (exposing given RPC port along the way) +func CreateAndRunNode(dataDir string, rpcPort int) error { + nodeManager := NewNodeManager(dataDir, rpcPort) + + if nodeManager.NodeInited() { + nodeManager.RunNode() + + <-nodeManager.node.started // block until node is ready + return nil + } + + return ErrNodeStartFailure +} + +// NewNodeManager makes new instance of node manager +func NewNodeManager(dataDir string, rpcPort int) *NodeManager { + createOnce.Do(func() { + nodeManagerInstance = &NodeManager{ + services: &NodeServiceStack{ + jailedRequestQueue: NewJailedRequestsQueue(), + }, + } + nodeManagerInstance.node = MakeNode(dataDir, rpcPort) + }) + + return nodeManagerInstance +} + +// NodeManagerInstance exposes node manager instance +func NodeManagerInstance() *NodeManager { + return nodeManagerInstance +} + +// RunNode starts Geth node +func (m *NodeManager) RunNode() { + go func() { + m.StartNode() + + if _, err := m.AccountManager(); err != nil { + glog.V(logger.Warn).Infoln(ErrInvalidAccountManager) + } + if err := m.node.geth.Service(&m.services.whisperService); err != nil { + glog.V(logger.Warn).Infoln("cannot get whisper service:", err) + } + if err := m.node.geth.Service(&m.services.lightEthereum); err != nil { + glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err) + } + + // setup handlers + lightEthereum, err := m.LightEthereumService() + if err != nil { + panic("service stack misses LES") + } + + lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest) + lightEthereum.StatusBackend.SetAccountsFilterHandler(onAccountsListRequest) + lightEthereum.StatusBackend.SetTransactionReturnHandler(onSendTransactionReturn) + + m.services.rpcClient, err = m.node.geth.Attach() + if err != nil { + glog.V(logger.Warn).Infoln("cannot get RPC client service:", ErrInvalidClient) + } + + // @TODO Remove after LES supports discover out of box + m.populateStaticPeers() + + m.onNodeStarted() // node started, notify listeners + m.node.geth.Wait() + }() +} + +// StartNode starts running P2P node +func (m *NodeManager) StartNode() { + if m == nil || !m.NodeInited() { + panic(ErrInvalidGethNode) + } + + if err := m.node.geth.Start(); err != nil { + panic(fmt.Sprintf("%v: %v", ErrNodeStartFailure, err)) + } + + // allow interrupting running nodes + go func() { + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt) + defer signal.Stop(sigc) + <-sigc + glog.V(logger.Info).Infoln("Got interrupt, shutting down...") + go m.node.geth.Stop() + for i := 3; i > 0; i-- { + <-sigc + if i > 1 { + glog.V(logger.Info).Infof("Already shutting down, interrupt %d more times for panic.", i-1) + } + } + panic("interrupted!") + }() +} + +// HasNode checks whether manager has initialized node attached +func (m *NodeManager) NodeInited() bool { + if m == nil || !m.node.Inited() { + return false + } + + return true +} + +// AccountManager exposes reference to accounts manager +func (m *NodeManager) AccountManager() (*accounts.Manager, error) { + if m == nil || !m.NodeInited() { + return nil, ErrInvalidGethNode + } + + return m.node.geth.AccountManager(), nil +} + +// LightEthereumService exposes LES +func (m *NodeManager) LightEthereumService() (*les.LightEthereum, error) { + if m == nil || !m.NodeInited() { + return nil, ErrInvalidGethNode + } + + if m.services.lightEthereum == nil { + return nil, ErrInvalidLightEthereumService + } + + return m.services.lightEthereum, nil +} + +// WhisperService exposes Whisper service +func (m *NodeManager) WhisperService() (*whisper.Whisper, error) { + if m == nil || !m.NodeInited() { + return nil, ErrInvalidGethNode + } + + if m.services.whisperService == nil { + return nil, ErrInvalidWhisperService + } + + return m.services.whisperService, nil +} + +// RPCClient exposes Geth's RPC client +func (m *NodeManager) RPCClient() (*rpc.Client, error) { + if m == nil || !m.NodeInited() { + return nil, ErrInvalidGethNode + } + + if m.services.rpcClient == nil { + return nil, ErrInvalidClient + } + + return m.services.rpcClient, nil +} + +// JailedRequestQueue exposes reference to queue of jailed requests +func (m *NodeManager) JailedRequestQueue() (*JailedRequestQueue, error) { + if m == nil || !m.NodeInited() { + return nil, ErrInvalidGethNode + } + + if m.services.jailedRequestQueue == nil { + return nil, ErrInvalidJailedRequestQueue + } + + return m.services.jailedRequestQueue, nil +} + +// AddPeer adds new peer node +func (m *NodeManager) AddPeer(url string) (bool, error) { + if m == nil || !m.NodeInited() { + return false, ErrInvalidGethNode + } + + server := m.node.geth.Server() + if server == nil { + return false, ErrInvalidGethNode + } + + // Try to add the url as a static peer and return + parsedNode, err := discover.ParseNode(url) + if err != nil { + return false, fmt.Errorf("invalid enode: %v", err) + } + server.AddPeer(parsedNode) + + return true, nil +} + +// onNodeStarted sends upward notification letting the app know that Geth node is ready to be used +func (m *NodeManager) onNodeStarted() { + // notify local listener + m.node.started <- struct{}{} + close(m.node.started) + + // send signal up to native app + event := GethEvent{ + Type: EventNodeStarted, + Event: struct{}{}, + } + + body, _ := json.Marshal(&event) + C.StatusServiceSignalEvent(C.CString(string(body))) +} + +// populateStaticPeers connects current node with our publicly available LES cluster +func (m *NodeManager) populateStaticPeers() { + // manually add static nodes (LES auto-discovery is not stable yet) + configFile, err := ioutil.ReadFile(filepath.Join("../data", "static-nodes.json")) + if err != nil { + return + } + var enodes []string + if err = json.Unmarshal(configFile, &enodes); err != nil { + return + } + + for _, enode := range enodes { + m.AddPeer(enode) + } +} diff --git a/geth/node_test.go b/geth/node_manager_test.go similarity index 100% rename from geth/node_test.go rename to geth/node_manager_test.go diff --git a/geth/txqueue.go b/geth/txqueue.go index 72a93caae..ba2fd5e9c 100644 --- a/geth/txqueue.go +++ b/geth/txqueue.go @@ -92,7 +92,7 @@ func sendTransactionErrorCode(err error) string { } func CompleteTransaction(id, password string) (common.Hash, error) { - lightEthereum, err := GetNodeManager().LightEthereumService() + lightEthereum, err := NodeManagerInstance().LightEthereumService() if err != nil { return common.Hash{}, err } @@ -125,7 +125,7 @@ func CompleteTransactions(ids, password string) map[string]RawCompleteTransactio } func DiscardTransaction(id string) error { - lightEthereum, err := GetNodeManager().LightEthereumService() + lightEthereum, err := NodeManagerInstance().LightEthereumService() if err != nil { return err } @@ -195,7 +195,7 @@ func (q *JailedRequestQueue) PostProcessRequest(vm *otto.Otto, req RPCCall, mess func (q *JailedRequestQueue) ProcessSendTransactionRequest(vm *otto.Otto, req RPCCall) (common.Hash, error) { // obtain status backend from LES service - lightEthereum, err := GetNodeManager().LightEthereumService() + lightEthereum, err := NodeManagerInstance().LightEthereumService() if err != nil { return common.Hash{}, err } diff --git a/geth/txqueue_test.go b/geth/txqueue_test.go index b49c21eab..77bc87c8e 100644 --- a/geth/txqueue_test.go +++ b/geth/txqueue_test.go @@ -21,7 +21,7 @@ func TestQueuedTransactions(t *testing.T) { } // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return @@ -91,7 +91,7 @@ func TestDoubleCompleteQueuedTransactions(t *testing.T) { } // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return @@ -212,7 +212,7 @@ func TestDiscardQueuedTransactions(t *testing.T) { } // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return @@ -327,7 +327,7 @@ func TestCompleteMultipleQueuedTransactions(t *testing.T) { } // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return @@ -452,7 +452,7 @@ func TestDiscardMultipleQueuedTransactions(t *testing.T) { } // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return @@ -653,7 +653,7 @@ func TestEvictionOfQueuedTransactions(t *testing.T) { } // obtain reference to status backend - lightEthereum, err := geth.GetNodeManager().LightEthereumService() + lightEthereum, err := geth.NodeManagerInstance().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) return diff --git a/geth/utils.go b/geth/utils.go index 6dfe9a4d7..51aeabfdb 100644 --- a/geth/utils.go +++ b/geth/utils.go @@ -12,10 +12,11 @@ import ( "os" "path" "path/filepath" + "strconv" "sync" "time" - "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -86,8 +87,8 @@ func PrepareTestNode() (err error) { muPrepareTestNode.Lock() defer muPrepareTestNode.Unlock() - manager := GetNodeManager() - if manager.HasNode() { + manager := NodeManagerInstance() + if manager.NodeInited() { return nil } @@ -117,17 +118,17 @@ func PrepareTestNode() (err error) { // internally once.Do() is used, so call below is thread-safe CreateAndRunNode(dataDir, 8546) // to avoid conflicts with running react-native app, run on different port - manager = GetNodeManager() - if !manager.HasNode() { + manager = NodeManagerInstance() + if !manager.NodeInited() { panic(ErrInvalidGethNode) } - if !manager.HasRPCClient() { + if service, err := manager.RPCClient(); err != nil || service == nil { panic(ErrInvalidGethNode) } - if !manager.HasWhisperService() { + if service, err := manager.WhisperService(); err != nil || service == nil { panic(ErrInvalidGethNode) } - if !manager.HasLightEthereumService() { + if service, err := manager.LightEthereumService(); err != nil || service == nil { panic(ErrInvalidGethNode) } @@ -181,12 +182,12 @@ func PanicAfter(waitSeconds time.Duration, abort chan struct{}, desc string) { } func FromAddress(accountAddress string) common.Address { - accountManager, err := GetNodeManager().AccountManager() + accountManager, err := NodeManagerInstance().AccountManager() if err != nil { return common.Address{} } - from, err := utils.MakeAddress(accountManager, accountAddress) + from, err := ParseAccountString(accountManager, accountAddress) if err != nil { return common.Address{} } @@ -195,15 +196,31 @@ func FromAddress(accountAddress string) common.Address { } func ToAddress(accountAddress string) *common.Address { - accountManager, err := GetNodeManager().AccountManager() + accountManager, err := NodeManagerInstance().AccountManager() if err != nil { return nil } - to, err := utils.MakeAddress(accountManager, accountAddress) + to, err := ParseAccountString(accountManager, accountAddress) if err != nil { return nil } return &to.Address } + +// parseAccount parses hex encoded string or key index in the accounts key store +// and converts it to an internal account representation. +func ParseAccountString(accman *accounts.Manager, account string) (accounts.Account, error) { + // valid address, convert to account + if common.IsHexAddress(account) { + return accounts.Account{Address: common.HexToAddress(account)}, nil + } + // valid key index, return account referenced by that key + index, err := strconv.Atoi(account) + if err != nil { + return accounts.Account{}, ErrInvalidAccountAddressOrKey + } + + return accman.AccountByIndex(index) +} diff --git a/geth/whisper.go b/geth/whisper.go index 19125e8da..9fb2e5902 100644 --- a/geth/whisper.go +++ b/geth/whisper.go @@ -36,7 +36,7 @@ func onWhisperMessage(message *whisper.Message) { } func AddWhisperFilter(args whisper.NewFilterArgs) int { - whisperService, err := GetNodeManager().WhisperService() + whisperService, err := NodeManagerInstance().WhisperService() if err != nil { return -1 } @@ -54,7 +54,7 @@ func AddWhisperFilter(args whisper.NewFilterArgs) int { } func RemoveWhisperFilter(idFilter int) { - whisperService, err := GetNodeManager().WhisperService() + whisperService, err := NodeManagerInstance().WhisperService() if err != nil { return } diff --git a/geth/whisper_test.go b/geth/whisper_test.go index 57aff0ad8..003590bb5 100644 --- a/geth/whisper_test.go +++ b/geth/whisper_test.go @@ -18,7 +18,7 @@ func TestWhisperMessaging(t *testing.T) { return } - whisperService, err := geth.GetNodeManager().WhisperService() + whisperService, err := geth.NodeManagerInstance().WhisperService() if err != nil { t.Errorf("whisper service not running: %v", err) } diff --git a/jail/jail.go b/jail/jail.go index 515b164dd..19b008191 100644 --- a/jail/jail.go +++ b/jail/jail.go @@ -283,8 +283,8 @@ func (jail *Jail) RPCClient() (*rpc.Client, error) { return jail.client, nil } - nodeManager := geth.GetNodeManager() - if !nodeManager.HasNode() { + nodeManager := geth.NodeManagerInstance() + if !nodeManager.NodeInited() { return nil, geth.ErrInvalidGethNode } @@ -307,8 +307,8 @@ func (jail *Jail) RequestQueue() (*geth.JailedRequestQueue, error) { return jail.requestQueue, nil } - nodeManager := geth.GetNodeManager() - if !nodeManager.HasNode() { + nodeManager := geth.NodeManagerInstance() + if !nodeManager.NodeInited() { return nil, geth.ErrInvalidGethNode } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go index 00a8cd3e9..06bd13cae 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go @@ -225,7 +225,11 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM from.SetBalance(common.MaxBig) // Execute the call. msg := callmsg{call} - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) + + evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain) + // Create a new environment which holds all relevant information + // about the transaction and calling mechanisms. + vmenv := vm.NewEnvironment(evmContext, statedb, chainConfig, vm.Config{}) gaspool := new(core.GasPool).AddGas(common.MaxBig) ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() return ret, gasUsed, err diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/addrcache.go b/vendor/github.com/ethereum/go-ethereum/accounts/addrcache.go index 0a904f788..a99f23606 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/addrcache.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/addrcache.go @@ -225,7 +225,7 @@ func (ac *addrCache) scan() ([]Account, error) { buf = new(bufio.Reader) addrs []Account keyJSON struct { - Address common.Address `json:"address"` + Address string `json:"address"` } ) for _, fi := range files { @@ -241,15 +241,16 @@ func (ac *addrCache) scan() ([]Account, error) { } buf.Reset(fd) // Parse the address. - keyJSON.Address = common.Address{} + keyJSON.Address = "" err = json.NewDecoder(buf).Decode(&keyJSON) + addr := common.HexToAddress(keyJSON.Address) switch { case err != nil: glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err) - case (keyJSON.Address == common.Address{}): + case (addr == common.Address{}): glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path) default: - addrs = append(addrs, Account{Address: keyJSON.Address, File: path}) + addrs = append(addrs, Account{Address: addr, File: path}) } fd.Close() } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/watch.go b/vendor/github.com/ethereum/go-ethereum/accounts/watch.go index 309e4d458..472be2df7 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/watch.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/watch.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build darwin,!ios freebsd linux,!arm64 netbsd solaris windows +// +build darwin,!ios freebsd linux,!arm64 netbsd solaris package accounts diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/watch_fallback.go b/vendor/github.com/ethereum/go-ethereum/accounts/watch_fallback.go index 7b5e221df..bf971cb1b 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/watch_fallback.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/watch_fallback.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build ios linux,arm64 !darwin,!freebsd,!linux,!netbsd,!solaris,!windows +// +build ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris // This is the fallback implementation of directory watching. // It is used on unsupported platforms. diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go index abecac3d8..9b5ba1936 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" ) func main() { @@ -39,6 +40,7 @@ func main() { 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:)") + netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)") runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") nodeKey *ecdsa.PrivateKey @@ -81,12 +83,20 @@ func main() { os.Exit(0) } + var restrictList *netutil.Netlist + if *netrestrict != "" { + restrictList, err = netutil.ParseNetlist(*netrestrict) + if err != nil { + utils.Fatalf("-netrestrict: %v", err) + } + } + if *runv5 { - if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { + if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil { utils.Fatalf("%v", err) } } else { - if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { + if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil { utils.Fatalf("%v", err) } } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go index b2f14a4a9..4bb2ca04a 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go @@ -23,6 +23,7 @@ import ( "os" "runtime" "strconv" + "strings" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/cmd/utils" @@ -38,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/swarm" bzzapi "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/network" "gopkg.in/urfave/cli.v1" ) @@ -61,17 +63,22 @@ var ( Name: "bzzport", Usage: "Swarm local http api port", } + SwarmNetworkIdFlag = cli.IntFlag{ + Name: "bzznetworkid", + Usage: "Network identifier (integer, default 322=swarm testnet)", + Value: network.NetworkId, + } SwarmConfigPathFlag = cli.StringFlag{ Name: "bzzconfig", Usage: "Swarm config file path (datadir/bzz)", } - SwarmSwapDisabled = cli.BoolFlag{ - Name: "bzznoswap", - Usage: "Swarm SWAP disabled (default false)", + SwarmSwapEnabled = cli.BoolFlag{ + Name: "swap", + Usage: "Swarm SWAP enabled (default false)", } - SwarmSyncDisabled = cli.BoolFlag{ - Name: "bzznosync", - Usage: "Swarm Syncing disabled (default false)", + SwarmSyncEnabled = cli.BoolTFlag{ + Name: "sync", + Usage: "Swarm Syncing enabled (default true)", } EthAPI = cli.StringFlag{ Name: "ethapi", @@ -86,6 +93,7 @@ func init() { // Override flag defaults so bzzd can run alongside geth. utils.ListenPortFlag.Value = 30399 utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"} + utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3" // Set up the cli app. app.Commands = nil @@ -96,20 +104,24 @@ func init() { utils.BootnodesFlag, utils.KeyStoreDirFlag, utils.ListenPortFlag, - utils.MaxPeersFlag, - utils.NATFlag, + utils.NoDiscoverFlag, + utils.DiscoveryV5Flag, + utils.NetrestrictFlag, utils.NodeKeyFileFlag, utils.NodeKeyHexFlag, + utils.MaxPeersFlag, + utils.NATFlag, utils.IPCDisabledFlag, utils.IPCApiFlag, utils.IPCPathFlag, // bzzd-specific flags EthAPI, SwarmConfigPathFlag, - SwarmSwapDisabled, - SwarmSyncDisabled, + SwarmSwapEnabled, + SwarmSyncEnabled, SwarmPortFlag, SwarmAccountFlag, + SwarmNetworkIdFlag, ChequebookAddrFlag, } app.Flags = append(app.Flags, debug.Flags...) @@ -137,7 +149,8 @@ func bzzd(ctx *cli.Context) error { // Add bootnodes as initial peers. if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { - injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name)) + bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",") + injectBootnodes(stack.Server(), bootnodes) } else { injectBootnodes(stack.Server(), defaultBootnodes) } @@ -154,7 +167,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { if bzzdir == "" { bzzdir = stack.InstanceDir() } - bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey) + bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name)) if err != nil { utils.Fatalf("unable to configure swarm: %v", err) } @@ -162,16 +175,18 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { if len(bzzport) > 0 { bzzconfig.Port = bzzport } - swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name) - syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name) + swapEnabled := ctx.GlobalBool(SwarmSwapEnabled.Name) + syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabled.Name) ethapi := ctx.GlobalString(EthAPI.Name) - if ethapi == "" { - utils.Fatalf("Option %q must not be empty", EthAPI.Name) - } boot := func(ctx *node.ServiceContext) (node.Service, error) { - client, err := ethclient.Dial(ethapi) + var client *ethclient.Client + if ethapi == "" { + err = fmt.Errorf("use ethapi flag to connect to a an eth client and talk to the blockchain") + } else { + client, err = ethclient.Dial(ethapi) + } if err != nil { utils.Fatalf("Can't connect: %v", err) } @@ -240,6 +255,7 @@ func injectBootnodes(srv *p2p.Server, nodes []string) { n, err := discover.ParseNode(url) if err != nil { glog.Errorf("invalid bootnode %q", err) + continue } srv.AddPeer(n) } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go index 2c4329fa5..993dd7659 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go @@ -20,21 +20,18 @@ package main import ( "fmt" "io/ioutil" - "math/big" "os" - "runtime" + goruntime "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/core/vm/runtime" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/params" "gopkg.in/urfave/cli.v1" ) @@ -129,13 +126,6 @@ func run(ctx *cli.Context) error { logger := vm.NewStructLogger(nil) - 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), - Tracer: logger, - }) - tstart := time.Now() var ( @@ -168,25 +158,30 @@ func run(ctx *cli.Context) error { if ctx.GlobalBool(CreateFlag.Name) { input := append(code, 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)), - ) + ret, _, err = runtime.Create(input, &runtime.Config{ + Origin: sender.Address(), + State: statedb, + GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)), + GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)), + Value: common.Big(ctx.GlobalString(ValueFlag.Name)), + EVMConfig: vm.Config{ + Tracer: logger, + }, + }) } else { receiver := statedb.CreateAccount(common.StringToAddress("receiver")) - receiver.SetCode(crypto.Keccak256Hash(code), code) - 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)), - ) + + ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{ + Origin: sender.Address(), + State: statedb, + GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)), + GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)), + Value: common.Big(ctx.GlobalString(ValueFlag.Name)), + EVMConfig: vm.Config{ + Tracer: logger, + }, + }) } vmdone := time.Since(tstart) @@ -197,8 +192,8 @@ func run(ctx *cli.Context) error { vm.StdErrFormat(logger.StructLogs()) if ctx.GlobalBool(SysStatFlag.Name) { - var mem runtime.MemStats - runtime.ReadMemStats(&mem) + var mem goruntime.MemStats + goruntime.ReadMemStats(&mem) fmt.Printf("vm took %v\n", vmdone) fmt.Printf(`alloc: %d tot alloc: %d @@ -223,87 +218,3 @@ func main() { 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()), - } - - env.evm = vm.New(env, cfg) - return env -} - -// ruleSet implements vm.ChainConfig and will always default to the homestead rule set. -type ruleSet struct{} - -func (ruleSet) IsHomestead(*big.Int) bool { return true } -func (ruleSet) GasTable(*big.Int) params.GasTable { - return params.GasTableHomesteadGasRepriceFork -} - -func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() } -func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) } -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) 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) -} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go index 1a7a9eb18..c77bd554c 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go @@ -32,6 +32,7 @@ import ( "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" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/trie" "github.com/syndtr/goleveldb/leveldb/util" @@ -39,6 +40,18 @@ import ( ) var ( + initCommand = cli.Command{ + Action: initGenesis, + Name: "init", + Usage: "Bootstrap and initialize a new genesis block", + ArgsUsage: "", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +The init command initializes a new genesis block and definition for the network. +This is a destructive action and changes the network in which you will be +participating. +`, + } importCommand = cli.Command{ Action: importChain, Name: "import", @@ -95,6 +108,30 @@ Use "ethereum dump 0" to dump the genesis block. } ) +// 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") + } + + stack := makeFullNode(ctx) + chaindb := utils.MakeChainDatabase(ctx, stack) + + 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 +} + func importChain(ctx *cli.Context) error { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go index b6928a1c8..332e1ae8d 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go @@ -20,26 +20,23 @@ package main import ( "encoding/hex" "fmt" - "io/ioutil" "os" - "path/filepath" "runtime" - "strconv" "strings" "time" - "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/contracts/release" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" "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/params" + "github.com/ethereum/go-ethereum/rlp" "gopkg.in/urfave/cli.v1" ) @@ -62,59 +59,26 @@ func init() { app.HideVersion = true // we have a command to print the version app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" app.Commands = []cli.Command{ + // See chaincmd.go: + initCommand, importCommand, exportCommand, upgradedbCommand, removedbCommand, dumpCommand, + // See monitorcmd.go: monitorCommand, + // See accountcmd.go: accountCommand, walletCommand, + // See consolecmd.go: consoleCommand, attachCommand, javascriptCommand, - { - Action: makedag, - Name: "makedag", - Usage: "Generate ethash DAG (for testing)", - ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", - 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: version, - Name: "version", - Usage: "Print version numbers", - ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", - Description: ` -The output of this command is supposed to be machine-readable. -`, - }, - { - Action: initGenesis, - Name: "init", - Usage: "Bootstrap and initialize a new genesis block", - ArgsUsage: "", - Category: "BLOCKCHAIN COMMANDS", - Description: ` -The init command initializes a new genesis block and definition for the network. -This is a destructive action and changes the network in which you will be -participating. -`, - }, - { - Action: license, - Name: "license", - Usage: "Display license information", - ArgsUsage: " ", - Category: "MISCELLANEOUS COMMANDS", - }, + // See misccmd.go: + makedagCommand, + versionCommand, + licenseCommand, } app.Flags = []cli.Flag{ @@ -138,8 +102,6 @@ participating. utils.MaxPendingPeersFlag, utils.EtherbaseFlag, utils.GasPriceFlag, - utils.SupportDAOFork, - utils.OpposeDAOFork, utils.MinerThreadsFlag, utils.MiningEnabledFlag, utils.AutoDAGFlag, @@ -148,7 +110,7 @@ participating. utils.NatspecEnabledFlag, utils.NoDiscoverFlag, utils.DiscoveryV5Flag, - utils.NoEthFlag, + utils.NetrestrictFlag, utils.NodeKeyFileFlag, utils.NodeKeyHexFlag, utils.RPCEnabledFlag, @@ -173,6 +135,7 @@ participating. utils.VMEnableJitFlag, utils.NetworkIdFlag, utils.RPCCORSDomainFlag, + utils.EthStatsURLFlag, utils.MetricsEnabledFlag, utils.FakePoWFlag, utils.SolcPathFlag, @@ -229,33 +192,25 @@ func geth(ctx *cli.Context) error { 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") - } - - stack := makeFullNode(ctx) - chaindb := utils.MakeChainDatabase(ctx, stack) - - 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 -} - func makeFullNode(ctx *cli.Context) *node.Node { + // Create the default extradata and construct the base node + var clientInfo = struct { + Version uint + Name string + GoVersion string + Os string + }{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.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) + extra = nil + } stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) - utils.RegisterEthService(ctx, stack, utils.MakeDefaultExtraData(clientIdentifier)) + utils.RegisterEthService(ctx, stack, extra) // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name) @@ -263,14 +218,17 @@ func makeFullNode(ctx *cli.Context) *node.Node { if shhEnabled || shhAutoEnabled { utils.RegisterShhService(stack) } - + // Add the Ethereum Stats daemon if requested + if url := ctx.GlobalString(utils.EthStatsURLFlag.Name); url != "" { + utils.RegisterEthStatsService(stack, url) + } // Add the release oracle service so it boots along with node. if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { config := release.Config{ Oracle: relOracle, - Major: uint32(utils.VersionMajor), - Minor: uint32(utils.VersionMinor), - Patch: uint32(utils.VersionPatch), + Major: uint32(params.VersionMajor), + Minor: uint32(params.VersionMinor), + Patch: uint32(params.VersionPatch), } commit, _ := hex.DecodeString(gitCommit) copy(config.Commit[:], commit) @@ -278,7 +236,6 @@ func makeFullNode(ctx *cli.Context) *node.Node { }); err != nil { utils.Fatalf("Failed to register the Geth release oracle service: %v", err) } - return stack } @@ -309,65 +266,3 @@ func startNode(ctx *cli.Context, stack *node.Node) { } } } - -func makedag(ctx *cli.Context) error { - args := ctx.Args() - wrongArgs := func() { - utils.Fatalf(`Usage: geth makedag `) - } - 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 version(ctx *cli.Context) error { - fmt.Println(strings.Title(clientIdentifier)) - fmt.Println("Version:", utils.Version) - if gitCommit != "" { - fmt.Println("Git Commit:", gitCommit) - } - fmt.Println("Protocol Versions:", eth.ProtocolVersions) - fmt.Println("Network Id:", ctx.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 -} - -func license(_ *cli.Context) error { - fmt.Println(`Geth 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. - -Geth 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 geth. If not, see . -`) - return nil -} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/misccmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/misccmd.go new file mode 100644 index 000000000..077f1ad11 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/misccmd.go @@ -0,0 +1,128 @@ +// 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 . + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/params" + "gopkg.in/urfave/cli.v1" +) + +var ( + makedagCommand = cli.Command{ + Action: makedag, + Name: "makedag", + Usage: "Generate ethash DAG (for testing)", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", + 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. +`, + } + versionCommand = cli.Command{ + Action: version, + Name: "version", + Usage: "Print version numbers", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", + Description: ` +The output of this command is supposed to be machine-readable. +`, + } + licenseCommand = cli.Command{ + Action: license, + Name: "license", + Usage: "Display license information", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", + } +) + +func makedag(ctx *cli.Context) error { + args := ctx.Args() + wrongArgs := func() { + utils.Fatalf(`Usage: geth makedag `) + } + 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 version(ctx *cli.Context) error { + fmt.Println(strings.Title(clientIdentifier)) + fmt.Println("Version:", params.Version) + if gitCommit != "" { + fmt.Println("Git Commit:", gitCommit) + } + fmt.Println("Protocol Versions:", eth.ProtocolVersions) + fmt.Println("Network Id:", ctx.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 +} + +func license(_ *cli.Context) error { + fmt.Println(`Geth 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. + +Geth 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 geth. If not, see . +`) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go index 5e52040d5..50c742e7c 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go @@ -161,6 +161,7 @@ var AppHelpFlagGroups = []flagGroup{ { Name: "LOGGING AND DEBUGGING", Flags: append([]cli.Flag{ + utils.EthStatsURLFlag, utils.MetricsEnabledFlag, utils.FakePoWFlag, }, debug.Flags...), @@ -170,7 +171,6 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ utils.WhisperEnabledFlag, utils.NatspecEnabledFlag, - utils.NoEthFlag, }, }, { diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go index 3b4863e2b..06f6a6971 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . +// Package utils contains internal helper functions for go-ethereum commands. package utils import ( @@ -36,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/les" @@ -46,6 +48,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rpc" @@ -86,7 +89,7 @@ func NewApp(gitCommit, usage string) *cli.App { app.Author = "" //app.Authors = nil app.Email = "" - app.Version = Version + app.Version = params.Version if gitCommit != "" { app.Version += "-" + gitCommit[:8] } @@ -114,7 +117,7 @@ var ( } NetworkIdFlag = cli.IntFlag{ Name: "networkid", - Usage: "Network identifier (integer, 0=Olympic, 1=Frontier, 2=Morden)", + Usage: "Network identifier (integer, 0=Olympic (disused), 1=Frontier, 2=Morden (disused), 3=Ropsten)", Value: eth.NetworkId, } OlympicFlag = cli.BoolFlag{ @@ -123,7 +126,7 @@ var ( } TestNetFlag = cli.BoolFlag{ Name: "testnet", - Usage: "Morden network: pre-configured test network with modified starting nonces (replay protection)", + Usage: "Ropsten network: pre-configured test network", } DevModeFlag = cli.BoolFlag{ Name: "dev", @@ -175,15 +178,6 @@ var ( Usage: "Number of trie node generations to keep in memory", Value: int(state.MaxTrieCacheGen), } - // 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 MiningEnabledFlag = cli.BoolFlag{ Name: "mine", @@ -242,8 +236,11 @@ var ( Name: "jitvm", Usage: "Enable the JIT VM", } - - // logging and debug settings + // Logging and debug settings + EthStatsURLFlag = cli.StringFlag{ + Name: "ethstats", + Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", + } MetricsEnabledFlag = cli.BoolFlag{ Name: metrics.MetricsEnabledFlag, Usage: "Enable metrics collection and reporting", @@ -367,18 +364,20 @@ var ( Name: "v5disc", Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", } - NoEthFlag = cli.BoolFlag{ - Name: "noeth", - Usage: "Disable Ethereum Protocol", + NetrestrictFlag = cli.StringFlag{ + Name: "netrestrict", + Usage: "Restricts network communication to the given IP networks (CIDR masks)", } + WhisperEnabledFlag = cli.BoolFlag{ Name: "shh", Usage: "Enable Whisper", } + // ATM the url is left to the user and deployment to JSpathFlag = cli.StringFlag{ Name: "jspath", - Usage: "JavaScript root path for `loadScript` and document root for `admin.httpGet`", + Usage: "JavaScript root path for `loadScript`", Value: ".", } SolcPathFlag = cli.StringFlag{ @@ -665,7 +664,7 @@ func MakePasswordList(ctx *cli.Context) []string { // MakeNode configures a node with no services from command line flags. func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { - vsn := Version + vsn := params.Version if gitCommit != "" { vsn += "-" + gitCommit[:8] } @@ -705,6 +704,14 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { config.MaxPeers = 0 config.ListenAddr = ":0" } + if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" { + list, err := netutil.ParseNetlist(netrestrict) + if err != nil { + Fatalf("Option %q: %v", NetrestrictFlag.Name, err) + } + config.NetRestrict = list + } + stack, err := node.New(config) if err != nil { Fatalf("Failed to create the protocol stack: %v", err) @@ -715,12 +722,6 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { // RegisterEthService configures eth.Ethereum from command line flags and adds it to the // given node. func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { - ethDisabled := ctx.GlobalBool(NoEthFlag.Name) - if ethDisabled { - glog.V(logger.Info).Infof("Ethereum service registration by-passed (--%s flag used)\n", NoEthFlag.Name) - return - } - // Avoid conflicting network flags networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag, OlympicFlag} for _, flag := range netFlags { @@ -804,13 +805,30 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { } } -// RegisterShhService configures whisper and adds it to the given node. +// RegisterShhService configures Whisper and adds it to the given node. func RegisterShhService(stack *node.Node) { if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil { Fatalf("Failed to register the Whisper service: %v", err) } } +// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to +// th egiven node. +func RegisterEthStatsService(stack *node.Node, url string) { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + // Retrieve both eth and les services + var ethServ *eth.Ethereum + ctx.Service(ðServ) + + var lesServ *les.LightEthereum + ctx.Service(&lesServ) + + return ethstats.New(url, ethServ, lesServ) + }); err != nil { + Fatalf("Failed to register the Ethereum Stats service: %v", err) + } +} + // SetupNetwork configures the system for either the main net or some test network. func SetupNetwork(ctx *cli.Context) { switch { @@ -882,13 +900,6 @@ func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainCon config.ChainId = params.MainNetChainID } } - // 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 - } return config } diff --git a/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil.go b/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil.go new file mode 100644 index 000000000..29e6de333 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil.go @@ -0,0 +1,232 @@ +// 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 . + +/* +Package hexutil implements hex encoding with 0x prefix. +This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads. + +Encoding Rules + +All hex data must have prefix "0x". + +For byte slices, the hex data must be of even length. An empty byte slice +encodes as "0x". + +Integers are encoded using the least amount of digits (no leading zero digits). Their +encoding may be of uneven length. The number zero encodes as "0x0". +*/ +package hexutil + +import ( + "encoding/hex" + "errors" + "fmt" + "math/big" + "strconv" +) + +const uintBits = 32 << (uint64(^uint(0)) >> 63) + +var ( + ErrEmptyString = errors.New("empty hex string") + ErrMissingPrefix = errors.New("missing 0x prefix for hex data") + ErrSyntax = errors.New("invalid hex") + ErrEmptyNumber = errors.New("hex number has no digits after 0x") + ErrLeadingZero = errors.New("hex number has leading zero digits after 0x") + ErrOddLength = errors.New("hex string has odd length") + ErrUint64Range = errors.New("hex number does not fit into 64 bits") + ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits) +) + +// Decode decodes a hex string with 0x prefix. +func Decode(input string) ([]byte, error) { + if len(input) == 0 { + return nil, ErrEmptyString + } + if !has0xPrefix(input) { + return nil, ErrMissingPrefix + } + return hex.DecodeString(input[2:]) +} + +// MustDecode decodes a hex string with 0x prefix. It panics for invalid input. +func MustDecode(input string) []byte { + dec, err := Decode(input) + if err != nil { + panic(err) + } + return dec +} + +// Encode encodes b as a hex string with 0x prefix. +func Encode(b []byte) string { + enc := make([]byte, len(b)*2+2) + copy(enc, "0x") + hex.Encode(enc[2:], b) + return string(enc) +} + +// DecodeUint64 decodes a hex string with 0x prefix as a quantity. +func DecodeUint64(input string) (uint64, error) { + raw, err := checkNumber(input) + if err != nil { + return 0, err + } + dec, err := strconv.ParseUint(raw, 16, 64) + if err != nil { + err = mapError(err) + } + return dec, err +} + +// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity. +// It panics for invalid input. +func MustDecodeUint64(input string) uint64 { + dec, err := DecodeUint64(input) + if err != nil { + panic(err) + } + return dec +} + +// EncodeUint64 encodes i as a hex string with 0x prefix. +func EncodeUint64(i uint64) string { + enc := make([]byte, 2, 10) + copy(enc, "0x") + return string(strconv.AppendUint(enc, i, 16)) +} + +var bigWordNibbles int + +func init() { + // This is a weird way to compute the number of nibbles required for big.Word. + // The usual way would be to use constant arithmetic but go vet can't handle that. + b, _ := new(big.Int).SetString("FFFFFFFFFF", 16) + switch len(b.Bits()) { + case 1: + bigWordNibbles = 16 + case 2: + bigWordNibbles = 8 + default: + panic("weird big.Word size") + } +} + +// DecodeBig decodes a hex string with 0x prefix as a quantity. +func DecodeBig(input string) (*big.Int, error) { + raw, err := checkNumber(input) + if err != nil { + return nil, err + } + words := make([]big.Word, len(raw)/bigWordNibbles+1) + end := len(raw) + for i := range words { + start := end - bigWordNibbles + if start < 0 { + start = 0 + } + for ri := start; ri < end; ri++ { + nib := decodeNibble(raw[ri]) + if nib == badNibble { + return nil, ErrSyntax + } + words[i] *= 16 + words[i] += big.Word(nib) + } + end = start + } + dec := new(big.Int).SetBits(words) + return dec, nil +} + +// MustDecodeBig decodes a hex string with 0x prefix as a quantity. +// It panics for invalid input. +func MustDecodeBig(input string) *big.Int { + dec, err := DecodeBig(input) + if err != nil { + panic(err) + } + return dec +} + +// EncodeBig encodes bigint as a hex string with 0x prefix. +// The sign of the integer is ignored. +func EncodeBig(bigint *big.Int) string { + nbits := bigint.BitLen() + if nbits == 0 { + return "0x0" + } + enc := make([]byte, 2, (nbits/8)*2+2) + copy(enc, "0x") + for i := len(bigint.Bits()) - 1; i >= 0; i-- { + enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16) + } + return string(enc) +} + +func has0xPrefix(input string) bool { + return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') +} + +func checkNumber(input string) (raw string, err error) { + if len(input) == 0 { + return "", ErrEmptyString + } + if !has0xPrefix(input) { + return "", ErrMissingPrefix + } + input = input[2:] + if len(input) == 0 { + return "", ErrEmptyNumber + } + if len(input) > 1 && input[0] == '0' { + return "", ErrLeadingZero + } + return input, nil +} + +const badNibble = ^uint64(0) + +func decodeNibble(in byte) uint64 { + switch { + case in >= '0' && in <= '9': + return uint64(in - '0') + case in >= 'A' && in <= 'F': + return uint64(in - 'A' + 10) + case in >= 'a' && in <= 'f': + return uint64(in - 'a' + 10) + default: + return badNibble + } +} + +func mapError(err error) error { + if err, ok := err.(*strconv.NumError); ok { + switch err.Err { + case strconv.ErrRange: + return ErrUint64Range + case strconv.ErrSyntax: + return ErrSyntax + } + } + if _, ok := err.(hex.InvalidByteError); ok { + return ErrSyntax + } + if err == hex.ErrLength { + return ErrOddLength + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go b/vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go new file mode 100644 index 000000000..cbbadbed6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go @@ -0,0 +1,271 @@ +// 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 . + +package hexutil + +import ( + "encoding/hex" + "errors" + "fmt" + "math/big" + "strconv" +) + +var ( + jsonNull = []byte("null") + jsonZero = []byte(`"0x0"`) + errNonString = errors.New("cannot unmarshal non-string as hex data") + errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer") +) + +// Bytes marshals/unmarshals as a JSON string with 0x prefix. +// The empty slice marshals as "0x". +type Bytes []byte + +// MarshalJSON implements json.Marshaler. +func (b Bytes) MarshalJSON() ([]byte, error) { + result := make([]byte, len(b)*2+4) + copy(result, `"0x`) + hex.Encode(result[3:], b) + result[len(result)-1] = '"' + return result, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Bytes) UnmarshalJSON(input []byte) error { + raw, err := checkJSON(input) + if err != nil { + return err + } + dec := make([]byte, len(raw)/2) + if _, err = hex.Decode(dec, raw); err != nil { + err = mapError(err) + } else { + *b = dec + } + return err +} + +// String returns the hex encoding of b. +func (b Bytes) String() string { + return Encode(b) +} + +// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out +// determines the required input length. This function is commonly used to implement the +// UnmarshalJSON method for fixed-size types: +// +// type Foo [8]byte +// +// func (f *Foo) UnmarshalJSON(input []byte) error { +// return hexutil.UnmarshalJSON("Foo", input, f[:]) +// } +func UnmarshalJSON(typname string, input, out []byte) error { + raw, err := checkJSON(input) + if err != nil { + return err + } + if len(raw)/2 != len(out) { + return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) + } + // Pre-verify syntax before modifying out. + for _, b := range raw { + if decodeNibble(b) == badNibble { + return ErrSyntax + } + } + hex.Decode(out, raw) + return nil +} + +// Big marshals/unmarshals as a JSON string with 0x prefix. The zero value marshals as +// "0x0". Negative integers are not supported at this time. Attempting to marshal them +// will return an error. +type Big big.Int + +// MarshalJSON implements json.Marshaler. +func (b *Big) MarshalJSON() ([]byte, error) { + if b == nil { + return jsonNull, nil + } + bigint := (*big.Int)(b) + if bigint.Sign() == -1 { + return nil, errNegativeBigInt + } + nbits := bigint.BitLen() + if nbits == 0 { + return jsonZero, nil + } + enc := make([]byte, 3, (nbits/8)*2+4) + copy(enc, `"0x`) + for i := len(bigint.Bits()) - 1; i >= 0; i-- { + enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16) + } + enc = append(enc, '"') + return enc, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Big) UnmarshalJSON(input []byte) error { + raw, err := checkNumberJSON(input) + if err != nil { + return err + } + words := make([]big.Word, len(raw)/bigWordNibbles+1) + end := len(raw) + for i := range words { + start := end - bigWordNibbles + if start < 0 { + start = 0 + } + for ri := start; ri < end; ri++ { + nib := decodeNibble(raw[ri]) + if nib == badNibble { + return ErrSyntax + } + words[i] *= 16 + words[i] += big.Word(nib) + } + end = start + } + var dec big.Int + dec.SetBits(words) + *b = (Big)(dec) + return nil +} + +// ToInt converts b to a big.Int. +func (b *Big) ToInt() *big.Int { + return (*big.Int)(b) +} + +// String returns the hex encoding of b. +func (b *Big) String() string { + return EncodeBig(b.ToInt()) +} + +// Uint64 marshals/unmarshals as a JSON string with 0x prefix. +// The zero value marshals as "0x0". +type Uint64 uint64 + +// MarshalJSON implements json.Marshaler. +func (b Uint64) MarshalJSON() ([]byte, error) { + buf := make([]byte, 3, 12) + copy(buf, `"0x`) + buf = strconv.AppendUint(buf, uint64(b), 16) + buf = append(buf, '"') + return buf, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Uint64) UnmarshalJSON(input []byte) error { + raw, err := checkNumberJSON(input) + if err != nil { + return err + } + if len(raw) > 16 { + return ErrUint64Range + } + var dec uint64 + for _, byte := range raw { + nib := decodeNibble(byte) + if nib == badNibble { + return ErrSyntax + } + dec *= 16 + dec += uint64(nib) + } + *b = Uint64(dec) + return nil +} + +// String returns the hex encoding of b. +func (b Uint64) String() string { + return EncodeUint64(uint64(b)) +} + +// Uint marshals/unmarshals as a JSON string with 0x prefix. +// The zero value marshals as "0x0". +type Uint uint + +// MarshalJSON implements json.Marshaler. +func (b Uint) MarshalJSON() ([]byte, error) { + return Uint64(b).MarshalJSON() +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Uint) UnmarshalJSON(input []byte) error { + var u64 Uint64 + err := u64.UnmarshalJSON(input) + if err != nil { + return err + } else if u64 > Uint64(^uint(0)) { + return ErrUintRange + } + *b = Uint(u64) + return nil +} + +// String returns the hex encoding of b. +func (b Uint) String() string { + return EncodeUint64(uint64(b)) +} + +func isString(input []byte) bool { + return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' +} + +func bytesHave0xPrefix(input []byte) bool { + return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') +} + +func checkJSON(input []byte) (raw []byte, err error) { + if !isString(input) { + return nil, errNonString + } + if len(input) == 2 { + return nil, ErrEmptyString + } + if !bytesHave0xPrefix(input[1:]) { + return nil, ErrMissingPrefix + } + input = input[3 : len(input)-1] + if len(input)%2 != 0 { + return nil, ErrOddLength + } + return input, nil +} + +func checkNumberJSON(input []byte) (raw []byte, err error) { + if !isString(input) { + return nil, errNonString + } + input = input[1 : len(input)-1] + if len(input) == 0 { + return nil, ErrEmptyString + } + if !bytesHave0xPrefix(input) { + return nil, ErrMissingPrefix + } + input = input[2:] + if len(input) == 0 { + return nil, ErrEmptyNumber + } + if len(input) > 1 && input[0] == '0' { + return nil, ErrLeadingZero + } + return input, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/httpclient/httpclient.go b/vendor/github.com/ethereum/go-ethereum/common/httpclient/httpclient.go deleted file mode 100644 index a0a1efd38..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/httpclient/httpclient.go +++ /dev/null @@ -1,124 +0,0 @@ -// 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 . - -package httpclient - -import ( - "fmt" - "io/ioutil" - "net/http" - "path/filepath" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -type HTTPClient struct { - *http.Transport - DocRoot string - schemes []string -} - -func New(docRoot string) (self *HTTPClient) { - self = &HTTPClient{ - Transport: &http.Transport{}, - DocRoot: docRoot, - schemes: []string{"file"}, - } - self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot))) - return -} - -// Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines. - -// A Client is higher-level than a RoundTripper (such as Transport) and additionally handles HTTP details such as cookies and redirects. - -func (self *HTTPClient) Client() *http.Client { - return &http.Client{ - Transport: self, - } -} - -func (self *HTTPClient) RegisterScheme(scheme string, rt http.RoundTripper) { - self.schemes = append(self.schemes, scheme) - self.RegisterProtocol(scheme, rt) -} - -func (self *HTTPClient) HasScheme(scheme string) bool { - for _, s := range self.schemes { - if s == scheme { - return true - } - } - return false -} - -func (self *HTTPClient) GetAuthContent(uri string, hash common.Hash) ([]byte, error) { - // retrieve content - content, err := self.Get(uri, "") - if err != nil { - return nil, err - } - - // check hash to authenticate content - chash := crypto.Keccak256Hash(content) - if chash != hash { - return nil, fmt.Errorf("content hash mismatch %x != %x (exp)", hash[:], chash[:]) - } - - return content, nil - -} - -// Get(uri, path) downloads the document at uri, if path is non-empty it -// is interpreted as a filepath to which the contents are saved -func (self *HTTPClient) Get(uri, path string) ([]byte, error) { - // retrieve content - resp, err := self.Client().Get(uri) - if err != nil { - return nil, err - } - defer func() { - if resp != nil { - resp.Body.Close() - } - }() - - var content []byte - content, err = ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - if resp.StatusCode/100 != 2 { - return content, fmt.Errorf("HTTP error: %s", resp.Status) - } - - if path != "" { - var abspath string - abspath, err = filepath.Abs(path) - if err != nil { - return nil, err - } - err = ioutil.WriteFile(abspath, content, 0600) - if err != nil { - return nil, err - } - } - - return content, nil - -} diff --git a/vendor/github.com/ethereum/go-ethereum/common/registrar/contracts.go b/vendor/github.com/ethereum/go-ethereum/common/registrar/contracts.go deleted file mode 100644 index cd80dfcab..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/registrar/contracts.go +++ /dev/null @@ -1,163 +0,0 @@ -// 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 . - -package registrar - -const ( // built-in contracts address source code and evm code - UrlHintCode = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056" - - UrlHintSrc = ` -contract URLhint { - function register(uint256 _hash, uint8 idx, uint256 _url) { - if (owner[_hash] == 0 || owner[_hash] == msg.sender) { - owner[_hash] = msg.sender; - url[_hash][idx] = _url; - } - } - mapping (uint256 => address) owner; - (uint256 => uint256[256]) url; -} - ` - - HashRegCode = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056" - - HashRegSrc = ` -contract HashReg { - function setowner() { - if (owner == 0) { - owner = msg.sender; - } - } - function register(uint256 _key, uint256 _content) { - if (msg.sender == owner) { - content[_key] = _content; - } - } - address owner; - mapping (uint256 => uint256) content; -} -` - - GlobalRegistrarSrc = ` -//sol - -import "owned"; - -contract NameRegister { - function addr(bytes32 _name) constant returns (address o_owner) {} - function name(address _owner) constant returns (bytes32 o_name) {} -} - -contract Registrar is NameRegister { - event Changed(bytes32 indexed name); - event PrimaryChanged(bytes32 indexed name, address indexed addr); - - function owner(bytes32 _name) constant returns (address o_owner) {} - function addr(bytes32 _name) constant returns (address o_address) {} - function subRegistrar(bytes32 _name) constant returns (address o_subRegistrar) {} - function content(bytes32 _name) constant returns (bytes32 o_content) {} - - function name(address _owner) constant returns (bytes32 o_name) {} -} - -contract GlobalRegistrar is Registrar { - struct Record { - address owner; - address primary; - address subRegistrar; - bytes32 content; - uint value; - uint renewalDate; - } - - function Registrar() { - // TODO: Populate with hall-of-fame. - } - - function reserve(bytes32 _name) { - // Don't allow the same name to be overwritten. - // TODO: bidding mechanism - if (m_toRecord[_name].owner == 0) { - m_toRecord[_name].owner = msg.sender; - Changed(_name); - } - } - - /* - TODO - > 12 chars: free - <= 12 chars: auction: - 1. new names are auctioned - - 7 day period to collect all bid bytes32es + deposits - - 1 day period to collect all bids to be considered (validity requires associated deposit to be >10% of bid) - - all valid bids are burnt except highest - difference between that and second highest is returned to winner - 2. remember when last auctioned/renewed - 3. anyone can force renewal process: - - 7 day period to collect all bid bytes32es + deposits - - 1 day period to collect all bids & full amounts - bids only uncovered if sufficiently high. - - 1% of winner burnt; original owner paid rest. - */ - - modifier onlyrecordowner(bytes32 _name) { if (m_toRecord[_name].owner == msg.sender) _ } - - function transfer(bytes32 _name, address _newOwner) onlyrecordowner(_name) { - m_toRecord[_name].owner = _newOwner; - Changed(_name); - } - - function disown(bytes32 _name) onlyrecordowner(_name) { - if (m_toName[m_toRecord[_name].primary] == _name) - { - PrimaryChanged(_name, m_toRecord[_name].primary); - m_toName[m_toRecord[_name].primary] = ""; - } - delete m_toRecord[_name]; - Changed(_name); - } - - function setAddress(bytes32 _name, address _a, bool _primary) onlyrecordowner(_name) { - m_toRecord[_name].primary = _a; - if (_primary) - { - PrimaryChanged(_name, _a); - m_toName[_a] = _name; - } - Changed(_name); - } - function setSubRegistrar(bytes32 _name, address _registrar) onlyrecordowner(_name) { - m_toRecord[_name].subRegistrar = _registrar; - Changed(_name); - } - function setContent(bytes32 _name, bytes32 _content) onlyrecordowner(_name) { - m_toRecord[_name].content = _content; - Changed(_name); - } - - function owner(bytes32 _name) constant returns (address) { return m_toRecord[_name].owner; } - function addr(bytes32 _name) constant returns (address) { return m_toRecord[_name].primary; } -// function subRegistrar(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // TODO: bring in on next iteration. - function register(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // only possible for now - function content(bytes32 _name) constant returns (bytes32) { return m_toRecord[_name].content; } - function name(address _owner) constant returns (bytes32 o_name) { return m_toName[_owner]; } - - mapping (address => bytes32) m_toName; - mapping (bytes32 => Record) m_toRecord; -} -` - GlobalRegistrarAbi = `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]` - - GlobalRegistrarCode = `0x610b408061000e6000396000f3006000357c01000000000000000000000000000000000000000000000000000000009004806301984892146100b357806302571be3146100ce5780632dff6941146100ff5780633b3b57de1461011a578063432ced041461014b5780635a3a05bd1461016257806379ce9fac1461019357806389a69c0e146101b0578063b9f37c86146101cd578063be99a980146101de578063c3d014d614610201578063d93e75731461021e578063e1fa8e841461023557005b6100c4600480359060200150610b02565b8060005260206000f35b6100df6004803590602001506109f3565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b610110600480359060200150610ad4565b8060005260206000f35b61012b600480359060200150610a3e565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b61015c600480359060200150610271565b60006000f35b610173600480359060200150610266565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6101aa600480359060200180359060200150610341565b60006000f35b6101c7600480359060200180359060200150610844565b60006000f35b6101d860045061026e565b60006000f35b6101fb6004803590602001803590602001803590602001506106de565b60006000f35b61021860048035906020018035906020015061092c565b60006000f35b61022f600480359060200150610429565b60006000f35b610246600480359060200150610a89565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b60005b919050565b5b565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561033d57336001600050600083815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550807fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b5b50565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561042357816001600050600085815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b803373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156106d95781600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505414156105fd576001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16827ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a36000600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b6001600050600083815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556003820160005060009055600482016000506000905560058201600050600090555050817fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b50565b823373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561083d57826001600050600086815260200190815260200160002060005060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055508115610811578273ffffffffffffffffffffffffffffffffffffffff16847ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a383600060005060008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b837fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b505050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561092657816001600050600085815260200190815260200160002060005060020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156109ed57816001600050600085815260200190815260200160002060005060030160005081905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a39565b919050565b60006001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a84565b919050565b60006001600050600083815260200190815260200160002060005060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610acf565b919050565b600060016000506000838152602001908152602001600020600050600301600050549050610afd565b919050565b6000600060005060008373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050610b3b565b91905056` -) diff --git a/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go b/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go deleted file mode 100644 index a32653554..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go +++ /dev/null @@ -1,279 +0,0 @@ -// 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 . - -package ethreg - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/common/registrar" - "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/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/params" -) - -// registryAPIBackend is a backend for an Ethereum Registry. -type registryAPIBackend struct { - config *params.ChainConfig - bc *core.BlockChain - chainDb ethdb.Database - txPool *core.TxPool - am *accounts.Manager -} - -// PrivateRegistarAPI offers various functions to access the Ethereum registry. -type PrivateRegistarAPI struct { - config *params.ChainConfig - be *registryAPIBackend -} - -// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. -func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { - return &PrivateRegistarAPI{ - config: config, - be: ®istryAPIBackend{ - config: config, - bc: bc, - chainDb: chainDb, - txPool: txPool, - am: am, - }, - } -} - -// SetGlobalRegistrar allows clients to set the global registry for the node. -// This method can be used to deploy a new registry. First zero out the current -// address by calling the method with namereg = '0x0' and then call this method -// again with '' as namereg. This will submit a transaction to the network which -// will deploy a new registry on execution. The TX hash is returned. When called -// with namereg '' and the current address is not zero the current global is -// address is returned.. -func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) { - return registrar.New(api.be).SetGlobalRegistrar(namereg, from) -} - -// SetHashReg queries the registry for a hash. -func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) { - return registrar.New(api.be).SetHashReg(hashreg, from) -} - -// SetUrlHint queries the registry for an url. -func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) { - return registrar.New(api.be).SetUrlHint(hashreg, from) -} - -// SaveInfo stores contract information on the local file system. -func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) { - return compiler.SaveInfo(info, filename) -} - -// Register registers a new content hash in the registry. -func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) { - block := api.be.bc.CurrentBlock() - state, err := state.New(block.Root(), api.be.chainDb) - if err != nil { - return false, err - } - - codeb := state.GetCode(addr) - codeHash := common.BytesToHash(crypto.Keccak256(codeb)) - contentHash := common.HexToHash(contentHashHex) - - _, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash) - return err == nil, err -} - -// RegisterUrl registers a new url in the registry. -func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) { - _, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url) - return err == nil, err -} - -// callmsg is the message type used for call transations. -type callmsg struct { - from *state.StateObject - to *common.Address - gas, gasPrice *big.Int - value *big.Int - data []byte -} - -// accessor boilerplate to implement core.Message -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.gas -} -func (m callmsg) Value() *big.Int { - return m.value -} -func (m callmsg) Data() []byte { - return m.data -} - -// Call forms a transaction from the given arguments and tries to execute it on -// a private VM with a copy of the state. Any changes are therefore only temporary -// and not part of the actual state. This allows for local execution/queries. -func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { - block := be.bc.CurrentBlock() - statedb, err := state.New(block.Root(), be.chainDb) - if err != nil { - return "", "", err - } - - var from *state.StateObject - if len(fromStr) == 0 { - accounts := be.am.Accounts() - if len(accounts) == 0 { - from = statedb.GetOrNewStateObject(common.Address{}) - } else { - from = statedb.GetOrNewStateObject(accounts[0].Address) - } - } else { - from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr)) - } - - from.SetBalance(common.MaxBig) - - var to *common.Address - if len(toStr) > 0 { - addr := common.HexToAddress(toStr) - to = &addr - } - gas := common.Big(gasStr) - if gas.BitLen() == 0 { - gas = big.NewInt(50000000) - } - gasPrice := common.Big(gasPriceStr) - if gasPrice.BitLen() == 0 { - gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) - } - msg := types.NewMessage(from.Address(), to, 0, common.Big(valueStr), gas, gasPrice, common.FromHex(dataStr), false) - - header := be.bc.CurrentBlock().Header() - vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{}) - gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) - - return common.ToHex(res), gas.String(), err -} - -// StorageAt returns the data stores in the state for the given address and location. -func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string { - block := be.bc.CurrentBlock() - state, err := state.New(block.Root(), be.chainDb) - if err != nil { - return "" - } - return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex() -} - -// Transact forms a transaction from the given arguments and submits it to the -// transactio pool for execution. -func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { - if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) { - return "", errors.New("invalid address") - } - - var ( - from = common.HexToAddress(fromStr) - to = common.HexToAddress(toStr) - value = common.Big(valueStr) - gas *big.Int - price *big.Int - data []byte - contractCreation bool - ) - - if len(gasStr) == 0 { - gas = big.NewInt(90000) - } else { - gas = common.Big(gasStr) - } - - if len(gasPriceStr) == 0 { - price = big.NewInt(10000000000000) - } else { - price = common.Big(gasPriceStr) - } - - data = common.FromHex(codeStr) - if len(toStr) == 0 { - contractCreation = true - } - - nonce := be.txPool.State().GetNonce(from) - if len(nonceStr) != 0 { - nonce = common.Big(nonceStr).Uint64() - } - - var tx *types.Transaction - if contractCreation { - tx = types.NewContractCreation(nonce, value, gas, price, data) - } else { - tx = types.NewTransaction(nonce, to, value, gas, price, data) - } - - sigHash := (types.HomesteadSigner{}).Hash(tx) - signature, err := be.am.SignEthereum(from, sigHash.Bytes()) - if err != nil { - return "", err - } - signedTx, err := tx.WithSignature(types.HomesteadSigner{}, signature) - if err != nil { - return "", err - } - - be.txPool.SetLocal(signedTx) - if err := be.txPool.Add(signedTx); err != nil { - return "", nil - } - - if contractCreation { - addr := crypto.CreateAddress(from, nonce) - glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) - } else { - glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) - } - - return signedTx.Hash().Hex(), nil -} diff --git a/vendor/github.com/ethereum/go-ethereum/common/registrar/registrar.go b/vendor/github.com/ethereum/go-ethereum/common/registrar/registrar.go deleted file mode 100644 index 0606f6985..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/registrar/registrar.go +++ /dev/null @@ -1,436 +0,0 @@ -// 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 . - -package registrar - -import ( - "encoding/binary" - "fmt" - "math/big" - "regexp" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -/* -Registrar implements the Ethereum name registrar services mapping -- arbitrary strings to ethereum addresses -- hashes to hashes -- hashes to arbitrary strings -(likely will provide lookup service for all three) - -The Registrar is used by -* the roundtripper transport implementation of -url schemes to resolve domain names and services that register these names -* contract info retrieval (NatSpec). - -The Registrar uses 3 contracts on the blockchain: -* GlobalRegistrar: Name (string) -> Address (Owner) -* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash -* UrlHint : Content Hash -> Url Hint - -These contracts are (currently) not included in the genesis block. -Each Set needs to be called once on each blockchain/network once. - -Contract addresses need to be set the first time any Registrar method is called -in a client session. -This is done for frontier by default, otherwise the caller needs to make sure -the relevant environment initialised the desired contracts -*/ -var ( - // GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic - GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier - HashRegAddr = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier - UrlHintAddr = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier - - zero = regexp.MustCompile("^(0x)?0*$") -) - -const ( - trueHex = "0000000000000000000000000000000000000000000000000000000000000001" - falseHex = "0000000000000000000000000000000000000000000000000000000000000000" -) - -func abiSignature(s string) string { - return common.ToHex(crypto.Keccak256([]byte(s))[:4]) -} - -var ( - HashRegName = "HashReg" - UrlHintName = "UrlHint" - - registerContentHashAbi = abiSignature("register(uint256,uint256)") - registerUrlAbi = abiSignature("register(uint256,uint8,uint256)") - setOwnerAbi = abiSignature("setowner()") - reserveAbi = abiSignature("reserve(bytes32)") - resolveAbi = abiSignature("addr(bytes32)") - registerAbi = abiSignature("setAddress(bytes32,address,bool)") - addressAbiPrefix = falseHex[:24] -) - -// Registrar's backend is defined as an interface (implemented by xeth, but could be remote) -type Backend interface { - StorageAt(string, string) string - Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) - Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) -} - -// TODO Registrar should also just implement The Resolver and Registry interfaces -// Simplify for now. -type VersionedRegistrar interface { - Resolver(*big.Int) *Registrar - Registry() *Registrar -} - -type Registrar struct { - backend Backend -} - -func New(b Backend) (res *Registrar) { - res = &Registrar{b} - return -} - -func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) { - if namereg != "" { - GlobalRegistrarAddr = namereg - return - } - if zero.MatchString(GlobalRegistrarAddr) { - if (addr == common.Address{}) { - err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given") - return - } else { - txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode) - if err != nil { - err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err) - return - } - } - } - return -} - -func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) { - if hashreg != "" { - HashRegAddr = hashreg - } else { - if !zero.MatchString(HashRegAddr) { - return - } - nameHex, extra := encodeName(HashRegName, 2) - hashRegAbi := resolveAbi + nameHex + extra - glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi) - var res string - res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi) - if len(res) >= 40 { - HashRegAddr = "0x" + res[len(res)-40:len(res)] - } - if err != nil || zero.MatchString(HashRegAddr) { - if (addr == common.Address{}) { - err = fmt.Errorf("HashReg address not found and sender for creation not given") - return - } - - txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode) - if err != nil { - err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err) - } - glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash) - } else { - glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr) - return - } - } - - return -} - -func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) { - if urlhint != "" { - UrlHintAddr = urlhint - } else { - if !zero.MatchString(UrlHintAddr) { - return - } - nameHex, extra := encodeName(UrlHintName, 2) - urlHintAbi := resolveAbi + nameHex + extra - glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr) - var res string - res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi) - if len(res) >= 40 { - UrlHintAddr = "0x" + res[len(res)-40:len(res)] - } - if err != nil || zero.MatchString(UrlHintAddr) { - if (addr == common.Address{}) { - err = fmt.Errorf("UrlHint address not found and sender for creation not given") - return - } - txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode) - if err != nil { - err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err) - } - glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash) - } else { - glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr) - return - } - } - - return -} - -// ReserveName(from, name) reserves name for the sender address in the globalRegistrar -// the tx needs to be mined to take effect -func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) { - if zero.MatchString(GlobalRegistrarAddr) { - return "", fmt.Errorf("GlobalRegistrar address is not set") - } - nameHex, extra := encodeName(name, 2) - abi := reserveAbi + nameHex + extra - glog.V(logger.Detail).Infof("Reserve data: %s", abi) - return self.backend.Transact( - address.Hex(), - GlobalRegistrarAddr, - "", "", "", "", - abi, - ) -} - -// SetAddressToName(from, name, addr) will set the Address to address for name -// in the globalRegistrar using from as the sender of the transaction -// the tx needs to be mined to take effect -func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) { - if zero.MatchString(GlobalRegistrarAddr) { - return "", fmt.Errorf("GlobalRegistrar address is not set") - } - - nameHex, extra := encodeName(name, 6) - addrHex := encodeAddress(address) - - abi := registerAbi + nameHex + addrHex + trueHex + extra - glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr) - - return self.backend.Transact( - from.Hex(), - GlobalRegistrarAddr, - "", "", "", "", - abi, - ) -} - -// NameToAddr(from, name) queries the registrar for the address on name -func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) { - if zero.MatchString(GlobalRegistrarAddr) { - return address, fmt.Errorf("GlobalRegistrar address is not set") - } - - nameHex, extra := encodeName(name, 2) - abi := resolveAbi + nameHex + extra - glog.V(logger.Detail).Infof("NameToAddr data: %s", abi) - res, _, err := self.backend.Call( - from.Hex(), - GlobalRegistrarAddr, - "", "", "", - abi, - ) - if err != nil { - return - } - address = common.HexToAddress(res) - return -} - -// called as first step in the registration process on HashReg -func (self *Registrar) SetOwner(address common.Address) (txh string, err error) { - if zero.MatchString(HashRegAddr) { - return "", fmt.Errorf("HashReg address is not set") - } - return self.backend.Transact( - address.Hex(), - HashRegAddr, - "", "", "", "", - setOwnerAbi, - ) -} - -// registers some content hash to a key/code hash -// e.g., the contract Info combined Json Doc's ContentHash -// to CodeHash of a contract or hash of a domain -func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) { - if zero.MatchString(HashRegAddr) { - return "", fmt.Errorf("HashReg address is not set") - } - - _, err = self.SetOwner(address) - if err != nil { - return - } - codehex := common.Bytes2Hex(codehash[:]) - dochex := common.Bytes2Hex(dochash[:]) - - data := registerContentHashAbi + codehex + dochex - glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr) - return self.backend.Transact( - address.Hex(), - HashRegAddr, - "", "", "", "", - data, - ) -} - -// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched -// address is used as sender for the transaction and will be the owner of a new -// registry entry on first time use -// FIXME: silently doing nothing if sender is not the owner -// note that with content addressed storage, this step is no longer necessary -func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) { - if zero.MatchString(UrlHintAddr) { - return "", fmt.Errorf("UrlHint address is not set") - } - - hashHex := common.Bytes2Hex(hash[:]) - var urlHex string - urlb := []byte(url) - var cnt byte - n := len(urlb) - - for n > 0 { - if n > 32 { - n = 32 - } - urlHex = common.Bytes2Hex(urlb[:n]) - urlb = urlb[n:] - n = len(urlb) - bcnt := make([]byte, 32) - bcnt[31] = cnt - data := registerUrlAbi + - hashHex + - common.Bytes2Hex(bcnt) + - common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32)) - txh, err = self.backend.Transact( - address.Hex(), - UrlHintAddr, - "", "", "", "", - data, - ) - if err != nil { - return - } - cnt++ - } - return -} - -// HashToHash(key) resolves contenthash for key (a hash) using HashReg -// resolution is costless non-transactional -// implemented as direct retrieval from db -func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) { - if zero.MatchString(HashRegAddr) { - return common.Hash{}, fmt.Errorf("HashReg address is not set") - } - - // look up in hashReg - at := HashRegAddr[2:] - key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:])) - hash := self.backend.StorageAt(at, key) - - if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) { - err = fmt.Errorf("HashToHash: content hash not found for '%v'", khash.Hex()) - return - } - copy(chash[:], common.Hex2BytesFixed(hash[2:], 32)) - return -} - -// HashToUrl(contenthash) resolves the url for contenthash using UrlHint -// resolution is costless non-transactional -// implemented as direct retrieval from db -// if we use content addressed storage, this step is no longer necessary -func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) { - if zero.MatchString(UrlHintAddr) { - return "", fmt.Errorf("UrlHint address is not set") - } - // look up in URL reg - var str string = " " - var idx uint32 - for len(str) > 0 { - mapaddr := storageMapping(storageIdx2Addr(1), chash[:]) - key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx))) - hex := self.backend.StorageAt(UrlHintAddr[2:], key) - str = string(common.Hex2Bytes(hex[2:])) - l := 0 - for (l < len(str)) && (str[l] == 0) { - l++ - } - - str = str[l:] - uri = uri + str - idx++ - } - - if len(uri) == 0 { - err = fmt.Errorf("HashToUrl: URL hint not found for '%v'", chash.Hex()) - } - return -} - -func storageIdx2Addr(varidx uint32) []byte { - data := make([]byte, 32) - binary.BigEndian.PutUint32(data[28:32], varidx) - return data -} - -func storageMapping(addr, key []byte) []byte { - data := make([]byte, 64) - copy(data[0:32], key[0:32]) - copy(data[32:64], addr[0:32]) - sha := crypto.Keccak256(data) - return sha -} - -func storageFixedArray(addr, idx []byte) []byte { - var carry byte - for i := 31; i >= 0; i-- { - var b byte = addr[i] + idx[i] + carry - if b < addr[i] { - carry = 1 - } else { - carry = 0 - } - addr[i] = b - } - return addr -} - -func storageAddress(addr []byte) string { - return common.ToHex(addr) -} - -func encodeAddress(address common.Address) string { - return addressAbiPrefix + address.Hex()[2:] -} - -func encodeName(name string, index uint8) (string, string) { - extra := common.Bytes2Hex([]byte(name)) - if len(name) > 32 { - return fmt.Sprintf("%064x", index), extra - } - return extra + falseHex[len(extra):], "" -} diff --git a/vendor/github.com/ethereum/go-ethereum/common/types.go b/vendor/github.com/ethereum/go-ethereum/common/types.go index 70b7e7aae..8a456e965 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/types.go +++ b/vendor/github.com/ethereum/go-ethereum/common/types.go @@ -17,14 +17,12 @@ package common import ( - "encoding/hex" - "encoding/json" - "errors" "fmt" "math/big" "math/rand" "reflect" - "strings" + + "github.com/ethereum/go-ethereum/common/hexutil" ) const ( @@ -32,8 +30,6 @@ const ( AddressLength = 20 ) -var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes") - type ( // Hash represents the 32 byte Keccak256 hash of arbitrary data. Hash [HashLength]byte @@ -57,30 +53,16 @@ func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } func (h Hash) Str() string { return string(h[:]) } func (h Hash) Bytes() []byte { return h[:] } func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) } -func (h Hash) Hex() string { return "0x" + Bytes2Hex(h[:]) } +func (h Hash) Hex() string { return hexutil.Encode(h[:]) } // UnmarshalJSON parses a hash in its hex from to a hash. func (h *Hash) UnmarshalJSON(input []byte) error { - length := len(input) - if length >= 2 && input[0] == '"' && input[length-1] == '"' { - input = input[1 : length-1] - } - // strip "0x" for length check - if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" { - input = input[2:] - } - - // validate the length of the input hash - if len(input) != HashLength*2 { - return hashJsonLengthErr - } - h.SetBytes(FromHex(string(input))) - return nil + return hexutil.UnmarshalJSON("Hash", input, h[:]) } // Serialize given hash to JSON func (h Hash) MarshalJSON() ([]byte, error) { - return json.Marshal(h.Hex()) + return hexutil.Bytes(h[:]).MarshalJSON() } // Sets the hash to the value of b. If b is larger than len(h) it will panic @@ -142,7 +124,7 @@ func (a Address) Str() string { return string(a[:]) } func (a Address) Bytes() []byte { return a[:] } func (a Address) Big() *big.Int { return Bytes2Big(a[:]) } func (a Address) Hash() Hash { return BytesToHash(a[:]) } -func (a Address) Hex() string { return "0x" + Bytes2Hex(a[:]) } +func (a Address) Hex() string { return hexutil.Encode(a[:]) } // Sets the address to the value of b. If b is larger than len(a) it will panic func (a *Address) SetBytes(b []byte) { @@ -164,34 +146,12 @@ func (a *Address) Set(other Address) { // Serialize given address to JSON func (a Address) MarshalJSON() ([]byte, error) { - return json.Marshal(a.Hex()) + return hexutil.Bytes(a[:]).MarshalJSON() } // Parse address from raw json data -func (a *Address) UnmarshalJSON(data []byte) error { - if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' { - data = data[1 : len(data)-1] - } - - if len(data) > 2 && data[0] == '0' && data[1] == 'x' { - data = data[2:] - } - - if len(data) != 2*AddressLength { - return fmt.Errorf("Invalid address length, expected %d got %d bytes", 2*AddressLength, len(data)) - } - - n, err := hex.Decode(a[:], data) - if err != nil { - return err - } - - if n != AddressLength { - return fmt.Errorf("Invalid address") - } - - a.Set(HexToAddress(string(data))) - return nil +func (a *Address) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalJSON("Address", input, a[:]) } // PP Pretty Prints a byte slice in the following format: diff --git a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go index 9ace6d79a..1f762d147 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go @@ -152,9 +152,14 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain for hash, _ := range BadHashes { if header := bc.GetHeaderByHash(hash); header != nil { - glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4]) - bc.SetHead(header.Number.Uint64() - 1) - glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation") + // get the canonical block corresponding to the offending header's number + headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64()) + // make sure the headerByNumber (if present) is in our current canonical chain + if headerByNumber != nil && headerByNumber.Hash() == header.Hash() { + glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4]) + bc.SetHead(header.Number.Uint64() - 1) + glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation") + } } } // Take ownership of this particular state @@ -983,7 +988,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { glog.Infof("inserted forked block #%d [%x…] (TD=%v) in %9v: %3d txs %d uncles.", block.Number(), block.Hash().Bytes()[0:4], block.Difficulty(), common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), len(block.Uncles())) } blockInsertTimer.UpdateSince(bstart) - events = append(events, ChainSideEvent{block, logs}) + events = append(events, ChainSideEvent{block}) case SplitStatTy: events = append(events, ChainSplitEvent{block, logs}) @@ -1057,24 +1062,25 @@ func countTransactions(chain []*types.Block) (c int) { // event about them func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error { var ( - newChain types.Blocks - oldChain types.Blocks - commonBlock *types.Block - oldStart = oldBlock - newStart = newBlock - deletedTxs types.Transactions - deletedLogs vm.Logs - deletedLogsByHash = make(map[common.Hash]vm.Logs) + newChain types.Blocks + oldChain types.Blocks + commonBlock *types.Block + oldStart = oldBlock + newStart = newBlock + deletedTxs types.Transactions + deletedLogs vm.Logs // collectLogs collects the logs that were generated during the // processing of the block that corresponds with the given hash. // These logs are later announced as deleted. collectLogs = func(h common.Hash) { - // Coalesce logs + // Coalesce logs and set 'Removed'. receipts := GetBlockReceipts(self.chainDb, h, self.hc.GetBlockNumber(h)) for _, receipt := range receipts { - deletedLogs = append(deletedLogs, receipt.Logs...) - - deletedLogsByHash[h] = receipt.Logs + for _, log := range receipt.Logs { + del := *log + del.Removed = true + deletedLogs = append(deletedLogs, &del) + } } } ) @@ -1168,7 +1174,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error { if len(oldChain) > 0 { go func() { for _, block := range oldChain { - self.eventMux.Post(ChainSideEvent{Block: block, Logs: deletedLogsByHash[block.Hash()]}) + self.eventMux.Post(ChainSideEvent{Block: block}) } }() } diff --git a/vendor/github.com/ethereum/go-ethereum/core/events.go b/vendor/github.com/ethereum/go-ethereum/core/events.go index 322bcb769..414493fbf 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/events.go +++ b/vendor/github.com/ethereum/go-ethereum/core/events.go @@ -61,7 +61,6 @@ type ChainEvent struct { type ChainSideEvent struct { Block *types.Block - Logs vm.Logs } type PendingBlockEvent struct { diff --git a/vendor/github.com/ethereum/go-ethereum/core/evm.go b/vendor/github.com/ethereum/go-ethereum/core/evm.go new file mode 100644 index 000000000..6a5713075 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/evm.go @@ -0,0 +1,73 @@ +// 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 . + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" +) + +// BlockFetcher retrieves headers by their hash +type HeaderFetcher interface { + // GetHeader returns the hash corresponding to their hash + GetHeader(common.Hash, uint64) *types.Header +} + +// NewEVMContext creates a new context for use in the EVM. +func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context { + return vm.Context{ + CanTransfer: CanTransfer, + Transfer: Transfer, + GetHash: GetHashFn(header, chain), + + Origin: msg.From(), + Coinbase: header.Coinbase, + BlockNumber: new(big.Int).Set(header.Number), + Time: new(big.Int).Set(header.Time), + Difficulty: new(big.Int).Set(header.Difficulty), + GasLimit: new(big.Int).Set(header.GasLimit), + GasPrice: new(big.Int).Set(msg.GasPrice()), + } +} + +// GetHashFn returns a GetHashFunc which retrieves header hashes by number +func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash { + return func(n uint64) common.Hash { + for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { + if header.Number.Uint64() == n { + return header.Hash() + } + } + + return common.Hash{} + } +} + +// CanTransfer checks wether there are enough funds in the address' account to make a transfer. +// This does not take the necessary gas in to account to make the transfer valid. +func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { + return db.GetBalance(addr).Cmp(amount) >= 0 +} + +// Transfer subtracts amount from sender and adds amount to recipient using the given Db +func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { + db.SubBalance(sender, amount) + db.AddBalance(recipient, amount) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/execution.go b/vendor/github.com/ethereum/go-ethereum/core/execution.go deleted file mode 100644 index e3ea1006c..000000000 --- a/vendor/github.com/ethereum/go-ethereum/core/execution.go +++ /dev/null @@ -1,217 +0,0 @@ -// 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 . - -package core - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" -) - -// Call executes within the given contract -func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - - return nil, vm.DepthError - } - if !env.CanTransfer(caller.Address(), value) { - caller.ReturnGas(gas, gasPrice) - - return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) - } - - snapshotPreTransfer := env.SnapshotDatabase() - var ( - from = env.Db().GetAccount(caller.Address()) - to vm.Account - ) - if !env.Db().Exist(addr) { - if vm.Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 { - caller.ReturnGas(gas, gasPrice) - return nil, nil - } - - to = env.Db().CreateAccount(addr) - } else { - to = env.Db().GetAccount(addr) - } - env.Transfer(from, to, value) - - // initialise a new contract and set the code that is to be used by the - // EVM. The contract is a scoped environment for this execution context - // only. - contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) - defer contract.Finalise() - - ret, err = env.Vm().Run(contract, input) - // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally - // when we're in homestead this also counts for code storage gas errors. - if err != nil { - contract.UseGas(contract.Gas) - - env.RevertToSnapshot(snapshotPreTransfer) - } - return ret, err -} - -// CallCode executes the given address' code as the given contract address -func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - - return nil, vm.DepthError - } - if !env.CanTransfer(caller.Address(), value) { - caller.ReturnGas(gas, gasPrice) - - return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) - } - - var ( - snapshotPreTransfer = env.SnapshotDatabase() - to = env.Db().GetAccount(caller.Address()) - ) - // initialise a new contract and set the code that is to be used by the - // EVM. The contract is a scoped environment for this execution context - // only. - contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) - defer contract.Finalise() - - ret, err = env.Vm().Run(contract, input) - if err != nil { - contract.UseGas(contract.Gas) - - env.RevertToSnapshot(snapshotPreTransfer) - } - - return ret, err -} - -// Create creates a new contract with the given code -func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - - return nil, common.Address{}, vm.DepthError - } - if !env.CanTransfer(caller.Address(), value) { - caller.ReturnGas(gas, gasPrice) - - return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) - } - - // Create a new account on the state - nonce := env.Db().GetNonce(caller.Address()) - env.Db().SetNonce(caller.Address(), nonce+1) - - snapshotPreTransfer := env.SnapshotDatabase() - var ( - addr = crypto.CreateAddress(caller.Address(), nonce) - from = env.Db().GetAccount(caller.Address()) - to = env.Db().CreateAccount(addr) - ) - if env.ChainConfig().IsEIP158(env.BlockNumber()) { - env.Db().SetNonce(addr, 1) - } - env.Transfer(from, to, value) - - // initialise a new contract and set the code that is to be used by the - // EVM. The contract is a scoped environment for this execution context - // only. - contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code) - defer contract.Finalise() - - ret, err = env.Vm().Run(contract, nil) - // check whether the max code size has been exceeded - maxCodeSizeExceeded := len(ret) > params.MaxCodeSize - // if the contract creation ran successfully and no errors were returned - // calculate the gas required to store the code. If the code could not - // be stored due to not enough gas set an error and let it be handled - // by the error checking condition below. - if err == nil && !maxCodeSizeExceeded { - dataGas := big.NewInt(int64(len(ret))) - dataGas.Mul(dataGas, params.CreateDataGas) - if contract.UseGas(dataGas) { - env.Db().SetCode(addr, ret) - } else { - err = vm.CodeStoreOutOfGasError - } - } - - // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally - // when we're in homestead this also counts for code storage gas errors. - if maxCodeSizeExceeded || - (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) { - contract.UseGas(contract.Gas) - env.RevertToSnapshot(snapshotPreTransfer) - - // Nothing should be returned when an error is thrown. - return nil, addr, err - } - - return ret, addr, err -} - -// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope -func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - return nil, vm.DepthError - } - - var ( - snapshot = env.SnapshotDatabase() - to = env.Db().GetAccount(caller.Address()) - ) - - // Iinitialise a new contract and make initialise the delegate values - contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate() - contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) - defer contract.Finalise() - - ret, err = env.Vm().Run(contract, input) - if err != nil { - contract.UseGas(contract.Gas) - - env.RevertToSnapshot(snapshot) - } - - return ret, err -} - -// generic transfer method -func Transfer(from, to vm.Account, amount *big.Int) { - from.SubBalance(amount) - to.AddBalance(amount) -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go index d40b42d83..87aa8ccd6 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go @@ -288,7 +288,7 @@ func (self *StateObject) setBalance(amount *big.Int) { } // Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(gas, price *big.Int) {} +func (c *StateObject) ReturnGas(gas *big.Int) {} func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject { stateObject := newObject(db, self.address, self.data, onDirty) diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go index 3742c178b..82e2ec7c1 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go @@ -293,6 +293,7 @@ func (self *StateDB) HasSuicided(addr common.Address) bool { * SETTERS */ +// AddBalance adds amount to the account associated with addr func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { @@ -300,6 +301,14 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { } } +// SubBalance subtracts amount from the account associated with addr +func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SubBalance(amount) + } +} + func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go index e346917c3..67a7ad5a1 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go @@ -96,28 +96,36 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s if err != nil { return nil, nil, nil, err } - - _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp) + // Create a new context to be used in the EVM environment + context := NewEVMContext(msg, header, bc) + // Create a new environment which holds all relevant information + // about the transaction and calling mechanisms. + vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{}) + // Apply the transaction to the current state (included in the env) + _, gas, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, nil, nil, err } // Update the state with pending changes usedGas.Add(usedGas, gas) + // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx + // based on the eip phase, we're passing wether the root touch-delete accounts. receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) - if MessageCreatesContract(msg) { - receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce()) + // if the transaction created a contract, store the creation address in the receipt. + if msg.To() == nil { + receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) } - logs := statedb.GetLogs(tx.Hash()) - receipt.Logs = logs + // Set the receipt logs and create a bloom for filtering + receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) glog.V(logger.Debug).Infoln(receipt) - return receipt, logs, gas, err + return receipt, receipt.Logs, gas, err } // AccumulateRewards credits the coinbase of the given block with the diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go index 8abe17b0a..48540be14 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go @@ -55,9 +55,9 @@ type StateTransition struct { initialGas *big.Int value *big.Int data []byte - state vm.Database + state vm.StateDB - env vm.Environment + env *vm.Environment } // Message represents a message sent to a contract. @@ -106,7 +106,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int { } // NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition { +func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ gp: gp, env: env, @@ -116,7 +116,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran initialGas: new(big.Int), value: msg.Value(), data: msg.Data(), - state: env.Db(), + state: env.StateDB, } } @@ -127,7 +127,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. -func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { +func ApplyMessage(env *vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { st := NewStateTransition(env, msg, gp) ret, _, gasUsed, err := st.TransitionDb() @@ -217,47 +217,44 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b msg := self.msg sender := self.from() // err checked in preCheck - homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) + homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber) contractCreation := MessageCreatesContract(msg) // Pay intrinsic gas if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { return nil, nil, nil, InvalidTxError(err) } - vmenv := self.env - //var addr common.Address + var ( + vmenv = self.env + // vm errors do not effect consensus and are therefor + // not assigned to err, except for insufficient balance + // error. + vmerr error + ) if contractCreation { - ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) + ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value) if homestead && err == vm.CodeStoreOutOfGasError { self.gas = Big0 } - - if err != nil { - ret = nil - glog.V(logger.Core).Infoln("VM create err:", err) - } } else { // Increment the nonce for the next transaction self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) - ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value) - if err != nil { - glog.V(logger.Core).Infoln("VM call err:", err) + ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value) + } + if vmerr != nil { + glog.V(logger.Core).Infoln("vm returned with error:", err) + // The only possible consensus-error would be if there wasn't + // sufficient balance to make the transfer happen. The first + // balance transfer may never fail. + if vmerr == vm.ErrInsufficientBalance { + return nil, nil, nil, InvalidTxError(vmerr) } } - if err != nil && IsValueTransferErr(err) { - return nil, nil, nil, InvalidTxError(err) - } - - // We aren't interested in errors here. Errors returned by the VM are non-consensus errors and therefor shouldn't bubble up - if err != nil { - err = nil - } - requiredGas = new(big.Int).Set(self.gasUsed()) self.refundGas() - self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) + self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) return ret, requiredGas, self.gasUsed(), err } diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/block.go b/vendor/github.com/ethereum/go-ethereum/core/types/block.go index 68504ffcc..2034bb0ff 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/block.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/block.go @@ -29,6 +29,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/rlp" ) @@ -63,20 +64,12 @@ func (n BlockNonce) Uint64() uint64 { // MarshalJSON implements json.Marshaler func (n BlockNonce) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"0x%x"`, n)), nil + return hexutil.Bytes(n[:]).MarshalJSON() } // UnmarshalJSON implements json.Unmarshaler func (n *BlockNonce) UnmarshalJSON(input []byte) error { - var b hexBytes - if err := b.UnmarshalJSON(input); err != nil { - return err - } - if len(b) != 8 { - return errBadNonceSize - } - copy((*n)[:], b) - return nil + return hexutil.UnmarshalJSON("BlockNonce", input, n[:]) } // Header represents a block header in the Ethereum blockchain. @@ -106,12 +99,12 @@ type jsonHeader struct { TxHash *common.Hash `json:"transactionsRoot"` ReceiptHash *common.Hash `json:"receiptsRoot"` Bloom *Bloom `json:"logsBloom"` - Difficulty *hexBig `json:"difficulty"` - Number *hexBig `json:"number"` - GasLimit *hexBig `json:"gasLimit"` - GasUsed *hexBig `json:"gasUsed"` - Time *hexBig `json:"timestamp"` - Extra *hexBytes `json:"extraData"` + Difficulty *hexutil.Big `json:"difficulty"` + Number *hexutil.Big `json:"number"` + GasLimit *hexutil.Big `json:"gasLimit"` + GasUsed *hexutil.Big `json:"gasUsed"` + Time *hexutil.Big `json:"timestamp"` + Extra *hexutil.Bytes `json:"extraData"` MixDigest *common.Hash `json:"mixHash"` Nonce *BlockNonce `json:"nonce"` } @@ -151,12 +144,12 @@ func (h *Header) MarshalJSON() ([]byte, error) { TxHash: &h.TxHash, ReceiptHash: &h.ReceiptHash, Bloom: &h.Bloom, - Difficulty: (*hexBig)(h.Difficulty), - Number: (*hexBig)(h.Number), - GasLimit: (*hexBig)(h.GasLimit), - GasUsed: (*hexBig)(h.GasUsed), - Time: (*hexBig)(h.Time), - Extra: (*hexBytes)(&h.Extra), + Difficulty: (*hexutil.Big)(h.Difficulty), + Number: (*hexutil.Big)(h.Number), + GasLimit: (*hexutil.Big)(h.GasLimit), + GasUsed: (*hexutil.Big)(h.GasUsed), + Time: (*hexutil.Big)(h.Time), + Extra: (*hexutil.Bytes)(&h.Extra), MixDigest: &h.MixDigest, Nonce: &h.Nonce, }) diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go index d3945a734..a1d13e218 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go @@ -21,6 +21,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" ) @@ -77,20 +78,12 @@ func (b Bloom) TestBytes(test []byte) bool { // MarshalJSON encodes b as a hex string with 0x prefix. func (b Bloom) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil + return hexutil.Bytes(b[:]).MarshalJSON() } // UnmarshalJSON b as a hex string with 0x prefix. func (b *Bloom) UnmarshalJSON(input []byte) error { - var dec hexBytes - if err := dec.UnmarshalJSON(input); err != nil { - return err - } - if len(dec) != bloomLength { - return fmt.Errorf("invalid bloom size, want %d bytes", bloomLength) - } - copy((*b)[:], dec) - return nil + return hexutil.UnmarshalJSON("Bloom", input, b[:]) } func CreateBloom(receipts Receipts) Bloom { diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/json.go b/vendor/github.com/ethereum/go-ethereum/core/types/json.go deleted file mode 100644 index d2718a96d..000000000 --- a/vendor/github.com/ethereum/go-ethereum/core/types/json.go +++ /dev/null @@ -1,108 +0,0 @@ -// 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 . - -package types - -import ( - "encoding/hex" - "fmt" - "math/big" -) - -// JSON unmarshaling utilities. - -type hexBytes []byte - -func (b *hexBytes) MarshalJSON() ([]byte, error) { - if b != nil { - return []byte(fmt.Sprintf(`"0x%x"`, []byte(*b))), nil - } - return nil, nil -} - -func (b *hexBytes) UnmarshalJSON(input []byte) error { - if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { - return fmt.Errorf("cannot unmarshal non-string into hexBytes") - } - input = input[1 : len(input)-1] - if len(input) < 2 || input[0] != '0' || input[1] != 'x' { - return fmt.Errorf("missing 0x prefix in hexBytes input %q", input) - } - dec := make(hexBytes, (len(input)-2)/2) - if _, err := hex.Decode(dec, input[2:]); err != nil { - return err - } - *b = dec - return nil -} - -type hexBig big.Int - -func (b *hexBig) MarshalJSON() ([]byte, error) { - if b != nil { - return []byte(fmt.Sprintf(`"0x%x"`, (*big.Int)(b))), nil - } - return nil, nil -} - -func (b *hexBig) UnmarshalJSON(input []byte) error { - raw, err := checkHexNumber(input) - if err != nil { - return err - } - dec, ok := new(big.Int).SetString(string(raw), 16) - if !ok { - return fmt.Errorf("invalid hex number") - } - *b = (hexBig)(*dec) - return nil -} - -type hexUint64 uint64 - -func (b *hexUint64) MarshalJSON() ([]byte, error) { - if b != nil { - return []byte(fmt.Sprintf(`"0x%x"`, *(*uint64)(b))), nil - } - return nil, nil -} - -func (b *hexUint64) UnmarshalJSON(input []byte) error { - raw, err := checkHexNumber(input) - if err != nil { - return err - } - _, err = fmt.Sscanf(string(raw), "%x", b) - return err -} - -func checkHexNumber(input []byte) (raw []byte, err error) { - if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { - return nil, fmt.Errorf("cannot unmarshal non-string into hex number") - } - input = input[1 : len(input)-1] - if len(input) < 2 || input[0] != '0' || input[1] != 'x' { - return nil, fmt.Errorf("missing 0x prefix in hex number input %q", input) - } - if len(input) == 2 { - return nil, fmt.Errorf("empty hex number") - } - raw = input[2:] - if len(raw)%2 != 0 { - raw = append([]byte{'0'}, raw...) - } - return raw, nil -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go index b00fdabff..70c10d422 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go @@ -24,6 +24,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/rlp" ) @@ -49,12 +50,12 @@ type Receipt struct { type jsonReceipt struct { PostState *common.Hash `json:"root"` - CumulativeGasUsed *hexBig `json:"cumulativeGasUsed"` + CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"` Bloom *Bloom `json:"logsBloom"` Logs *vm.Logs `json:"logs"` TxHash *common.Hash `json:"transactionHash"` ContractAddress *common.Address `json:"contractAddress"` - GasUsed *hexBig `json:"gasUsed"` + GasUsed *hexutil.Big `json:"gasUsed"` } // NewReceipt creates a barebone transaction receipt, copying the init fields. @@ -90,12 +91,12 @@ func (r *Receipt) MarshalJSON() ([]byte, error) { return json.Marshal(&jsonReceipt{ PostState: &root, - CumulativeGasUsed: (*hexBig)(r.CumulativeGasUsed), + CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed), Bloom: &r.Bloom, Logs: &r.Logs, TxHash: &r.TxHash, ContractAddress: &r.ContractAddress, - GasUsed: (*hexBig)(r.GasUsed), + GasUsed: (*hexutil.Big)(r.GasUsed), }) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go index 323bfaee6..f566dc365 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go @@ -27,6 +27,7 @@ import ( "sync/atomic" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -69,15 +70,15 @@ type txdata struct { type jsonTransaction struct { Hash *common.Hash `json:"hash"` - AccountNonce *hexUint64 `json:"nonce"` - Price *hexBig `json:"gasPrice"` - GasLimit *hexBig `json:"gas"` + AccountNonce *hexutil.Uint64 `json:"nonce"` + Price *hexutil.Big `json:"gasPrice"` + GasLimit *hexutil.Big `json:"gas"` Recipient *common.Address `json:"to"` - Amount *hexBig `json:"value"` - Payload *hexBytes `json:"input"` - V *hexBig `json:"v"` - R *hexBig `json:"r"` - S *hexBig `json:"s"` + Amount *hexutil.Big `json:"value"` + Payload *hexutil.Bytes `json:"input"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` } func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { @@ -170,15 +171,15 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { return json.Marshal(&jsonTransaction{ Hash: &hash, - AccountNonce: (*hexUint64)(&tx.data.AccountNonce), - Price: (*hexBig)(tx.data.Price), - GasLimit: (*hexBig)(tx.data.GasLimit), + AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce), + Price: (*hexutil.Big)(tx.data.Price), + GasLimit: (*hexutil.Big)(tx.data.GasLimit), Recipient: tx.data.Recipient, - Amount: (*hexBig)(tx.data.Amount), - Payload: (*hexBytes)(&tx.data.Payload), - V: (*hexBig)(tx.data.V), - R: (*hexBig)(tx.data.R), - S: (*hexBig)(tx.data.S), + Amount: (*hexutil.Big)(tx.data.Amount), + Payload: (*hexutil.Bytes)(&tx.data.Payload), + V: (*hexutil.Big)(tx.data.V), + R: (*hexutil.Big)(tx.data.R), + S: (*hexutil.Big)(tx.data.S), }) } @@ -233,6 +234,8 @@ func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amo func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } func (tx *Transaction) CheckNonce() bool { return true } +// To returns the recipient address of the transaction. +// It returns nil if the transaction is a contract creation. func (tx *Transaction) To() *common.Address { if tx.data.Recipient == nil { return nil diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go b/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go index 70455a4c2..dfa93ab18 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go @@ -24,7 +24,7 @@ import ( // ContractRef is a reference to the contract's backing object type ContractRef interface { - ReturnGas(*big.Int, *big.Int) + ReturnGas(*big.Int) Address() common.Address Value() *big.Int SetCode(common.Hash, []byte) @@ -48,7 +48,7 @@ type Contract struct { CodeAddr *common.Address Input []byte - value, Gas, UsedGas, Price *big.Int + value, Gas, UsedGas *big.Int Args []byte @@ -56,7 +56,7 @@ type Contract struct { } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract { +func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract { c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} if parent, ok := caller.(*Contract); ok { @@ -70,9 +70,6 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big. // This pointer will be off the state transition c.Gas = gas //new(big.Int).Set(gas) c.value = new(big.Int).Set(value) - // In most cases price and value are pointers to transaction objects - // and we don't want the transaction's values to change. - c.Price = new(big.Int).Set(price) c.UsedGas = new(big.Int) return c @@ -114,7 +111,7 @@ func (c *Contract) Caller() common.Address { // caller. func (c *Contract) Finalise() { // Return the remaining gas to the caller - c.caller.ReturnGas(c.Gas, c.Price) + c.caller.ReturnGas(c.Gas) } // UseGas attempts the use gas and subtracts it and returns true on success @@ -127,7 +124,7 @@ func (c *Contract) UseGas(gas *big.Int) (ok bool) { } // ReturnGas adds the given gas back to itself. -func (c *Contract) ReturnGas(gas, price *big.Int) { +func (c *Contract) ReturnGas(gas *big.Int) { // Return the gas to the context c.Gas.Add(c.Gas, gas) c.UsedGas.Sub(c.UsedGas, gas) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go b/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go index e97c1e58c..50a09d444 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go @@ -17,110 +17,299 @@ package vm import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) -// Environment is an EVM requirement and helper which allows access to outside -// information such as states. -type Environment interface { - // The current ruleset - ChainConfig() *params.ChainConfig - // The state database - Db() Database - // Creates a restorable snapshot - SnapshotDatabase() int - // Set database to previous snapshot - RevertToSnapshot(int) - // Address of the original invoker (first occurrence of the VM invoker) - Origin() common.Address - // The block number this VM is invoked on - BlockNumber() *big.Int - // The n'th hash ago from this block number - GetHash(uint64) common.Hash - // The handler's address - Coinbase() common.Address - // The current time (block time) - Time() *big.Int - // Difficulty set on the current block - Difficulty() *big.Int - // The gas limit of the block - GasLimit() *big.Int - // Determines whether it's possible to transact - CanTransfer(from common.Address, balance *big.Int) bool - // Transfers amount from one account to the other - Transfer(from, to Account, amount *big.Int) - // Adds a LOG to the state - AddLog(*Log) - // Type of the VM - Vm() Vm - // Get the curret calling depth - Depth() int - // Set the current calling depth - SetDepth(i int) - // Call another contract - Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) - // Take another's contract code and execute within our own context - CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) - // Same as CallCode except sender and value is propagated from parent to child scope - DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) - // Create a new contract - Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) +type ( + CanTransferFunc func(StateDB, common.Address, *big.Int) bool + TransferFunc func(StateDB, common.Address, common.Address, *big.Int) + // GetHashFunc returns the nth block hash in the blockchain + // and is used by the BLOCKHASH EVM op code. + GetHashFunc func(uint64) common.Hash +) + +// Context provides the EVM with auxilary information. Once provided it shouldn't be modified. +type Context struct { + // CanTransfer returns whether the account contains + // sufficient ether to transfer the value + CanTransfer CanTransferFunc + // Transfer transfers ether from one account to the other + Transfer TransferFunc + // GetHash returns the hash corresponding to n + GetHash GetHashFunc + + // Message information + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE + + // Block information + Coinbase common.Address // Provides information for COINBASE + GasLimit *big.Int // Provides information for GASLIMIT + BlockNumber *big.Int // Provides information for NUMBER + Time *big.Int // Provides information for TIME + Difficulty *big.Int // Provides information for DIFFICULTY } -// Vm is the basic interface for an implementation of the EVM. -type Vm interface { - // Run should execute the given contract with the input given in in - // and return the contract execution return bytes or an error if it - // failed. - Run(c *Contract, in []byte) ([]byte, error) +// Environment provides information about external sources for the EVM +// +// The Environment should never be reused and is not thread safe. +type Environment struct { + // Context provides auxiliary blockchain related information + Context + // StateDB gives access to the underlying state + StateDB StateDB + // Depth is the current call stack + Depth int + + // evm is the ethereum virtual machine + evm Vm + // chainConfig contains information about the current chain + chainConfig *params.ChainConfig + vmConfig Config } -// Database is a EVM database for full state querying. -type Database interface { - GetAccount(common.Address) Account - CreateAccount(common.Address) Account - - AddBalance(common.Address, *big.Int) - GetBalance(common.Address) *big.Int - - GetNonce(common.Address) uint64 - SetNonce(common.Address, uint64) - - GetCodeHash(common.Address) common.Hash - GetCodeSize(common.Address) int - GetCode(common.Address) []byte - SetCode(common.Address, []byte) - - AddRefund(*big.Int) - GetRefund() *big.Int - - GetState(common.Address, common.Hash) common.Hash - SetState(common.Address, common.Hash, common.Hash) - - Suicide(common.Address) bool - HasSuicided(common.Address) bool - - // Exist reports whether the given account exists in state. - // Notably this should also return true for suicided accounts. - Exist(common.Address) bool - // Empty returns whether the given account is empty. Empty - // is defined according to EIP161 (balance = nonce = code = 0). - Empty(common.Address) bool +// NewEnvironment retutrns a new EVM environment. +func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment { + env := &Environment{ + Context: context, + StateDB: statedb, + vmConfig: vmConfig, + chainConfig: chainConfig, + } + env.evm = New(env, vmConfig) + return env } -// Account represents a contract or basic ethereum account. -type Account interface { - SubBalance(amount *big.Int) - AddBalance(amount *big.Int) - SetBalance(*big.Int) - SetNonce(uint64) - Balance() *big.Int - Address() common.Address - ReturnGas(*big.Int, *big.Int) - SetCode(common.Hash, []byte) - ForEachStorage(cb func(key, value common.Hash) bool) - Value() *big.Int +// Call executes the contract associated with the addr with the given input as paramaters. It also handles any +// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in +// case of an execution error or failed value transfer. +func (env *Environment) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) { + if env.vmConfig.NoRecursion && env.Depth > 0 { + caller.ReturnGas(gas) + + return nil, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + + return nil, DepthError + } + if !env.Context.CanTransfer(env.StateDB, caller.Address(), value) { + caller.ReturnGas(gas) + + return nil, ErrInsufficientBalance + } + + var ( + to Account + snapshotPreTransfer = env.StateDB.Snapshot() + ) + if !env.StateDB.Exist(addr) { + if Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 { + caller.ReturnGas(gas) + return nil, nil + } + + to = env.StateDB.CreateAccount(addr) + } else { + to = env.StateDB.GetAccount(addr) + } + env.Transfer(env.StateDB, caller.Address(), to.Address(), value) + + // initialise a new contract and set the code that is to be used by the + // E The contract is a scoped environment for this execution context + // only. + contract := NewContract(caller, to, value, gas) + contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) + defer contract.Finalise() + + ret, err := env.EVM().Run(contract, input) + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if err != nil { + contract.UseGas(contract.Gas) + + env.StateDB.RevertToSnapshot(snapshotPreTransfer) + } + return ret, err } + +// CallCode executes the contract associated with the addr with the given input as paramaters. It also handles any +// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in +// case of an execution error or failed value transfer. +// +// CallCode differs from Call in the sense that it executes the given address' code with the caller as context. +func (env *Environment) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) { + if env.vmConfig.NoRecursion && env.Depth > 0 { + caller.ReturnGas(gas) + + return nil, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + + return nil, DepthError + } + if !env.CanTransfer(env.StateDB, caller.Address(), value) { + caller.ReturnGas(gas) + + return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, env.StateDB.GetBalance(caller.Address())) + } + + var ( + snapshotPreTransfer = env.StateDB.Snapshot() + to = env.StateDB.GetAccount(caller.Address()) + ) + // initialise a new contract and set the code that is to be used by the + // E The contract is a scoped environment for this execution context + // only. + contract := NewContract(caller, to, value, gas) + contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) + defer contract.Finalise() + + ret, err := env.EVM().Run(contract, input) + if err != nil { + contract.UseGas(contract.Gas) + + env.StateDB.RevertToSnapshot(snapshotPreTransfer) + } + + return ret, err +} + +// DelegateCall executes the contract associated with the addr with the given input as paramaters. +// It reverses the state in case of an execution error. +// +// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context +// and the caller is set to the caller of the caller. +func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) ([]byte, error) { + if env.vmConfig.NoRecursion && env.Depth > 0 { + caller.ReturnGas(gas) + + return nil, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + return nil, DepthError + } + + var ( + snapshot = env.StateDB.Snapshot() + to = env.StateDB.GetAccount(caller.Address()) + ) + + // Iinitialise a new contract and make initialise the delegate values + contract := NewContract(caller, to, caller.Value(), gas).AsDelegate() + contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr)) + defer contract.Finalise() + + ret, err := env.EVM().Run(contract, input) + if err != nil { + contract.UseGas(contract.Gas) + + env.StateDB.RevertToSnapshot(snapshot) + } + + return ret, err +} + +// Create creates a new contract using code as deployment code. +func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.Int) ([]byte, common.Address, error) { + if env.vmConfig.NoRecursion && env.Depth > 0 { + caller.ReturnGas(gas) + + return nil, common.Address{}, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas) + + return nil, common.Address{}, DepthError + } + if !env.CanTransfer(env.StateDB, caller.Address(), value) { + caller.ReturnGas(gas) + + return nil, common.Address{}, ErrInsufficientBalance + } + + // Create a new account on the state + nonce := env.StateDB.GetNonce(caller.Address()) + env.StateDB.SetNonce(caller.Address(), nonce+1) + + snapshotPreTransfer := env.StateDB.Snapshot() + var ( + addr = crypto.CreateAddress(caller.Address(), nonce) + to = env.StateDB.CreateAccount(addr) + ) + if env.ChainConfig().IsEIP158(env.BlockNumber) { + env.StateDB.SetNonce(addr, 1) + } + env.Transfer(env.StateDB, caller.Address(), to.Address(), value) + + // initialise a new contract and set the code that is to be used by the + // E The contract is a scoped environment for this execution context + // only. + contract := NewContract(caller, to, value, gas) + contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code) + defer contract.Finalise() + + ret, err := env.EVM().Run(contract, nil) + // check whether the max code size has been exceeded + maxCodeSizeExceeded := len(ret) > params.MaxCodeSize + // if the contract creation ran successfully and no errors were returned + // calculate the gas required to store the code. If the code could not + // be stored due to not enough gas set an error and let it be handled + // by the error checking condition below. + if err == nil && !maxCodeSizeExceeded { + dataGas := big.NewInt(int64(len(ret))) + dataGas.Mul(dataGas, params.CreateDataGas) + if contract.UseGas(dataGas) { + env.StateDB.SetCode(addr, ret) + } else { + err = CodeStoreOutOfGasError + } + } + + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if maxCodeSizeExceeded || + (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != CodeStoreOutOfGasError)) { + contract.UseGas(contract.Gas) + env.StateDB.RevertToSnapshot(snapshotPreTransfer) + + // Nothing should be returned when an error is thrown. + return nil, addr, err + } + // If the vm returned with an error the return value should be set to nil. + // This isn't consensus critical but merely to for behaviour reasons such as + // tests, RPC calls, etc. + if err != nil { + ret = nil + } + + return ret, addr, err +} + +// ChainConfig returns the environment's chain configuration +func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig } + +// EVM returns the environments EVM +func (env *Environment) EVM() Vm { return env.evm } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go index 1766bf9fb..f8d26b1f0 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go @@ -23,7 +23,10 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var OutOfGasError = errors.New("Out of gas") -var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas") -var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth) -var TraceLimitReachedError = errors.New("The number of logs reached the specified limit") +var ( + OutOfGasError = errors.New("Out of gas") + CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas") + DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth) + TraceLimitReachedError = errors.New("The number of logs reached the specified limit") + ErrInsufficientBalance = errors.New("insufficient balance for transfer") +) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go index 4f98953b5..871c09e83 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go @@ -28,14 +28,14 @@ import ( type programInstruction interface { // executes the program instruction and allows the instruction to modify the state of the program - do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) + do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) // returns whether the program instruction halts the execution of the JIT halts() bool // Returns the current op code (debugging purposes) Op() OpCode } -type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) +type instrFn func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) type instruction struct { op OpCode @@ -59,9 +59,9 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract return mapping[to.Uint64()], nil } -func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (instr instruction) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // calculate the new memory size and gas price for the current executing opcode - newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack) + newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, memory, stack) if err != nil { return nil, err } @@ -115,26 +115,26 @@ func (instr instruction) Op() OpCode { return instr.op } -func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env *Environment, contract *Contract, memory *Memory, stack *Stack) { ret.Set(instr.data) } -func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opAdd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Add(x, y))) } -func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSub(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Sub(x, y))) } -func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opMul(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Mul(x, y))) } -func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opDiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) != 0 { stack.push(U256(x.Div(x, y))) @@ -143,7 +143,7 @@ func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSdiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) @@ -163,7 +163,7 @@ func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, } } -func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opMod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) @@ -172,7 +172,7 @@ func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { @@ -192,12 +192,12 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, } } -func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opExp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { base, exponent := stack.pop(), stack.pop() stack.push(math.Exp(base, exponent)) } -func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSignExtend(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { back := stack.pop() if back.Cmp(big.NewInt(31)) < 0 { bit := uint(back.Uint64()*8 + 7) @@ -214,12 +214,12 @@ func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Cont } } -func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opNot(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x := stack.pop() stack.push(U256(x.Not(x))) } -func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opLt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) < 0 { stack.push(big.NewInt(1)) @@ -228,7 +228,7 @@ func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, me } } -func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opGt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) > 0 { stack.push(big.NewInt(1)) @@ -237,7 +237,7 @@ func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, me } } -func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSlt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(S256(y)) < 0 { stack.push(big.NewInt(1)) @@ -246,7 +246,7 @@ func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSgt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(y) > 0 { stack.push(big.NewInt(1)) @@ -255,7 +255,7 @@ func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, m } } -func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opEq(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) == 0 { stack.push(big.NewInt(1)) @@ -264,7 +264,7 @@ func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, me } } -func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opIszero(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x := stack.pop() if x.Cmp(common.Big0) > 0 { stack.push(new(big.Int)) @@ -273,19 +273,19 @@ func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract } } -func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opAnd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(x.And(x, y)) } -func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opOr(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(x.Or(x, y)) } -func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opXor(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() stack.push(x.Xor(x, y)) } -func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opByte(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { th, val := stack.pop(), stack.pop() if th.Cmp(big.NewInt(32)) < 0 { byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) @@ -294,7 +294,7 @@ func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, stack.push(new(big.Int)) } } -func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opAddmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(Zero) > 0 { add := x.Add(x, y) @@ -304,7 +304,7 @@ func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract stack.push(new(big.Int)) } } -func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opMulmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(Zero) > 0 { mul := x.Mul(x, y) @@ -315,45 +315,45 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract } } -func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSha3(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { offset, size := stack.pop(), stack.pop() hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64())) stack.push(common.BytesToBig(hash)) } -func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opAddress(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(common.Bytes2Big(contract.Address().Bytes())) } -func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opBalance(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { addr := common.BigToAddress(stack.pop()) - balance := env.Db().GetBalance(addr) + balance := env.StateDB.GetBalance(addr) stack.push(new(big.Int).Set(balance)) } -func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(env.Origin().Big()) +func opOrigin(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + stack.push(env.Origin.Big()) } -func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCaller(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(contract.Caller().Big()) } -func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCallValue(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(contract.value)) } -func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCalldataLoad(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32))) } -func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCalldataSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(big.NewInt(int64(len(contract.Input)))) } -func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCalldataCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { var ( mOff = stack.pop() cOff = stack.pop() @@ -362,18 +362,18 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l)) } -func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opExtCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { addr := common.BigToAddress(stack.pop()) - l := big.NewInt(int64(env.Db().GetCodeSize(addr))) + l := big.NewInt(int64(env.StateDB.GetCodeSize(addr))) stack.push(l) } -func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { l := big.NewInt(int64(len(contract.Code))) stack.push(l) } -func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { var ( mOff = stack.pop() cOff = stack.pop() @@ -384,70 +384,70 @@ func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contra memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) } -func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opExtCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { var ( addr = common.BigToAddress(stack.pop()) mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) - codeCopy := getData(env.Db().GetCode(addr), cOff, l) + codeCopy := getData(env.StateDB.GetCode(addr), cOff, l) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) } -func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(new(big.Int).Set(contract.Price)) +func opGasprice(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + stack.push(new(big.Int).Set(env.GasPrice)) } -func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opBlockhash(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { num := stack.pop() - n := new(big.Int).Sub(env.BlockNumber(), common.Big257) - if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber()) < 0 { + n := new(big.Int).Sub(env.BlockNumber, common.Big257) + if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 { stack.push(env.GetHash(num.Uint64()).Big()) } else { stack.push(new(big.Int)) } } -func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(env.Coinbase().Big()) +func opCoinbase(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + stack.push(env.Coinbase.Big()) } -func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(U256(new(big.Int).Set(env.Time()))) +func opTimestamp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + stack.push(U256(new(big.Int).Set(env.Time))) } -func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(U256(new(big.Int).Set(env.BlockNumber()))) +func opNumber(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + stack.push(U256(new(big.Int).Set(env.BlockNumber))) } -func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(U256(new(big.Int).Set(env.Difficulty()))) +func opDifficulty(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + stack.push(U256(new(big.Int).Set(env.Difficulty))) } -func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - stack.push(U256(new(big.Int).Set(env.GasLimit()))) +func opGasLimit(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + stack.push(U256(new(big.Int).Set(env.GasLimit))) } -func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opPop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.pop() } -func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opPush(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(instr.data)) } -func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opDup(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.dup(int(instr.data.Int64())) } -func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSwap(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.swap(int(instr.data.Int64())) } -func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opLog(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { n := int(instr.data.Int64()) topics := make([]common.Hash, n) mStart, mSize := stack.pop(), stack.pop() @@ -456,77 +456,77 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m } d := memory.Get(mStart.Int64(), mSize.Int64()) - log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64()) - env.AddLog(log) + log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64()) + env.StateDB.AddLog(log) } -func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opMload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { offset := stack.pop() val := common.BigD(memory.Get(offset.Int64(), 32)) stack.push(val) } -func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opMstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { // pop value of the stack mStart, val := stack.pop(), stack.pop() memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) } -func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opMstore8(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { off, val := stack.pop().Int64(), stack.pop().Int64() memory.store[off] = byte(val & 0xff) } -func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { loc := common.BigToHash(stack.pop()) - val := env.Db().GetState(contract.Address(), loc).Big() + val := env.StateDB.GetState(contract.Address(), loc).Big() stack.push(val) } -func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opSstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { loc := common.BigToHash(stack.pop()) val := stack.pop() - env.Db().SetState(contract.Address(), loc, common.BigToHash(val)) + env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) } -func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opJump(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opJumpi(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opJumpdest(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opPc(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(instr.data)) } -func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opMsize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(big.NewInt(int64(memory.Len()))) } -func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opGas(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(contract.Gas)) } -func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { var ( value = stack.pop() offset, size = stack.pop(), stack.pop() input = memory.Get(offset.Int64(), size.Int64()) gas = new(big.Int).Set(contract.Gas) ) - if env.ChainConfig().IsEIP150(env.BlockNumber()) { + if env.ChainConfig().IsEIP150(env.BlockNumber) { gas.Div(gas, n64) gas = gas.Sub(contract.Gas, gas) } contract.UseGas(gas) - _, addr, suberr := env.Create(contract, input, gas, contract.Price, value) + _, addr, suberr := env.Create(contract, input, gas, value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. - if env.ChainConfig().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { + if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == CodeStoreOutOfGasError { stack.push(new(big.Int)) } else if suberr != nil && suberr != CodeStoreOutOfGasError { stack.push(new(big.Int)) @@ -535,7 +535,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract } } -func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() @@ -554,7 +554,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, gas.Add(gas, params.CallStipend) } - ret, err := env.Call(contract, address, args, gas, contract.Price, value) + ret, err := env.Call(contract, address, args, gas, value) if err != nil { stack.push(new(big.Int)) @@ -566,7 +566,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, } } -func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opCallCode(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() @@ -585,7 +585,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra gas.Add(gas, params.CallStipend) } - ret, err := env.CallCode(contract, address, args, gas, contract.Price, value) + ret, err := env.CallCode(contract, address, args, gas, value) if err != nil { stack.push(new(big.Int)) @@ -597,12 +597,12 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra } } -func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opDelegateCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(to) args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price) + ret, err := env.DelegateCall(contract, toAddr, args, gas) if err != nil { stack.push(new(big.Int)) } else { @@ -611,23 +611,23 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co } } -func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opReturn(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { +func opStop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { } -func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { - balance := env.Db().GetBalance(contract.Address()) - env.Db().AddBalance(common.BigToAddress(stack.pop()), balance) +func opSuicide(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { + balance := env.StateDB.GetBalance(contract.Address()) + env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) - env.Db().Suicide(contract.Address()) + env.StateDB.Suicide(contract.Address()) } // following functions are used by the instruction jump table // make log instruction function func makeLog(size int) instrFn { - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { + return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { topics := make([]common.Hash, size) mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { @@ -635,14 +635,14 @@ func makeLog(size int) instrFn { } d := memory.Get(mStart.Int64(), mSize.Int64()) - log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64()) - env.AddLog(log) + log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64()) + env.StateDB.AddLog(log) } } // make push instruction function func makePush(size uint64, bsize *big.Int) instrFn { - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { + return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize) stack.push(common.Bytes2Big(byts)) *pc += size @@ -651,7 +651,7 @@ func makePush(size uint64, bsize *big.Int) instrFn { // make push instruction function func makeDup(size int64) instrFn { - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { + return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.dup(int(size)) } } @@ -660,7 +660,7 @@ func makeDup(size int64) instrFn { func makeSwap(size int64) instrFn { // switch n + 1 otherwise n would be swapped with n size += 1 - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { + return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { stack.swap(int(size)) } } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/interface.go b/vendor/github.com/ethereum/go-ethereum/core/vm/interface.go new file mode 100644 index 000000000..918fde85f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/interface.go @@ -0,0 +1,97 @@ +// 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 . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// Vm is the basic interface for an implementation of the EVM. +type Vm interface { + // Run should execute the given contract with the input given in in + // and return the contract execution return bytes or an error if it + // failed. + Run(c *Contract, in []byte) ([]byte, error) +} + +// StateDB is an EVM database for full state querying. +type StateDB interface { + GetAccount(common.Address) Account + CreateAccount(common.Address) Account + + SubBalance(common.Address, *big.Int) + AddBalance(common.Address, *big.Int) + GetBalance(common.Address) *big.Int + + GetNonce(common.Address) uint64 + SetNonce(common.Address, uint64) + + GetCodeHash(common.Address) common.Hash + GetCode(common.Address) []byte + SetCode(common.Address, []byte) + GetCodeSize(common.Address) int + + AddRefund(*big.Int) + GetRefund() *big.Int + + GetState(common.Address, common.Hash) common.Hash + SetState(common.Address, common.Hash, common.Hash) + + Suicide(common.Address) bool + HasSuicided(common.Address) bool + + // Exist reports whether the given account exists in state. + // Notably this should also return true for suicided accounts. + Exist(common.Address) bool + // Empty returns whether the given account is empty. Empty + // is defined according to EIP161 (balance = nonce = code = 0). + Empty(common.Address) bool + + RevertToSnapshot(int) + Snapshot() int + + AddLog(*Log) +} + +// Account represents a contract or basic ethereum account. +type Account interface { + SubBalance(amount *big.Int) + AddBalance(amount *big.Int) + SetBalance(*big.Int) + SetNonce(uint64) + Balance() *big.Int + Address() common.Address + ReturnGas(*big.Int) + SetCode(common.Hash, []byte) + ForEachStorage(cb func(key, value common.Hash) bool) + Value() *big.Int +} + +// CallContext provides a basic interface for the EVM calling conventions. The EVM Environment +// depends on this context being implemented for doing subcalls and initialising new EVM contracts. +type CallContext interface { + // Call another contract + Call(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) + // Take another's contract code and execute within our own context + CallCode(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) + // Same as CallCode except sender and value is propagated from parent to child scope + DelegateCall(env *Environment, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) + // Create a new contract + Create(env *Environment, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go index b75558d39..aabe4488b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go @@ -299,11 +299,11 @@ func CompileProgram(program *Program) (err error) { // RunProgram runs the program given the environment and contract and returns an // error if the execution failed (non-consensus) -func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) { +func RunProgram(program *Program, env *Environment, contract *Contract, input []byte) ([]byte, error) { return runProgram(program, 0, NewMemory(), newstack(), env, contract, input) } -func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env Environment, contract *Contract, input []byte) ([]byte, error) { +func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env *Environment, contract *Contract, input []byte) ([]byte, error) { contract.Input = input var ( @@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env }() } - homestead := env.ChainConfig().IsHomestead(env.BlockNumber()) + homestead := env.ChainConfig().IsHomestead(env.BlockNumber) for pc < uint64(len(program.instructions)) { instrCount++ @@ -357,7 +357,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool { // jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { +func jitCalculateGasAndSize(env *Environment, contract *Contract, instr instruction, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -408,7 +408,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi var g *big.Int y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] - val := statedb.GetState(contract.Address(), common.BigToHash(x)) + val := env.StateDB.GetState(contract.Address(), common.BigToHash(x)) // This checks for 3 scenario's and calculates gas accordingly // 1. From a zero-value address to a non-zero value (NEW VALUE) @@ -417,7 +417,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { g = params.SstoreSetGas } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { - statedb.AddRefund(params.SstoreRefundGas) + env.StateDB.AddRefund(params.SstoreRefundGas) g = params.SstoreClearGas } else { @@ -425,8 +425,8 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi } gas.Set(g) case SUICIDE: - if !statedb.HasSuicided(contract.Address()) { - statedb.AddRefund(params.SuicideRefundGas) + if !env.StateDB.HasSuicided(contract.Address()) { + env.StateDB.AddRefund(params.SuicideRefundGas) } case MLOAD: newMemSize = calcMemSize(stack.peek(), u256(32)) @@ -463,7 +463,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { + if !env.StateDB.Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/log.go b/vendor/github.com/ethereum/go-ethereum/core/vm/log.go index b292f5f43..347bd6e5d 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/log.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/log.go @@ -23,57 +23,79 @@ import ( "io" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" ) var errMissingLogFields = errors.New("missing required JSON log fields") -// Log represents a contract log event. These events are generated by the LOG -// opcode and stored/indexed by the node. +// Log represents a contract log event. These events are generated by the LOG opcode and +// stored/indexed by the node. type Log struct { // Consensus fields. Address common.Address // address of the contract that generated the event Topics []common.Hash // list of topics provided by the contract. Data []byte // supplied by the contract, usually ABI-encoded - // Derived fields (don't reorder!). + // Derived fields. These fields are filled in by the node + // but not secured by consensus. BlockNumber uint64 // block in which the transaction was included TxHash common.Hash // hash of the transaction TxIndex uint // index of the transaction in the block BlockHash common.Hash // hash of the block in which the transaction was included Index uint // index of the log in the receipt + + // The Removed field is true if this log was reverted due to a chain reorganisation. + // You must pay attention to this field if you receive logs through a filter query. + Removed bool +} + +type rlpLog struct { + Address common.Address + Topics []common.Hash + Data []byte +} + +type rlpStorageLog struct { + Address common.Address + Topics []common.Hash + Data []byte + BlockNumber uint64 + TxHash common.Hash + TxIndex uint + BlockHash common.Hash + Index uint } type jsonLog struct { Address *common.Address `json:"address"` Topics *[]common.Hash `json:"topics"` - Data string `json:"data"` - BlockNumber string `json:"blockNumber"` - TxIndex string `json:"transactionIndex"` + Data *hexutil.Bytes `json:"data"` + BlockNumber *hexutil.Uint64 `json:"blockNumber"` + TxIndex *hexutil.Uint `json:"transactionIndex"` TxHash *common.Hash `json:"transactionHash"` BlockHash *common.Hash `json:"blockHash"` - Index string `json:"logIndex"` + Index *hexutil.Uint `json:"logIndex"` + Removed bool `json:"removed"` } func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log { return &Log{Address: address, Topics: topics, Data: data, BlockNumber: number} } +// EncodeRLP implements rlp.Encoder. func (l *Log) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{l.Address, l.Topics, l.Data}) + return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}) } +// DecodeRLP implements rlp.Decoder. func (l *Log) DecodeRLP(s *rlp.Stream) error { - var log struct { - Address common.Address - Topics []common.Hash - Data []byte + var dec rlpLog + err := s.Decode(&dec) + if err == nil { + l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data } - if err := s.Decode(&log); err != nil { - return err - } - l.Address, l.Topics, l.Data = log.Address, log.Topics, log.Data - return nil + return err } func (l *Log) String() string { @@ -81,54 +103,88 @@ func (l *Log) String() string { } // MarshalJSON implements json.Marshaler. -func (r *Log) MarshalJSON() ([]byte, error) { - return json.Marshal(&jsonLog{ - Address: &r.Address, - Topics: &r.Topics, - Data: fmt.Sprintf("0x%x", r.Data), - BlockNumber: fmt.Sprintf("0x%x", r.BlockNumber), - TxIndex: fmt.Sprintf("0x%x", r.TxIndex), - TxHash: &r.TxHash, - BlockHash: &r.BlockHash, - Index: fmt.Sprintf("0x%x", r.Index), - }) +func (l *Log) MarshalJSON() ([]byte, error) { + jslog := &jsonLog{ + Address: &l.Address, + Topics: &l.Topics, + Data: (*hexutil.Bytes)(&l.Data), + TxIndex: (*hexutil.Uint)(&l.TxIndex), + TxHash: &l.TxHash, + Index: (*hexutil.Uint)(&l.Index), + Removed: l.Removed, + } + // Set block information for mined logs. + if (l.BlockHash != common.Hash{}) { + jslog.BlockHash = &l.BlockHash + jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber) + } + return json.Marshal(jslog) } // UnmarshalJSON implements json.Umarshaler. -func (r *Log) UnmarshalJSON(input []byte) error { +func (l *Log) UnmarshalJSON(input []byte) error { var dec jsonLog if err := json.Unmarshal(input, &dec); err != nil { return err } - if dec.Address == nil || dec.Topics == nil || dec.Data == "" || dec.BlockNumber == "" || - dec.TxIndex == "" || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == "" { + if dec.Address == nil || dec.Topics == nil || dec.Data == nil || + dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil { return errMissingLogFields } declog := Log{ - Address: *dec.Address, - Topics: *dec.Topics, - TxHash: *dec.TxHash, - BlockHash: *dec.BlockHash, + Address: *dec.Address, + Topics: *dec.Topics, + Data: *dec.Data, + TxHash: *dec.TxHash, + TxIndex: uint(*dec.TxIndex), + Index: uint(*dec.Index), + Removed: dec.Removed, } - if _, err := fmt.Sscanf(dec.Data, "0x%x", &declog.Data); err != nil { - return fmt.Errorf("invalid hex log data") + // Block information may be missing if the log is received through + // the pending log filter, so it's handled specially here. + if dec.BlockHash != nil && dec.BlockNumber != nil { + declog.BlockHash = *dec.BlockHash + declog.BlockNumber = uint64(*dec.BlockNumber) } - if _, err := fmt.Sscanf(dec.BlockNumber, "0x%x", &declog.BlockNumber); err != nil { - return fmt.Errorf("invalid hex log block number") - } - if _, err := fmt.Sscanf(dec.TxIndex, "0x%x", &declog.TxIndex); err != nil { - return fmt.Errorf("invalid hex log tx index") - } - if _, err := fmt.Sscanf(dec.Index, "0x%x", &declog.Index); err != nil { - return fmt.Errorf("invalid hex log index") - } - *r = declog + *l = declog return nil } type Logs []*Log -// LogForStorage is a wrapper around a Log that flattens and parses the entire -// content of a log, as opposed to only the consensus fields originally (by hiding -// the rlp interface methods). +// LogForStorage is a wrapper around a Log that flattens and parses the entire content of +// a log including non-consensus fields. type LogForStorage Log + +// EncodeRLP implements rlp.Encoder. +func (l *LogForStorage) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rlpStorageLog{ + Address: l.Address, + Topics: l.Topics, + Data: l.Data, + BlockNumber: l.BlockNumber, + TxHash: l.TxHash, + TxIndex: l.TxIndex, + BlockHash: l.BlockHash, + Index: l.Index, + }) +} + +// DecodeRLP implements rlp.Decoder. +func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error { + var dec rlpStorageLog + err := s.Decode(&dec) + if err == nil { + *l = LogForStorage{ + Address: dec.Address, + Topics: dec.Topics, + Data: dec.Data, + BlockNumber: dec.BlockNumber, + TxHash: dec.TxHash, + TxIndex: dec.TxIndex, + BlockHash: dec.BlockHash, + Index: dec.Index, + } + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go index 9e13d703b..6a605a59c 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go @@ -65,7 +65,7 @@ type StructLog struct { // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type Tracer interface { - CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error + CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error } // StructLogger is an EVM state logger and implements Tracer. @@ -94,7 +94,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger { // captureState logs a new structured log message and pushes it out to the environment // // captureState also tracks SSTORE ops to track dirty values. -func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { +func (l *StructLogger) CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { // check if already accumulated the specified number of logs if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { return TraceLimitReachedError @@ -144,7 +144,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, storage = make(Storage) // Get the contract account and loop over each storage entry. This may involve looping over // the trie and is a very expensive process. - env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool { + env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool { storage[key] = value // Return true, indicating we'd like to continue. return true @@ -155,7 +155,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, } } // create a new snaptshot of the EVM. - log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err} + log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth, err} l.logs = append(l.logs, log) return nil diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/noop.go b/vendor/github.com/ethereum/go-ethereum/core/vm/noop.go new file mode 100644 index 000000000..ca7d1055a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/noop.go @@ -0,0 +1,68 @@ +// 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 . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool { + return true +} +func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {} + +type NoopEVMCallContext struct{} + +func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return nil, nil +} +func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return nil, nil +} +func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { + return nil, common.Address{}, nil +} +func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { + return nil, nil +} + +type NoopStateDB struct{} + +func (NoopStateDB) GetAccount(common.Address) Account { return nil } +func (NoopStateDB) CreateAccount(common.Address) Account { return nil } +func (NoopStateDB) SubBalance(common.Address, *big.Int) {} +func (NoopStateDB) AddBalance(common.Address, *big.Int) {} +func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil } +func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 } +func (NoopStateDB) SetNonce(common.Address, uint64) {} +func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} } +func (NoopStateDB) GetCode(common.Address) []byte { return nil } +func (NoopStateDB) SetCode(common.Address, []byte) {} +func (NoopStateDB) GetCodeSize(common.Address) int { return 0 } +func (NoopStateDB) AddRefund(*big.Int) {} +func (NoopStateDB) GetRefund() *big.Int { return nil } +func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} } +func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {} +func (NoopStateDB) Suicide(common.Address) bool { return false } +func (NoopStateDB) HasSuicided(common.Address) bool { return false } +func (NoopStateDB) Exist(common.Address) bool { return false } +func (NoopStateDB) Empty(common.Address) bool { return false } +func (NoopStateDB) RevertToSnapshot(int) {} +func (NoopStateDB) Snapshot() int { return 0 } +func (NoopStateDB) AddLog(*Log) {} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go index f1a2b60d3..3cf0dd024 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go @@ -23,92 +23,22 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" ) -// Env is a basic runtime environment required for running the EVM. -type Env struct { - chainConfig *params.ChainConfig - depth int - state *state.StateDB +func NewEnv(cfg *Config, state *state.StateDB) *vm.Environment { + context := vm.Context{ + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetHash: func(uint64) common.Hash { return common.Hash{} }, - origin common.Address - coinbase common.Address - - number *big.Int - time *big.Int - difficulty *big.Int - gasLimit *big.Int - - 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{ - chainConfig: cfg.ChainConfig, - state: state, - origin: cfg.Origin, - coinbase: cfg.Coinbase, - number: cfg.BlockNumber, - time: cfg.Time, - difficulty: cfg.Difficulty, - gasLimit: cfg.GasLimit, + Origin: cfg.Origin, + Coinbase: cfg.Coinbase, + BlockNumber: cfg.BlockNumber, + Time: cfg.Time, + Difficulty: cfg.Difficulty, + GasLimit: cfg.GasLimit, + GasPrice: new(big.Int), } - env.evm = vm.New(env, vm.Config{ - Debug: cfg.Debug, - EnableJit: !cfg.DisableJit, - ForceJit: !cfg.DisableJit, - }) - return env -} - -func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig } -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) SnapshotDatabase() int { - return self.state.Snapshot() -} -func (self *Env) RevertToSnapshot(snapshot int) { - self.state.RevertToSnapshot(snapshot) -} - -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) + return vm.NewEnvironment(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go index d51b435f8..3e99ed689 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go @@ -22,6 +22,7 @@ import ( "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" "github.com/ethereum/go-ethereum/params" @@ -49,6 +50,7 @@ type Config struct { Value *big.Int DisableJit bool // "disable" so it's enabled by default Debug bool + EVMConfig vm.Config State *state.StateDB GetHashFn func(n uint64) common.Hash @@ -123,13 +125,37 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { receiver.Address(), input, cfg.GasLimit, - cfg.GasPrice, cfg.Value, ) return ret, cfg.State, err } +// Create executes the code using the EVM create method +func Create(input []byte, cfg *Config) ([]byte, common.Address, 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) + ) + + // Call the code with the given configuration. + return vmenv.Create( + sender, + input, + cfg.GasLimit, + cfg.Value, + ) +} + // Call executes the code given by the contract's address. It will return the // EVM's return value or an error if it failed. // @@ -147,7 +173,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) { address, input, cfg.GasLimit, - cfg.GasPrice, cfg.Value, ) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go b/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go index 648d8a04a..47f535ab5 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go @@ -24,7 +24,7 @@ type jumpSeg struct { gas *big.Int } -func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { if !contract.UseGas(j.gas) { return nil, OutOfGasError } @@ -42,7 +42,7 @@ type pushSeg struct { gas *big.Int } -func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error if !contract.UseGas(s.gas) { diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go index 56aca6912..3521839df 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go @@ -30,10 +30,17 @@ import ( // Config are the configuration options for the EVM type Config struct { - Debug bool + // Debug enabled debugging EVM options + Debug bool + // EnableJit enabled the JIT VM EnableJit bool - ForceJit bool - Tracer Tracer + // ForceJit forces the JIT VM + ForceJit bool + // Tracer is the op code logger + Tracer Tracer + // NoRecursion disabled EVM call, callcode, + // delegate call and create. + NoRecursion bool } // EVM is used to run Ethereum based contracts and will utilise the @@ -41,26 +48,26 @@ type Config struct { // The EVM will run the byte code VM or JIT VM based on the passed // configuration. type EVM struct { - env Environment + env *Environment jumpTable vmJumpTable cfg Config gasTable params.GasTable } // New returns a new instance of the EVM. -func New(env Environment, cfg Config) *EVM { +func New(env *Environment, cfg Config) *EVM { return &EVM{ env: env, - jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()), + jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber), cfg: cfg, - gasTable: env.ChainConfig().GasTable(env.BlockNumber()), + gasTable: env.ChainConfig().GasTable(env.BlockNumber), } } // Run loops and evaluates the contract's code with the given input data func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { - evm.env.SetDepth(evm.env.Depth() + 1) - defer evm.env.SetDepth(evm.env.Depth() - 1) + evm.env.Depth++ + defer func() { evm.env.Depth-- }() if contract.CodeAddr != nil { if p := Precompiled[contract.CodeAddr.Str()]; p != nil { @@ -117,10 +124,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { code = contract.Code instrCount = 0 - op OpCode // current opcode - mem = NewMemory() // bound memory - stack = newstack() // local stack - statedb = evm.env.Db() // current state + op OpCode // current opcode + mem = NewMemory() // bound memory + stack = newstack() // local stack // For optimisation reason we're using uint64 as the program counter. // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible. pc = uint64(0) // program counter @@ -146,7 +152,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if err != nil && evm.cfg.Debug { - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, err) } }() @@ -174,7 +180,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { op = contract.GetOp(pc) //fmt.Printf("OP %d %v\n", op, op) // calculate the new memory size and gas price for the current executing opcode - newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack) + newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, mem, stack) if err != nil { return nil, err } @@ -189,7 +195,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { mem.Resize(newMemSize.Uint64()) // Add a log message if evm.cfg.Debug { - err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) + err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, nil) if err != nil { return nil, err } @@ -242,7 +248,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { +func calculateGasAndSize(gasTable params.GasTable, env *Environment, contract *Contract, caller ContractRef, op OpCode, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -260,21 +266,21 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co gas.Set(gasTable.Suicide) var ( address = common.BigToAddress(stack.data[len(stack.data)-1]) - eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber) ) if eip158 { // if empty and transfers value - if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 { + if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 { gas.Add(gas, gasTable.CreateBySuicide) } - } else if !env.Db().Exist(address) { + } else if !env.StateDB.Exist(address) { gas.Add(gas, gasTable.CreateBySuicide) } } - if !statedb.HasSuicided(contract.Address()) { - statedb.AddRefund(params.SuicideRefundGas) + if !env.StateDB.HasSuicided(contract.Address()) { + env.StateDB.AddRefund(params.SuicideRefundGas) } case EXTCODESIZE: gas.Set(gasTable.ExtcodeSize) @@ -323,7 +329,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co var g *big.Int y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] - val := statedb.GetState(contract.Address(), common.BigToHash(x)) + val := env.StateDB.GetState(contract.Address(), common.BigToHash(x)) // This checks for 3 scenario's and calculates gas accordingly // 1. From a zero-value address to a non-zero value (NEW VALUE) @@ -333,7 +339,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co // 0 => non 0 g = params.SstoreSetGas } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { - statedb.AddRefund(params.SstoreRefundGas) + env.StateDB.AddRefund(params.SstoreRefundGas) g = params.SstoreClearGas } else { @@ -394,13 +400,13 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co if op == CALL { var ( address = common.BigToAddress(stack.data[len(stack.data)-2]) - eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber) ) if eip158 { - if env.Db().Empty(address) && transfersValue { + if env.StateDB.Empty(address) && transfersValue { gas.Add(gas, params.CallNewAccountGas) } - } else if !env.Db().Exist(address) { + } else if !env.StateDB.Exist(address) { gas.Add(gas, params.CallNewAccountGas) } } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go b/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go index 4fa98ccd9..44b60abf6 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go @@ -17,10 +17,3 @@ // +build !evmjit package vm - -import "fmt" - -func NewJitVm(env Environment) VirtualMachine { - fmt.Printf("Warning! EVM JIT not enabled.\n") - return New(env, Config{}) -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm_env.go b/vendor/github.com/ethereum/go-ethereum/core/vm_env.go index 43637bd13..58e71e305 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm_env.go @@ -15,104 +15,3 @@ // along with the go-ethereum library. If not, see . package core - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" -) - -// GetHashFn returns a function for which the VM env can query block hashes through -// up to the limit defined by the Yellow Paper and uses the given block chain -// to query for information. -func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash { - return func(n uint64) common.Hash { - for block := chain.GetBlockByHash(ref); block != nil; block = chain.GetBlock(block.ParentHash(), block.NumberU64()-1) { - if block.NumberU64() == n { - return block.Hash() - } - } - - return common.Hash{} - } -} - -type VMEnv struct { - chainConfig *params.ChainConfig // Chain configuration - state *state.StateDB // State to use for executing - evm *vm.EVM // The Ethereum Virtual Machine - depth int // Current execution depth - msg Message // Message appliod - - header *types.Header // Header information - chain *BlockChain // Blockchain handle - getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes -} - -func NewEnv(state *state.StateDB, chainConfig *params.ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv { - env := &VMEnv{ - chainConfig: chainConfig, - chain: chain, - state: state, - header: header, - msg: msg, - getHashFn: GetHashFn(header.ParentHash, chain), - } - - env.evm = vm.New(env, cfg) - return env -} - -func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Origin() common.Address { return self.msg.From() } -func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } -func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } -func (self *VMEnv) Time() *big.Int { return self.header.Time } -func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } -func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } -func (self *VMEnv) Value() *big.Int { return self.msg.Value() } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) Depth() int { return self.depth } -func (self *VMEnv) SetDepth(i int) { self.depth = i } -func (self *VMEnv) GetHash(n uint64) common.Hash { - return self.getHashFn(n) -} - -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) SnapshotDatabase() int { - return self.state.Snapshot() -} - -func (self *VMEnv) RevertToSnapshot(snapshot int) { - self.state.RevertToSnapshot(snapshot) -} - -func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { - Transfer(from, to, amount) -} - -func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return Call(self, me, addr, data, gas, price, value) -} -func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return CallCode(self, me, addr, data, gas, price, value) -} - -func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { - return DelegateCall(self, me, addr, data, gas, price) -} - -func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { - return Create(self, me, data, gas, price, value) -} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api.go b/vendor/github.com/ethereum/go-ethereum/eth/api.go index b3185c392..a86ed95cf 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api.go @@ -515,9 +515,11 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. if err != nil { return nil, fmt.Errorf("sender retrieval failed: %v", err) } + context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain()) + // Mutate the state if we haven't reached the tracing transaction yet if uint64(idx) < txIndex { - vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{}) + vmenv := vm.NewEnvironment(context, stateDb, api.config, vm.Config{}) _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("mutation failed: %v", err) @@ -525,8 +527,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. stateDb.DeleteSuicides() continue } - // Otherwise trace the transaction and return - vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer}) + + vmenv := vm.NewEnvironment(context, stateDb, api.config, vm.Config{Debug: true, Tracer: tracer}) ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go index 0925132ef..b95ef79c5 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go @@ -56,7 +56,7 @@ func (b *EthApiBackend) SetHead(number uint64) { 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() + block := b.eth.miner.PendingBlock() return block.Header(), nil } // Otherwise resolve and return the block @@ -69,7 +69,7 @@ func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNum func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { // Pending block is only known by the miner if blockNr == rpc.PendingBlockNumber { - block, _ := b.eth.miner.Pending() + block := b.eth.miner.PendingBlock() return block, nil } // Otherwise resolve and return the block @@ -106,12 +106,14 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(blockHash) } -func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { +func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) { statedb := state.(EthApiState).state from := statedb.GetOrNewStateObject(msg.From()) from.SetBalance(common.MaxBig) vmError := func() error { return nil } - return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil + + context := core.NewEVMContext(msg, header, b.eth.BlockChain()) + return vm.NewEnvironment(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil } func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend.go b/vendor/github.com/ethereum/go-ethereum/eth/backend.go index 2351a62c8..d5b767b12 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/backend.go @@ -31,8 +31,6 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/httpclient" - "github.com/ethereum/go-ethereum/common/registrar/ethreg" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" @@ -127,7 +125,6 @@ type Ethereum struct { eventMux *event.TypeMux pow *ethash.Ethash - httpclient *httpclient.HTTPClient accountManager *accounts.Manager ApiBackend *EthApiBackend @@ -173,7 +170,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { pow: pow, shutdownChan: make(chan bool), stopDbUpgrade: stopDbUpgrade, - httpclient: httpclient.New(config.DocRoot), netVersionId: config.NetworkId, NatSpec: config.NatSpec, PowTest: config.PowTest, @@ -356,10 +352,6 @@ func (s *Ethereum) APIs() []rpc.API { Version: "1.0", Service: s.netRPCService, Public: true, - }, { - Namespace: "admin", - Version: "1.0", - Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager), }, }...) } @@ -527,12 +519,6 @@ func (self *Ethereum) StopAutoDAG() { glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir) } -// HTTPClient returns the light http client used for fetching offchain docs -// (natspec, source for verification) -func (self *Ethereum) HTTPClient() *httpclient.HTTPClient { - return self.httpclient -} - // dagFiles(epoch) returns the two alternative DAG filenames (not a path) // 1) - 2) full-R- func dagFiles(epoch uint64) (string, string) { diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go index 834513262..bbb34d3de 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go @@ -17,7 +17,6 @@ package filters import ( - "encoding/hex" "encoding/json" "errors" "fmt" @@ -28,7 +27,9 @@ import ( "golang.org/x/net/context" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "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" "github.com/ethereum/go-ethereum/rpc" @@ -45,7 +46,7 @@ type filter struct { deadline *time.Timer // filter is inactiv when deadline triggers hashes []common.Hash crit FilterCriteria - logs []Log + logs []*vm.Log s *Subscription // associated subscription in event system } @@ -239,11 +240,17 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported } - rpcSub := notifier.CreateSubscription() + var ( + rpcSub = notifier.CreateSubscription() + matchedLogs = make(chan []*vm.Log) + ) + + logsSub, err := api.events.SubscribeLogs(crit, matchedLogs) + if err != nil { + return nil, err + } go func() { - matchedLogs := make(chan []Log) - logsSub := api.events.SubscribeLogs(crit, matchedLogs) for { select { @@ -276,22 +283,24 @@ type FilterCriteria struct { // used to retrieve logs when the state changes. This method cannot be // used to fetch logs that are already stored in the state. // +// Default criteria for the from and to block are "latest". +// Using "latest" as block number will return logs for mined blocks. +// Using "pending" as block number returns logs for not yet mined (pending) blocks. +// In case logs are removed (chain reorg) previously returned logs are returned +// again but with the removed property set to true. +// +// In case "fromBlock" > "toBlock" an error is returned. +// // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter -func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID { - var ( - logs = make(chan []Log) - logsSub = api.events.SubscribeLogs(crit, logs) - ) - - if crit.FromBlock == nil { - crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) - } - if crit.ToBlock == nil { - crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) +func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { + logs := make(chan []*vm.Log) + logsSub, err := api.events.SubscribeLogs(crit, logs) + if err != nil { + return rpc.ID(""), err } api.filtersMu.Lock() - api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]Log, 0), s: logsSub} + api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*vm.Log, 0), s: logsSub} api.filtersMu.Unlock() go func() { @@ -312,13 +321,13 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID { } }() - return logsSub.ID + return logsSub.ID, nil } // GetLogs returns logs matching the given argument that are stored within the state. // // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs -func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]Log, error) { +func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*vm.Log, error) { if crit.FromBlock == nil { crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) } @@ -357,34 +366,44 @@ func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { // If the filter could not be found an empty array of logs is returned. // // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs -func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log, error) { +func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*vm.Log, error) { api.filtersMu.Lock() f, found := api.filters[id] api.filtersMu.Unlock() if !found || f.typ != LogsSubscription { - return []Log{}, nil + return nil, fmt.Errorf("filter not found") } filter := New(api.backend, api.useMipMap) - filter.SetBeginBlock(f.crit.FromBlock.Int64()) - filter.SetEndBlock(f.crit.ToBlock.Int64()) + if f.crit.FromBlock != nil { + filter.SetBeginBlock(f.crit.FromBlock.Int64()) + } else { + filter.SetBeginBlock(rpc.LatestBlockNumber.Int64()) + } + if f.crit.ToBlock != nil { + filter.SetEndBlock(f.crit.ToBlock.Int64()) + } else { + filter.SetEndBlock(rpc.LatestBlockNumber.Int64()) + } filter.SetAddresses(f.crit.Addresses) filter.SetTopics(f.crit.Topics) logs, err := filter.Find(ctx) - return returnLogs(logs), err + if err != nil { + return nil, err + } + return returnLogs(logs), nil } // GetFilterChanges returns the logs for the filter with the given id since // last time is was called. This can be used for polling. // // For pending transaction and block filters the result is []common.Hash. -// (pending)Log filters return []Log. If the filter could not be found -// []interface{}{} is returned. +// (pending)Log filters return []Log. // // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges -func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} { +func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { api.filtersMu.Lock() defer api.filtersMu.Unlock() @@ -400,15 +419,15 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} { case PendingTransactionsSubscription, BlocksSubscription: hashes := f.hashes f.hashes = nil - return returnHashes(hashes) - case PendingLogsSubscription, LogsSubscription: + return returnHashes(hashes), nil + case LogsSubscription: logs := f.logs f.logs = nil - return returnLogs(logs) + return returnLogs(logs), nil } } - return []interface{}{} + return []interface{}{}, fmt.Errorf("filter not found") } // returnHashes is a helper that will return an empty hash array case the given hash array is nil, @@ -422,9 +441,9 @@ func returnHashes(hashes []common.Hash) []common.Hash { // returnLogs is a helper that will return an empty log array in case the given logs array is nil, // otherwise the given logs array is returned. -func returnLogs(logs []Log) []Log { +func returnLogs(logs []*vm.Log) []*vm.Log { if logs == nil { - return []Log{} + return []*vm.Log{} } return logs } @@ -443,15 +462,11 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error { return err } - if raw.From == nil || raw.From.Int64() < 0 { - args.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) - } else { + if raw.From != nil { args.FromBlock = big.NewInt(raw.From.Int64()) } - if raw.ToBlock == nil || raw.ToBlock.Int64() < 0 { - args.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) - } else { + if raw.ToBlock != nil { args.ToBlock = big.NewInt(raw.ToBlock.Int64()) } @@ -459,52 +474,28 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error { if raw.Addresses != nil { // raw.Address can contain a single address or an array of addresses - var addresses []common.Address - if strAddrs, ok := raw.Addresses.([]interface{}); ok { - for i, addr := range strAddrs { + switch rawAddr := raw.Addresses.(type) { + case []interface{}: + for i, addr := range rawAddr { if strAddr, ok := addr.(string); ok { - if len(strAddr) >= 2 && strAddr[0] == '0' && (strAddr[1] == 'x' || strAddr[1] == 'X') { - strAddr = strAddr[2:] - } - if decAddr, err := hex.DecodeString(strAddr); err == nil { - addresses = append(addresses, common.BytesToAddress(decAddr)) - } else { - return fmt.Errorf("invalid address given") + addr, err := decodeAddress(strAddr) + if err != nil { + return fmt.Errorf("invalid address at index %d: %v", i, err) } + args.Addresses = append(args.Addresses, addr) } else { - return fmt.Errorf("invalid address on index %d", i) + return fmt.Errorf("non-string address at index %d", i) } } - } else if singleAddr, ok := raw.Addresses.(string); ok { - if len(singleAddr) >= 2 && singleAddr[0] == '0' && (singleAddr[1] == 'x' || singleAddr[1] == 'X') { - singleAddr = singleAddr[2:] + case string: + addr, err := decodeAddress(rawAddr) + if err != nil { + return fmt.Errorf("invalid address: %v", err) } - if decAddr, err := hex.DecodeString(singleAddr); err == nil { - addresses = append(addresses, common.BytesToAddress(decAddr)) - } else { - return fmt.Errorf("invalid address given") - } - } else { - return errors.New("invalid address(es) given") + args.Addresses = []common.Address{addr} + default: + return errors.New("invalid addresses in query") } - args.Addresses = addresses - } - - // helper function which parses a string to a topic hash - topicConverter := func(raw string) (common.Hash, error) { - if len(raw) == 0 { - return common.Hash{}, nil - } - if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') { - raw = raw[2:] - } - if len(raw) != 2*common.HashLength { - return common.Hash{}, errors.New("invalid topic(s)") - } - if decAddr, err := hex.DecodeString(raw); err == nil { - return common.BytesToHash(decAddr), nil - } - return common.Hash{}, errors.New("invalid topic(s)") } // topics is an array consisting of strings and/or arrays of strings. @@ -512,20 +503,25 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error { if len(raw.Topics) > 0 { args.Topics = make([][]common.Hash, len(raw.Topics)) for i, t := range raw.Topics { - if t == nil { // ignore topic when matching logs + switch topic := t.(type) { + case nil: + // ignore topic when matching logs args.Topics[i] = []common.Hash{common.Hash{}} - } else if topic, ok := t.(string); ok { // match specific topic - top, err := topicConverter(topic) + + case string: + // match specific topic + top, err := decodeTopic(topic) if err != nil { return err } args.Topics[i] = []common.Hash{top} - } else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"] - for _, rawTopic := range topics { + case []interface{}: + // or case e.g. [null, "topic0", "topic1"] + for _, rawTopic := range topic { if rawTopic == nil { args.Topics[i] = append(args.Topics[i], common.Hash{}) } else if topic, ok := rawTopic.(string); ok { - parsed, err := topicConverter(topic) + parsed, err := decodeTopic(topic) if err != nil { return err } @@ -534,7 +530,7 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error { return fmt.Errorf("invalid topic(s)") } } - } else { + default: return fmt.Errorf("invalid topic(s)") } } @@ -542,3 +538,19 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error { return nil } + +func decodeAddress(s string) (common.Address, error) { + b, err := hexutil.Decode(s) + if err == nil && len(b) != common.AddressLength { + err = fmt.Errorf("hex has invalid length %d after decoding", len(b)) + } + return common.BytesToAddress(b), err +} + +func decodeTopic(s string) (common.Hash, error) { + b, err := hexutil.Decode(s) + if err == nil && len(b) != common.HashLength { + err = fmt.Errorf("hex has invalid length %d after decoding", len(b)) + } + return common.BytesToHash(b), err +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go index 4004af300..a695d7eb7 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go @@ -20,9 +20,12 @@ import ( "math" "time" + "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/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" @@ -36,7 +39,7 @@ type Backend interface { GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) } -// Filter can be used to retrieve and filter logs +// Filter can be used to retrieve and filter logs. type Filter struct { backend Backend useMipMap bool @@ -83,7 +86,7 @@ func (f *Filter) SetTopics(topics [][]common.Hash) { } // Run filters logs with the current parameters set -func (f *Filter) Find(ctx context.Context) ([]Log, error) { +func (f *Filter) Find(ctx context.Context) ([]*vm.Log, error) { head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) if head == nil { return nil, nil @@ -108,7 +111,7 @@ func (f *Filter) Find(ctx context.Context) ([]Log, error) { return f.mipFind(beginBlockNo, endBlockNo, 0), nil } -func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { +func (f *Filter) mipFind(start, end uint64, depth int) (logs []*vm.Log) { level := core.MIPMapLevels[depth] // normalise numerator so we can work in level specific batches and // work with the proper range checks @@ -139,7 +142,7 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { return logs } -func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) { +func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*vm.Log, err error) { for i := start; i <= end; i++ { header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i)) if header == nil || err != nil { @@ -154,15 +157,11 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er if err != nil { return nil, err } - var unfiltered []Log + var unfiltered []*vm.Log for _, receipt := range receipts { - rl := make([]Log, len(receipt.Logs)) - for i, l := range receipt.Logs { - rl[i] = Log{l, false} - } - unfiltered = append(unfiltered, rl...) + unfiltered = append(unfiltered, ([]*vm.Log)(receipt.Logs)...) } - logs = append(logs, filterLogs(unfiltered, f.addresses, f.topics)...) + logs = append(logs, filterLogs(unfiltered, nil, nil, f.addresses, f.topics)...) } } @@ -179,12 +178,18 @@ func includes(addresses []common.Address, a common.Address) bool { return false } -func filterLogs(logs []Log, addresses []common.Address, topics [][]common.Hash) []Log { - var ret []Log - - // Filter the logs for interesting stuff +// filterLogs creates a slice of logs matching the given criteria. +func filterLogs(logs []*vm.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*vm.Log { + var ret []*vm.Log Logs: for _, log := range logs { + if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { + continue + } + if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { + continue + } + if len(addresses) > 0 && !includes(addresses, log.Address) { continue } @@ -211,7 +216,6 @@ Logs: continue Logs } } - ret = append(ret, log) } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go index c2c072a9f..1b360cfdb 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go @@ -19,7 +19,6 @@ package filters import ( - "encoding/json" "errors" "fmt" "sync" @@ -43,48 +42,29 @@ const ( UnknownSubscription Type = iota // LogsSubscription queries for new or removed (chain reorg) logs LogsSubscription - // PendingLogsSubscription queries for logs for the pending block + // PendingLogsSubscription queries for logs in pending blocks PendingLogsSubscription + // MinedAndPendingLogsSubscription queries for logs in mined and pending blocks. + MinedAndPendingLogsSubscription // PendingTransactionsSubscription queries tx hashes for pending // transactions entering the pending state PendingTransactionsSubscription // BlocksSubscription queries hashes for blocks that are imported BlocksSubscription + // LastSubscription keeps track of the last index + LastIndexSubscription ) var ( ErrInvalidSubscriptionID = errors.New("invalid id") ) -// Log is a helper that can hold additional information about vm.Log -// necessary for the RPC interface. -type Log struct { - *vm.Log - Removed bool `json:"removed"` -} - -func (l *Log) MarshalJSON() ([]byte, error) { - fields := map[string]interface{}{ - "address": l.Address, - "data": fmt.Sprintf("0x%x", l.Data), - "blockNumber": fmt.Sprintf("%#x", l.BlockNumber), - "logIndex": fmt.Sprintf("%#x", l.Index), - "blockHash": l.BlockHash, - "transactionHash": l.TxHash, - "transactionIndex": fmt.Sprintf("%#x", l.TxIndex), - "topics": l.Topics, - "removed": l.Removed, - } - - return json.Marshal(fields) -} - type subscription struct { id rpc.ID typ Type created time.Time logsCrit FilterCriteria - logs chan []Log + logs chan []*vm.Log hashes chan common.Hash headers chan *types.Header installed chan struct{} // closed when the filter is installed @@ -169,8 +149,65 @@ func (es *EventSystem) subscribe(sub *subscription) *Subscription { } // SubscribeLogs creates a subscription that will write all logs matching the +// given criteria to the given logs channel. Default value for the from and to +// block is "latest". If the fromBlock > toBlock an error is returned. +func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []*vm.Log) (*Subscription, error) { + var from, to rpc.BlockNumber + if crit.FromBlock == nil { + from = rpc.LatestBlockNumber + } else { + from = rpc.BlockNumber(crit.FromBlock.Int64()) + } + if crit.ToBlock == nil { + to = rpc.LatestBlockNumber + } else { + to = rpc.BlockNumber(crit.ToBlock.Int64()) + } + + // only interested in pending logs + if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber { + return es.subscribePendingLogs(crit, logs), nil + } + // only interested in new mined logs + if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber { + return es.subscribeLogs(crit, logs), nil + } + // only interested in mined logs within a specific block range + if from >= 0 && to >= 0 && to >= from { + return es.subscribeLogs(crit, logs), nil + } + // interested in mined logs from a specific block number, new logs and pending logs + if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber { + return es.subscribeMinedPendingLogs(crit, logs), nil + } + // interested in logs from a specific block number to new mined blocks + if from >= 0 && to == rpc.LatestBlockNumber { + return es.subscribeLogs(crit, logs), nil + } + return nil, fmt.Errorf("invalid from and to block combination: from > to") +} + +// subscribeMinedPendingLogs creates a subscription that returned mined and +// pending logs that match the given criteria. +func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []*vm.Log) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: MinedAndPendingLogsSubscription, + logsCrit: crit, + created: time.Now(), + logs: logs, + hashes: make(chan common.Hash), + headers: make(chan *types.Header), + installed: make(chan struct{}), + err: make(chan error), + } + + return es.subscribe(sub) +} + +// subscribeLogs creates a subscription that will write all logs matching the // given criteria to the given logs channel. -func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subscription { +func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []*vm.Log) *Subscription { sub := &subscription{ id: rpc.NewID(), typ: LogsSubscription, @@ -186,9 +223,9 @@ func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subs return es.subscribe(sub) } -// SubscribePendingLogs creates a subscription that will write pending logs matching the -// given criteria to the given channel. -func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log) *Subscription { +// subscribePendingLogs creates a subscription that writes transaction hashes for +// transactions that enter the transaction pool. +func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []*vm.Log) *Subscription { sub := &subscription{ id: rpc.NewID(), typ: PendingLogsSubscription, @@ -204,23 +241,6 @@ func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log return es.subscribe(sub) } -// SubscribePendingTxEvents creates a sbuscription that writes transaction hashes for -// transactions that enter the transaction pool. -func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription { - sub := &subscription{ - id: rpc.NewID(), - typ: PendingTransactionsSubscription, - created: time.Now(), - logs: make(chan []Log), - hashes: hashes, - headers: make(chan *types.Header), - installed: make(chan struct{}), - err: make(chan error), - } - - return es.subscribe(sub) -} - // SubscribeNewHeads creates a subscription that writes the header of a block that is // imported in the chain. func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription { @@ -228,7 +248,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti id: rpc.NewID(), typ: BlocksSubscription, created: time.Now(), - logs: make(chan []Log), + logs: make(chan []*vm.Log), hashes: make(chan common.Hash), headers: headers, installed: make(chan struct{}), @@ -238,6 +258,23 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti return es.subscribe(sub) } +// SubscribePendingTxEvents creates a subscription that writes transaction hashes for +// transactions that enter the transaction pool. +func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: PendingTransactionsSubscription, + created: time.Now(), + logs: make(chan []*vm.Log), + hashes: hashes, + headers: make(chan *types.Header), + installed: make(chan struct{}), + err: make(chan error), + } + + return es.subscribe(sub) +} + type filterIndex map[Type]map[rpc.ID]*subscription // broadcast event to filters that match criteria. @@ -251,7 +288,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) { if len(e) > 0 { for _, f := range filters[LogsSubscription] { if ev.Time.After(f.created) { - if matchedLogs := filterLogs(convertLogs(e, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { + if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { f.logs <- matchedLogs } } @@ -260,7 +297,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) { case core.RemovedLogsEvent: for _, f := range filters[LogsSubscription] { if ev.Time.After(f.created) { - if matchedLogs := filterLogs(convertLogs(e.Logs, true), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { + if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { f.logs <- matchedLogs } } @@ -268,7 +305,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) { case core.PendingLogsEvent: for _, f := range filters[PendingLogsSubscription] { if ev.Time.After(f.created) { - if matchedLogs := filterLogs(convertLogs(e.Logs, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { + if matchedLogs := filterLogs(e.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { f.logs <- matchedLogs } } @@ -333,26 +370,23 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func } // filter logs of a single header in light client mode -func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []Log { - //fmt.Println("lightFilterLogs", header.Number.Uint64(), remove) +func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*vm.Log { if bloomFilter(header.Bloom, addresses, topics) { - //fmt.Println("bloom match") // Get the logs of the block ctx, _ := context.WithTimeout(context.Background(), time.Second*5) receipts, err := es.backend.GetReceipts(ctx, header.Hash()) if err != nil { return nil } - var unfiltered []Log + var unfiltered []*vm.Log for _, receipt := range receipts { - rl := make([]Log, len(receipt.Logs)) - for i, l := range receipt.Logs { - rl[i] = Log{l, remove} + for _, log := range receipt.Logs { + logcopy := *log + logcopy.Removed = remove + unfiltered = append(unfiltered, &logcopy) } - unfiltered = append(unfiltered, rl...) } - logs := filterLogs(unfiltered, addresses, topics) - //fmt.Println("found", len(logs)) + logs := filterLogs(unfiltered, nil, nil, addresses, topics) return logs } return nil @@ -364,6 +398,11 @@ func (es *EventSystem) eventLoop() { index = make(filterIndex) sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, vm.Logs{}, core.TxPreEvent{}, core.ChainEvent{}) ) + + for i := UnknownSubscription; i < LastIndexSubscription; i++ { + index[i] = make(map[rpc.ID]*subscription) + } + for { select { case ev, active := <-sub.Chan(): @@ -372,23 +411,23 @@ func (es *EventSystem) eventLoop() { } es.broadcast(index, ev) case f := <-es.install: - if _, found := index[f.typ]; !found { - index[f.typ] = make(map[rpc.ID]*subscription) + if f.typ == MinedAndPendingLogsSubscription { + // the type are logs and pending logs subscriptions + index[LogsSubscription][f.id] = f + index[PendingLogsSubscription][f.id] = f + } else { + index[f.typ][f.id] = f } - index[f.typ][f.id] = f close(f.installed) case f := <-es.uninstall: - delete(index[f.typ], f.id) + if f.typ == MinedAndPendingLogsSubscription { + // the type are logs and pending logs subscriptions + delete(index[LogsSubscription], f.id) + delete(index[PendingLogsSubscription], f.id) + } else { + delete(index[f.typ], f.id) + } close(f.err) } } } - -// convertLogs is a helper utility that converts vm.Logs to []filter.Log. -func convertLogs(in vm.Logs, removed bool) []Log { - logs := make([]Log, len(in)) - for i, l := range in { - logs[i] = Log{l, removed} - } - return logs -} diff --git a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go index a095aa076..4daebda92 100644 --- a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go +++ b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/rlp" @@ -80,6 +81,8 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface err := ec.c.CallContext(ctx, &raw, method, args...) if err != nil { return nil, err + } else if len(raw) == 0 { + return nil, ethereum.NotFound } // Decode header and transactions. var head *types.Header @@ -111,7 +114,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface for i := range reqs { reqs[i] = rpc.BatchElem{ Method: "eth_getUncleByBlockHashAndIndex", - Args: []interface{}{body.Hash, fmt.Sprintf("%#x", i)}, + Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))}, Result: &uncles[i], } } @@ -122,6 +125,9 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface if reqs[i].Error != nil { return nil, reqs[i].Error } + if uncles[i] == nil { + return nil, fmt.Errorf("got null header for uncle %d of block %x", i, body.Hash[:]) + } } } return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil @@ -131,6 +137,9 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { var head *types.Header err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false) + if err == nil && head == nil { + err = ethereum.NotFound + } return head, err } @@ -139,26 +148,38 @@ func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { var head *types.Header err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) + if err == nil && head == nil { + err = ethereum.NotFound + } return head, err } // TransactionByHash returns the transaction with the given hash. -func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) { - var tx *types.Transaction - err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) - if err == nil { - if _, r, _ := tx.RawSignatureValues(); r == nil { - return nil, fmt.Errorf("server returned transaction without signature") - } +func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { + var raw json.RawMessage + err = ec.c.CallContext(ctx, &raw, "eth_getTransactionByHash", hash) + if err != nil { + return nil, false, err + } else if len(raw) == 0 { + return nil, false, ethereum.NotFound } - return tx, err + if err := json.Unmarshal(raw, tx); err != nil { + return nil, false, err + } else if _, r, _ := tx.RawSignatureValues(); r == nil { + return nil, false, fmt.Errorf("server returned transaction without signature") + } + var block struct{ BlockHash *common.Hash } + if err := json.Unmarshal(raw, &block); err != nil { + return nil, false, err + } + return tx, block.BlockHash == nil, nil } // TransactionCount returns the total number of transactions in the given block. func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { - var num rpc.HexNumber + var num hexutil.Uint err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash) - return num.Uint(), err + return uint(num), err } // TransactionInBlock returns a single transaction at index in the given block. @@ -166,11 +187,9 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, var tx *types.Transaction err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, index) if err == nil { - var signer types.Signer = types.HomesteadSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } - if _, r, _ := types.SignatureValues(signer, tx); r == nil { + if tx == nil { + return nil, ethereum.NotFound + } else if _, r, _ := tx.RawSignatureValues(); r == nil { return nil, fmt.Errorf("server returned transaction without signature") } } @@ -182,8 +201,12 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { var r *types.Receipt err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash) - if err == nil && r != nil && len(r.PostState) == 0 { - return nil, fmt.Errorf("server returned receipt without post state") + if err == nil { + if r == nil { + return nil, ethereum.NotFound + } else if len(r.PostState) == 0 { + return nil, fmt.Errorf("server returned receipt without post state") + } } return r, err } @@ -192,15 +215,15 @@ func toBlockNumArg(number *big.Int) string { if number == nil { return "latest" } - return fmt.Sprintf("%#x", number) + return hexutil.EncodeBig(number) } type rpcProgress struct { - StartingBlock rpc.HexNumber - CurrentBlock rpc.HexNumber - HighestBlock rpc.HexNumber - PulledStates rpc.HexNumber - KnownStates rpc.HexNumber + StartingBlock hexutil.Uint64 + CurrentBlock hexutil.Uint64 + HighestBlock hexutil.Uint64 + PulledStates hexutil.Uint64 + KnownStates hexutil.Uint64 } // SyncProgress retrieves the current progress of the sync algorithm. If there's @@ -220,11 +243,11 @@ func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, err return nil, err } return ðereum.SyncProgress{ - StartingBlock: progress.StartingBlock.Uint64(), - CurrentBlock: progress.CurrentBlock.Uint64(), - HighestBlock: progress.HighestBlock.Uint64(), - PulledStates: progress.PulledStates.Uint64(), - KnownStates: progress.KnownStates.Uint64(), + StartingBlock: uint64(progress.StartingBlock), + CurrentBlock: uint64(progress.CurrentBlock), + HighestBlock: uint64(progress.HighestBlock), + PulledStates: uint64(progress.PulledStates), + KnownStates: uint64(progress.KnownStates), }, nil } @@ -239,7 +262,7 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) // BalanceAt returns the wei balance of the given account. // The block number can be nil, in which case the balance is taken from the latest known block. func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { - var result rpc.HexNumber + var result hexutil.Big err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber)) return (*big.Int)(&result), err } @@ -247,7 +270,7 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNu // StorageAt returns the value of key in the contract storage of the given account. // The block number can be nil, in which case the value is taken from the latest known block. func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { - var result rpc.HexBytes + var result hexutil.Bytes err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber)) return result, err } @@ -255,7 +278,7 @@ func (ec *Client) StorageAt(ctx context.Context, account common.Address, key com // CodeAt returns the contract code of the given account. // The block number can be nil, in which case the code is taken from the latest known block. func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - var result rpc.HexBytes + var result hexutil.Bytes err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber)) return result, err } @@ -263,9 +286,9 @@ func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumbe // NonceAt returns the account nonce of the given account. // The block number can be nil, in which case the nonce is taken from the latest known block. func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { - var result rpc.HexNumber + var result hexutil.Uint64 err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber)) - return result.Uint64(), err + return uint64(result), err } // Filters @@ -286,7 +309,7 @@ func toFilterArg(q ethereum.FilterQuery) interface{} { arg := map[string]interface{}{ "fromBlock": toBlockNumArg(q.FromBlock), "toBlock": toBlockNumArg(q.ToBlock), - "addresses": q.Addresses, + "address": q.Addresses, "topics": q.Topics, } if q.FromBlock == nil { @@ -299,21 +322,21 @@ func toFilterArg(q ethereum.FilterQuery) interface{} { // PendingBalanceAt returns the wei balance of the given account in the pending state. func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { - var result rpc.HexNumber + var result hexutil.Big err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending") return (*big.Int)(&result), err } // PendingStorageAt returns the value of key in the contract storage of the given account in the pending state. func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { - var result rpc.HexBytes + var result hexutil.Bytes err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending") return result, err } // PendingCodeAt returns the contract code of the given account in the pending state. func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { - var result rpc.HexBytes + var result hexutil.Bytes err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending") return result, err } @@ -321,16 +344,16 @@ func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([] // PendingNonceAt returns the account nonce of the given account in the pending state. // This is the nonce that should be used for the next transaction. func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - var result rpc.HexNumber + var result hexutil.Uint64 err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending") - return result.Uint64(), err + return uint64(result), err } // PendingTransactionCount returns the total number of transactions in the pending state. func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { - var num rpc.HexNumber + var num hexutil.Uint err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending") - return num.Uint(), err + return uint(num), err } // TODO: SubscribePendingTransactions (needs server side) @@ -344,29 +367,29 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { // case the code is taken from the latest known block. Note that state from very old // blocks might not be available. func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { - var hex string + var hex hexutil.Bytes err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber)) if err != nil { return nil, err } - return common.FromHex(hex), nil + return hex, nil } // PendingCallContract executes a message call transaction using the EVM. // The state seen by the contract call is the pending state. func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { - var hex string + var hex hexutil.Bytes err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending") if err != nil { return nil, err } - return common.FromHex(hex), nil + return hex, nil } // SuggestGasPrice retrieves the currently suggested gas price to allow a timely // execution of a transaction. func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - var hex rpc.HexNumber + var hex hexutil.Big if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { return nil, err } @@ -378,7 +401,7 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { // the true gas limit requirement as other transactions may be added or removed by miners, // but it should provide a basis for setting a reasonable default. func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { - var hex rpc.HexNumber + var hex hexutil.Big err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) if err != nil { return nil, err @@ -404,16 +427,16 @@ func toCallArg(msg ethereum.CallMsg) interface{} { "to": msg.To, } if len(msg.Data) > 0 { - arg["data"] = fmt.Sprintf("%#x", msg.Data) + arg["data"] = hexutil.Bytes(msg.Data) } if msg.Value != nil { - arg["value"] = fmt.Sprintf("%#x", msg.Value) + arg["value"] = (*hexutil.Big)(msg.Value) } if msg.Gas != nil { - arg["gas"] = fmt.Sprintf("%#x", msg.Gas) + arg["gas"] = (*hexutil.Big)(msg.Gas) } if msg.GasPrice != nil { - arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice) + arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } return arg } diff --git a/vendor/github.com/ethereum/go-ethereum/ethstats/ethstats.go b/vendor/github.com/ethereum/go-ethereum/ethstats/ethstats.go new file mode 100644 index 000000000..a5fa84468 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethstats/ethstats.go @@ -0,0 +1,466 @@ +// 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 . + +// Package ethstats implements the network stats reporting service. +package ethstats + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "regexp" + "runtime" + "strconv" + "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/event" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/websocket" +) + +// Service implements an Ethereum netstats reporting daemon that pushes local +// chain statistics up to a monitoring server. +type Service struct { + stack *node.Node // Temporary workaround, remove when API finalized + + server *p2p.Server // Peer-to-peer server to retrieve networking infos + eth *eth.Ethereum // Full Ethereum service if monitoring a full node + les *les.LightEthereum // Light Ethereum service if monitoring a light node + + node string // Name of the node to display on the monitoring page + pass string // Password to authorize access to the monitoring page + host string // Remote address of the monitoring service +} + +// New returns a monitoring service ready for stats reporting. +func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Service, error) { + // Parse the netstats connection url + re := regexp.MustCompile("([^:@]*)(:([^@]*))?@(.+)") + parts := re.FindStringSubmatch(url) + if len(parts) != 5 { + return nil, fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url) + } + // Assemble and return the stats service + return &Service{ + eth: ethServ, + les: lesServ, + node: parts[1], + pass: parts[3], + host: parts[4], + }, nil +} + +// Protocols implements node.Service, returning the P2P network protocols used +// by the stats service (nil as it doesn't use the devp2p overlay network). +func (s *Service) Protocols() []p2p.Protocol { return nil } + +// APIs implements node.Service, returning the RPC API endpoints provided by the +// stats service (nil as it doesn't provide any user callable APIs). +func (s *Service) APIs() []rpc.API { return nil } + +// Start implements node.Service, starting up the monitoring and reporting daemon. +func (s *Service) Start(server *p2p.Server) error { + s.server = server + go s.loop() + + glog.V(logger.Info).Infoln("Stats daemon started") + return nil +} + +// Stop implements node.Service, terminating the monitoring and reporting daemon. +func (s *Service) Stop() error { + glog.V(logger.Info).Infoln("Stats daemon stopped") + return nil +} + +// loop keeps trying to connect to the netstats server, reporting chain events +// until termination. +func (s *Service) loop() { + // Subscribe tso chain events to execute updates on + var emux *event.TypeMux + if s.eth != nil { + emux = s.eth.EventMux() + } else { + emux = s.les.EventMux() + } + headSub := emux.Subscribe(core.ChainHeadEvent{}) + defer headSub.Unsubscribe() + + txSub := emux.Subscribe(core.TxPreEvent{}) + defer txSub.Unsubscribe() + + // Loop reporting until termination + for { + // Establish a websocket connection to the server and authenticate the node + conn, err := websocket.Dial(fmt.Sprintf("wss://%s/api", s.host), "", "http://localhost/") + if err != nil { + glog.V(logger.Warn).Infof("Stats server unreachable: %v", err) + time.Sleep(10 * time.Second) + continue + } + in := json.NewDecoder(conn) + out := json.NewEncoder(conn) + + if err = s.login(in, out); err != nil { + glog.V(logger.Warn).Infof("Stats login failed: %v", err) + conn.Close() + time.Sleep(10 * time.Second) + continue + } + if err = s.report(in, out); err != nil { + glog.V(logger.Warn).Infof("Initial stats report failed: %v", err) + conn.Close() + continue + } + // Keep sending status updates until the connection breaks + fullReport := time.NewTicker(15 * time.Second) + + for err == nil { + select { + case <-fullReport.C: + if err = s.report(in, out); err != nil { + glog.V(logger.Warn).Infof("Full stats report failed: %v", err) + } + case head := <-headSub.Chan(): + if head == nil { // node stopped + conn.Close() + return + } + if err = s.reportBlock(out, head.Data.(core.ChainHeadEvent).Block); err != nil { + glog.V(logger.Warn).Infof("Block stats report failed: %v", err) + } + if err = s.reportPending(out); err != nil { + glog.V(logger.Warn).Infof("Post-block transaction stats report failed: %v", err) + } + case ev := <-txSub.Chan(): + if ev == nil { // node stopped + conn.Close() + return + } + // Exhaust events to avoid reporting too frequently + for exhausted := false; !exhausted; { + select { + case <-headSub.Chan(): + default: + exhausted = true + } + } + if err = s.reportPending(out); err != nil { + glog.V(logger.Warn).Infof("Transaction stats report failed: %v", err) + } + } + } + // Make sure the connection is closed + conn.Close() + } +} + +// nodeInfo is the collection of metainformation about a node that is displayed +// on the monitoring page. +type nodeInfo struct { + Name string `json:"name"` + Node string `json:"node"` + Port int `json:"port"` + Network string `json:"net"` + Protocol string `json:"protocol"` + API string `json:"api"` + Os string `json:"os"` + OsVer string `json:"os_v"` + Client string `json:"client"` +} + +// authMsg is the authentication infos needed to login to a monitoring server. +type authMsg struct { + Id string `json:"id"` + Info nodeInfo `json:"info"` + Secret string `json:"secret"` +} + +// login tries to authorize the client at the remote server. +func (s *Service) login(in *json.Decoder, out *json.Encoder) error { + // Construct and send the login authentication + infos := s.server.NodeInfo() + + var network, protocol string + if info := infos.Protocols["eth"]; info != nil { + network = strconv.Itoa(info.(*eth.EthNodeInfo).Network) + protocol = fmt.Sprintf("eth/%d", eth.ProtocolVersions[0]) + } else { + network = strconv.Itoa(infos.Protocols["les"].(*eth.EthNodeInfo).Network) + protocol = fmt.Sprintf("les/%d", les.ProtocolVersions[0]) + } + auth := &authMsg{ + Id: s.node, + Info: nodeInfo{ + Name: s.node, + Node: infos.Name, + Port: infos.Ports.Listener, + Network: network, + Protocol: protocol, + API: "No", + Os: runtime.GOOS, + OsVer: runtime.GOARCH, + Client: "0.1.1", + }, + Secret: s.pass, + } + login := map[string][]interface{}{ + "emit": []interface{}{"hello", auth}, + } + if err := out.Encode(login); err != nil { + return err + } + // Retrieve the remote ack or connection termination + var ack map[string][]string + if err := in.Decode(&ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" { + return errors.New("unauthorized") + } + return nil +} + +// report collects all possible data to report and send it to the stats server. +// This should only be used on reconnects or rarely to avoid overloading the +// server. Use the individual methods for reporting subscribed events. +func (s *Service) report(in *json.Decoder, out *json.Encoder) error { + if err := s.reportLatency(in, out); err != nil { + return err + } + if err := s.reportBlock(out, nil); err != nil { + return err + } + if err := s.reportPending(out); err != nil { + return err + } + if err := s.reportStats(out); err != nil { + return err + } + return nil +} + +// reportLatency sends a ping request to the server, measures the RTT time and +// finally sends a latency update. +func (s *Service) reportLatency(in *json.Decoder, out *json.Encoder) error { + // Send the current time to the ethstats server + start := time.Now() + + ping := map[string][]interface{}{ + "emit": []interface{}{"node-ping", map[string]string{ + "id": s.node, + "clientTime": start.String(), + }}, + } + if err := out.Encode(ping); err != nil { + return err + } + // Wait for the pong request to arrive back + var pong map[string][]interface{} + if err := in.Decode(&pong); err != nil || len(pong["emit"]) != 2 || pong["emit"][0].(string) != "node-pong" { + return errors.New("unexpected ping reply") + } + // Send back the measured latency + latency := map[string][]interface{}{ + "emit": []interface{}{"latency", map[string]string{ + "id": s.node, + "latency": strconv.Itoa(int((time.Since(start) / time.Duration(2)).Nanoseconds() / 1000000)), + }}, + } + if err := out.Encode(latency); err != nil { + return err + } + return nil +} + +// blockStats is the information to report about individual blocks. +type blockStats struct { + Number *big.Int `json:"number"` + Hash common.Hash `json:"hash"` + Miner common.Address `json:"miner"` + GasUsed *big.Int `json:"gasUsed"` + GasLimit *big.Int `json:"gasLimit"` + Diff string `json:"difficulty"` + TotalDiff string `json:"totalDifficulty"` + Txs txStats `json:"transactions"` + Uncles uncleStats `json:"uncles"` +} + +// txStats is a custom wrapper around a transaction array to force serializing +// empty arrays instead of returning null for them. +type txStats []*types.Transaction + +func (s txStats) MarshalJSON() ([]byte, error) { + if txs := ([]*types.Transaction)(s); len(txs) > 0 { + return json.Marshal(txs) + } + return []byte("[]"), nil +} + +// uncleStats is a custom wrapper around an uncle array to force serializing +// empty arrays instead of returning null for them. +type uncleStats []*types.Header + +func (s uncleStats) MarshalJSON() ([]byte, error) { + if uncles := ([]*types.Header)(s); len(uncles) > 0 { + return json.Marshal(uncles) + } + return []byte("[]"), nil +} + +// reportBlock retrieves the current chain head and repors it to the stats server. +func (s *Service) reportBlock(out *json.Encoder, block *types.Block) error { + // Gather the head block infos from the local blockchain + var ( + head *types.Header + td *big.Int + txs []*types.Transaction + uncles []*types.Header + ) + if s.eth != nil { + // Full nodes have all needed information available + if block == nil { + block = s.eth.BlockChain().CurrentBlock() + } + head = block.Header() + td = s.eth.BlockChain().GetTd(head.Hash(), head.Number.Uint64()) + + txs = block.Transactions() + uncles = block.Uncles() + } else { + // Light nodes would need on-demand lookups for transactions/uncles, skip + if block != nil { + head = block.Header() + } else { + head = s.les.BlockChain().CurrentHeader() + } + td = s.les.BlockChain().GetTd(head.Hash(), head.Number.Uint64()) + } + // Assemble the block stats report and send it to the server + stats := map[string]interface{}{ + "id": s.node, + "block": &blockStats{ + Number: head.Number, + Hash: head.Hash(), + Miner: head.Coinbase, + GasUsed: new(big.Int).Set(head.GasUsed), + GasLimit: new(big.Int).Set(head.GasLimit), + Diff: head.Difficulty.String(), + TotalDiff: td.String(), + Txs: txs, + Uncles: uncles, + }, + } + report := map[string][]interface{}{ + "emit": []interface{}{"block", stats}, + } + if err := out.Encode(report); err != nil { + return err + } + return nil +} + +// pendStats is the information to report about pending transactions. +type pendStats struct { + Pending int `json:"pending"` +} + +// reportPending retrieves the current number of pending transactions and reports +// it to the stats server. +func (s *Service) reportPending(out *json.Encoder) error { + // Retrieve the pending count from the local blockchain + var pending int + if s.eth != nil { + pending, _ = s.eth.TxPool().Stats() + } else { + pending = s.les.TxPool().Stats() + } + // Assemble the transaction stats and send it to the server + stats := map[string]interface{}{ + "id": s.node, + "stats": &pendStats{ + Pending: pending, + }, + } + report := map[string][]interface{}{ + "emit": []interface{}{"pending", stats}, + } + if err := out.Encode(report); err != nil { + return err + } + return nil +} + +// blockStats is the information to report about the local node. +type nodeStats struct { + Active bool `json:"active"` + Syncing bool `json:"syncing"` + Mining bool `json:"mining"` + Hashrate int `json:"hashrate"` + Peers int `json:"peers"` + GasPrice int `json:"gasPrice"` + Uptime int `json:"uptime"` +} + +// reportPending retrieves various stats about the node at the networking and +// mining layer and reports it to the stats server. +func (s *Service) reportStats(out *json.Encoder) error { + // Gather the syncing and mining infos from the local miner instance + var ( + mining bool + hashrate int + syncing bool + gasprice int + ) + if s.eth != nil { + mining = s.eth.Miner().Mining() + hashrate = int(s.eth.Miner().HashRate()) + + sync := s.eth.Downloader().Progress() + syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock + + gasprice = int(s.eth.Miner().GasPrice().Uint64()) + } else { + sync := s.les.Downloader().Progress() + syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock + } + stats := map[string]interface{}{ + "id": s.node, + "stats": &nodeStats{ + Active: true, + Mining: mining, + Hashrate: hashrate, + Peers: s.server.PeerCount(), + GasPrice: gasprice, + Syncing: syncing, + Uptime: 100, + }, + } + report := map[string][]interface{}{ + "emit": []interface{}{"stats", stats}, + } + if err := out.Encode(report); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/interfaces.go b/vendor/github.com/ethereum/go-ethereum/interfaces.go index aab0e2029..bbb204ff2 100644 --- a/vendor/github.com/ethereum/go-ethereum/interfaces.go +++ b/vendor/github.com/ethereum/go-ethereum/interfaces.go @@ -18,6 +18,7 @@ package ethereum import ( + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -26,6 +27,9 @@ import ( "golang.org/x/net/context" ) +// NotFound is returned by API methods if the requested item does not exist. +var NotFound = errors.New("not found") + // TODO: move subscription to package event // Subscription represents an event subscription where events are @@ -46,6 +50,8 @@ type Subscription interface { // blockchain fork that was previously downloaded and processed by the node. The block // number argument can be nil to select the latest canonical block. Reading block headers // should be preferred over full blocks whenever possible. +// +// The returned error is NotFound if the requested item does not exist. type ChainReader interface { BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) @@ -53,7 +59,30 @@ type ChainReader interface { HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) - TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) + + // This method subscribes to notifications about changes of the head block of + // the canonical chain. + SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (Subscription, error) +} + +// TransactionReader provides access to past transactions and their receipts. +// Implementations may impose arbitrary restrictions on the transactions and receipts that +// can be retrieved. Historic transactions may not be available. +// +// Avoid relying on this interface if possible. Contract logs (through the LogFilterer +// interface) are more reliable and usually safer in the presence of chain +// reorganisations. +// +// The returned error is NotFound if the requested item does not exist. +type TransactionReader interface { + // TransactionByHash checks the pool of pending transactions in addition to the + // blockchain. The isPending return value indicates whether the transaction has been + // mined yet. Note that the transaction may not be part of the canonical chain even if + // it's not pending. + TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) + // TransactionReceipt returns the receipt of a mined transaction. Note that the + // transaction may not be included in the current canonical chain even if a receipt + // exists. TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) } @@ -83,11 +112,6 @@ type ChainSyncReader interface { SyncProgress(ctx context.Context) (*SyncProgress, error) } -// A ChainHeadEventer returns notifications whenever the canonical head block is updated. -type ChainHeadEventer interface { - SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (Subscription, error) -} - // CallMsg contains parameters for contract calls. type CallMsg struct { From common.Address // the sender of the 'transaction' @@ -128,6 +152,9 @@ type FilterQuery struct { // LogFilterer provides access to contract log events using a one-off query or continuous // event subscription. +// +// Logs received through a streaming query subscription may have Removed set to true, +// indicating that the log was reverted due to a chain reorganisation. type LogFilterer interface { FilterLogs(ctx context.Context, q FilterQuery) ([]vm.Log, error) SubscribeFilterLogs(ctx context.Context, q FilterQuery, ch chan<- vm.Log) (Subscription, error) diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go index 89a17255c..a48a28c9a 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go @@ -1126,13 +1126,11 @@ func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(ctx context.Context return common.Hash{}, err } - if args.Nonce == nil { - nonce, err := s.b.GetPoolNonce(ctx, args.From) - if err != nil { - return common.Hash{}, err - } - args.Nonce = rpc.NewHexNumber(nonce) + nonce, err := s.b.GetPoolNonce(ctx, args.From) + if err != nil { + return common.Hash{}, err } + args.Nonce = rpc.NewHexNumber(nonce) var tx *types.Transaction if args.To == nil { diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go index fdc4a39dc..77df7eb8d 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go @@ -51,7 +51,7 @@ type Backend interface { 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 - GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (vm.Environment, func() error, error) + GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.Environment, func() error, error) // TxPool API SendTx(ctx context.Context, signedTx *types.Transaction) error RemoveTx(txHash common.Hash) diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go index 5f69826a3..6d632376b 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go @@ -124,7 +124,7 @@ func (sw *stackWrapper) toValue(vm *otto.Otto) otto.Value { // dbWrapper provides a JS wrapper around vm.Database type dbWrapper struct { - db vm.Database + db vm.StateDB } // getBalance retrieves an account's balance @@ -278,11 +278,11 @@ func wrapError(context string, err error) error { } // CaptureState implements the Tracer interface to trace a single step of VM execution -func (jst *JavascriptTracer) CaptureState(env vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { +func (jst *JavascriptTracer) CaptureState(env *vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { if jst.err == nil { jst.memory.memory = memory jst.stack.stack = stack - jst.db.db = env.Db() + jst.db.db = env.StateDB ocw := &opCodeWrapper{op} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go index c8a0cac8c..ce50d3634 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go @@ -37,12 +37,6 @@ web3._extend({ property: 'bzz', methods: [ - new web3._extend.Method({ - name: 'blockNetworkRead', - call: 'bzz_blockNetworkRead', - params: 1, - inputFormatter: [null] - }), new web3._extend.Method({ name: 'syncEnabled', call: 'bzz_syncEnabled', @@ -68,17 +62,11 @@ web3._extend({ inputFormatter: [null, null] }), new web3._extend.Method({ - name: 'retrieve', - call: 'bzz_retrieve', + name: 'resolve', + call: 'bzz_resolve', params: 1, inputFormatter: [null] }), - new web3._extend.Method({ - name: 'store', - call: 'bzz_store', - params: 2, - inputFormatter: [null] - }), new web3._extend.Method({ name: 'get', call: 'bzz_get', @@ -226,41 +214,6 @@ web3._extend({ 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: diff --git a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go index b77767ed7..8df963f6e 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go @@ -88,7 +88,7 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(blockHash) } -func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { +func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) { stateDb := state.(*light.LightState).Copy() addr := msg.From() from, err := stateDb.GetOrNewStateObject(ctx, addr) @@ -96,8 +96,10 @@ func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et return nil, nil, err } from.SetBalance(common.MaxBig) - env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}) - return env, env.Error, nil + + vmstate := light.NewVMState(ctx, stateDb) + context := core.NewEVMContext(msg, header, b.eth.blockchain) + return vm.NewEnvironment(context, vmstate, b.eth.chainConfig, vm.Config{}), vmstate.Error, nil } func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/vendor/github.com/ethereum/go-ethereum/les/backend.go b/vendor/github.com/ethereum/go-ethereum/les/backend.go index d7b9c7e28..44d14aadd 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/backend.go @@ -26,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/common/httpclient" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" @@ -62,7 +61,6 @@ type LightEthereum struct { eventMux *event.TypeMux pow *ethash.Ethash - httpclient *httpclient.HTTPClient accountManager *accounts.Manager solcPath string solc *compiler.Solidity @@ -98,7 +96,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { accountManager: ctx.AccountManager, pow: pow, shutdownChan: make(chan bool), - httpclient: httpclient.New(config.DocRoot), netVersionId: config.NetworkId, NatSpec: config.NatSpec, PowTest: config.PowTest, @@ -189,6 +186,7 @@ func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchai 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 } +func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } // Protocols implements node.Service, returning all the currently configured // network protocols to start. diff --git a/vendor/github.com/ethereum/go-ethereum/light/state.go b/vendor/github.com/ethereum/go-ethereum/light/state.go index 9f2376809..f8b75c588 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/state.go +++ b/vendor/github.com/ethereum/go-ethereum/light/state.go @@ -141,6 +141,15 @@ func (self *LightState) AddBalance(ctx context.Context, addr common.Address, amo return err } +// SubBalance adds the given amount to the balance of the specified account +func (self *LightState) SubBalance(ctx context.Context, addr common.Address, amount *big.Int) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SubBalance(amount) + } + return err +} + // SetNonce sets the nonce of the specified account func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce uint64) error { stateObject, err := self.GetOrNewStateObject(ctx, addr) diff --git a/vendor/github.com/ethereum/go-ethereum/light/state_object.go b/vendor/github.com/ethereum/go-ethereum/light/state_object.go index 61c3888fe..56f607bff 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/state_object.go +++ b/vendor/github.com/ethereum/go-ethereum/light/state_object.go @@ -179,7 +179,7 @@ func (c *StateObject) SetBalance(amount *big.Int) { } // ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(gas, price *big.Int) {} +func (c *StateObject) ReturnGas(gas *big.Int) {} // Copy creates a copy of the state object func (self *StateObject) Copy() *StateObject { @@ -201,14 +201,19 @@ func (self *StateObject) Copy() *StateObject { // Attribute accessors // +// empty returns whether the account is considered empty. +func (self *StateObject) empty() bool { + return self.nonce == 0 && self.balance.BitLen() == 0 && bytes.Equal(self.codeHash, emptyCodeHash) +} + // Balance returns the account balance func (self *StateObject) Balance() *big.Int { return self.balance } // Address returns the address of the contract/account -func (c *StateObject) Address() common.Address { - return c.address +func (self *StateObject) Address() common.Address { + return self.address } // Code returns the contract code diff --git a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go index 5d330b072..cc0c568c9 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go @@ -20,123 +20,38 @@ import ( "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/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" "golang.org/x/net/context" ) -// VMEnv is the light client version of the vm execution environment. -// Unlike other structures, VMEnv holds a context that is applied by state -// retrieval requests through the entire execution. If any state operation -// returns an error, the execution fails. -type VMEnv struct { - vm.Environment - ctx context.Context - chainConfig *params.ChainConfig - evm *vm.EVM - state *VMState - header *types.Header - msg core.Message - depth int - chain *LightChain - err error -} - -// NewEnv creates a new execution environment based on an ODR capable light state -func NewEnv(ctx context.Context, state *LightState, chainConfig *params.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv { - env := &VMEnv{ - chainConfig: chainConfig, - chain: chain, - header: header, - msg: msg, - } - env.state = &VMState{ctx: ctx, state: state, env: env} - - env.evm = vm.New(env, cfg) - return env -} - -func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Origin() common.Address { return self.msg.From() } -func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } -func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } -func (self *VMEnv) Time() *big.Int { return self.header.Time } -func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } -func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) Depth() int { return self.depth } -func (self *VMEnv) SetDepth(i int) { self.depth = i } -func (self *VMEnv) GetHash(n uint64) common.Hash { - for header := self.chain.GetHeader(self.header.ParentHash, self.header.Number.Uint64()-1); header != nil; header = self.chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { - if header.Number.Uint64() == n { - return header.Hash() - } - } - - return common.Hash{} -} - -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) SnapshotDatabase() int { - return self.state.SnapshotDatabase() -} - -func (self *VMEnv) RevertToSnapshot(idx int) { - self.state.RevertToSnapshot(idx) -} - -func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { - core.Transfer(from, to, amount) -} - -func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return core.Call(self, me, addr, data, gas, price, value) -} -func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return core.CallCode(self, me, addr, data, gas, price, value) -} - -func (self *VMEnv) 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 *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { - return core.Create(self, me, data, gas, price, value) -} - -// Error returns the error (if any) that happened during execution. -func (self *VMEnv) Error() error { - return self.err -} - // VMState is a wrapper for the light state that holds the actual context and // passes it to any state operation that requires it. type VMState struct { - vm.Database ctx context.Context state *LightState snapshots []*LightState - env *VMEnv + err error } +func NewVMState(ctx context.Context, state *LightState) *VMState { + return &VMState{ctx: ctx, state: state} +} + +func (s *VMState) Error() error { + return s.err +} + +func (s *VMState) AddLog(log *vm.Log) {} + // errHandler handles and stores any state error that happens during execution. func (s *VMState) errHandler(err error) { - if err != nil && s.env.err == nil { - s.env.err = err + if err != nil && s.err == nil { + s.err = err } } -func (self *VMState) SnapshotDatabase() int { +func (self *VMState) Snapshot() int { self.snapshots = append(self.snapshots, self.state.Copy()) return len(self.snapshots) - 1 } @@ -175,6 +90,12 @@ func (s *VMState) AddBalance(addr common.Address, amount *big.Int) { s.errHandler(err) } +// SubBalance adds the given amount to the balance of the specified account +func (s *VMState) SubBalance(addr common.Address, amount *big.Int) { + err := s.state.SubBalance(s.ctx, addr, amount) + s.errHandler(err) +} + // GetBalance retrieves the balance from the given address or 0 if the account does // not exist func (s *VMState) GetBalance(addr common.Address) *big.Int { @@ -263,6 +184,13 @@ func (s *VMState) Exist(addr common.Address) bool { return res } +// Empty returns true if the account at the given address is considered empty +func (s *VMState) Empty(addr common.Address) bool { + so, err := s.state.GetStateObject(s.ctx, addr) + s.errHandler(err) + return so == nil || so.empty() +} + // HasSuicided returns true if the given account has been marked for deletion // or false if the account does not exist func (s *VMState) HasSuicided(addr common.Address) bool { diff --git a/vendor/github.com/ethereum/go-ethereum/miner/miner.go b/vendor/github.com/ethereum/go-ethereum/miner/miner.go index c85a1cd8e..87568ac18 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/miner.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/miner.go @@ -187,6 +187,15 @@ func (self *Miner) Pending() (*types.Block, *state.StateDB) { return self.worker.pending() } +// PendingBlock returns the currently pending block. +// +// Note, to access both the pending block and the pending state +// simultaneously, please use Pending(), as the pending state can +// change between multiple method calls +func (self *Miner) PendingBlock() *types.Block { + return self.worker.pendingBlock() +} + func (self *Miner) SetEtherbase(addr common.Address) { self.coinbase = addr self.worker.setEtherbase(addr) diff --git a/vendor/github.com/ethereum/go-ethereum/miner/worker.go b/vendor/github.com/ethereum/go-ethereum/miner/worker.go index 2933b6bd3..edbd502c1 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/worker.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/worker.go @@ -176,6 +176,21 @@ func (self *worker) pending() (*types.Block, *state.StateDB) { return self.current.Block, self.current.state.Copy() } +func (self *worker) pendingBlock() *types.Block { + self.currentMu.Lock() + defer self.currentMu.Unlock() + + if atomic.LoadInt32(&self.mining) == 0 { + return types.NewBlock( + self.current.header, + self.current.txs, + nil, + self.current.receipts, + ) + } + return self.current.Block +} + func (self *worker) start() { self.mu.Lock() defer self.mu.Unlock() @@ -262,6 +277,7 @@ func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) ( func (self *worker) wait() { for { + mustCommitNewWork := true for result := range self.recv { atomic.AddInt32(&self.atWork, -1) @@ -315,6 +331,8 @@ func (self *worker) wait() { core.WriteReceipts(self.chainDb, work.receipts) // Write map map bloom filters core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts) + // implicit by posting ChainHeadEvent + mustCommitNewWork = false } // broadcast before waiting for validation @@ -343,7 +361,9 @@ func (self *worker) wait() { } glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm) - self.commitNewWork() + if mustCommitNewWork { + self.commitNewWork() + } } } } @@ -451,6 +471,7 @@ func (self *worker) commitNewWork() { tstart := time.Now() parent := self.chain.CurrentBlock() + tstamp := tstart.Unix() if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { tstamp = parent.Time().Int64() + 1 @@ -618,7 +639,16 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB txs.Shift() } } + if len(coalescedLogs) > 0 || env.tcount > 0 { + // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined + // logs by filling in the block hash when the block was mined by the local miner. This can + // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. + cpy := make(vm.Logs, len(coalescedLogs)) + for i, l := range coalescedLogs { + cpy[i] = new(vm.Log) + *cpy[i] = *l + } go func(logs vm.Logs, tcount int) { if len(logs) > 0 { mux.Post(core.PendingLogsEvent{Logs: logs}) @@ -626,7 +656,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB if tcount > 0 { mux.Post(core.PendingStateEvent{}) } - }(coalescedLogs, env.tcount) + }(cpy, env.tcount) } } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go index 668d65e32..a60fc2fa5 100644 --- a/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go +++ b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go @@ -73,7 +73,8 @@ func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (*Header // GetTransactionByHash returns the transaction with the given hash. func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (*Transaction, error) { - tx, err := ec.client.TransactionByHash(ctx.context, hash.hash) + // TODO(karalabe): handle isPending + tx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash) return &Transaction{tx}, err } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go index 738c0c548..7ea4b2f65 100644 --- a/vendor/github.com/ethereum/go-ethereum/mobile/geth.go +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/nat" @@ -65,6 +66,12 @@ type NodeConfig struct { // A minimum of 16MB is always reserved. EthereumDatabaseCache int + // EthereumNetStats is a netstats connection string to use to report various + // chain, transaction and node stats to a monitoring server. + // + // It has the form "nodename:secret@host:port" + EthereumNetStats string + // WhisperEnabled specifies whether the node should run the Whisper protocol. WhisperEnabled bool } @@ -106,6 +113,7 @@ func NewNode(datadir string, config *NodeConfig) (*Node, error) { // Create the empty networking stack nodeConf := &node.Config{ Name: clientIdentifier, + Version: params.Version, DataDir: datadir, KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! NoDiscovery: true, @@ -150,6 +158,17 @@ func NewNode(datadir string, config *NodeConfig) (*Node, error) { }); err != nil { return nil, fmt.Errorf("ethereum init: %v", err) } + // If netstats reporting is requested, do it + if config.EthereumNetStats != "" { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + var lesServ *les.LightEthereum + ctx.Service(&lesServ) + + return ethstats.New(config.EthereumNetStats, nil, lesServ) + }); err != nil { + return nil, fmt.Errorf("netstats init: %v", err) + } + } } // Register the Whisper protocol if requested if config.WhisperEnabled { diff --git a/vendor/github.com/ethereum/go-ethereum/node/api.go b/vendor/github.com/ethereum/go-ethereum/node/api.go index 631e92c8e..7c9ad601a 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/api.go +++ b/vendor/github.com/ethereum/go-ethereum/node/api.go @@ -21,7 +21,7 @@ import ( "strings" "time" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" @@ -331,6 +331,6 @@ func (s *PublicWeb3API) ClientVersion() string { // Sha3 applies the ethereum sha3 implementation on the input. // It assumes the input is hex encoded. -func (s *PublicWeb3API) Sha3(input string) string { - return common.ToHex(crypto.Keccak256(common.FromHex(input))) +func (s *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes { + return crypto.Keccak256(input) } diff --git a/vendor/github.com/ethereum/go-ethereum/node/config.go b/vendor/github.com/ethereum/go-ethereum/node/config.go index 8d85b7ff8..8d75e441b 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/config.go +++ b/vendor/github.com/ethereum/go-ethereum/node/config.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" ) var ( @@ -103,6 +104,10 @@ type Config struct { // Listener address for the V5 discovery protocol UDP traffic. DiscoveryV5Addr string + // Restrict communication to white listed IP networks. + // The whitelist only applies when non-nil. + NetRestrict *netutil.Netlist + // BootstrapNodes used to establish connectivity with the rest of the network. BootstrapNodes []*discover.Node diff --git a/vendor/github.com/ethereum/go-ethereum/node/node.go b/vendor/github.com/ethereum/go-ethereum/node/node.go index d49ae3a45..4b56fba4c 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/node.go +++ b/vendor/github.com/ethereum/go-ethereum/node/node.go @@ -165,6 +165,7 @@ func (n *Node) Start() error { TrustedNodes: n.config.TrusterNodes(), NodeDatabase: n.config.NodeDB(), ListenAddr: n.config.ListenAddr, + NetRestrict: n.config.NetRestrict, NAT: n.config.NAT, Dialer: n.config.Dialer, NoDial: n.config.NoDial, diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/dial.go b/vendor/github.com/ethereum/go-ethereum/p2p/dial.go index 691b8539e..57fba136a 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/dial.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/dial.go @@ -19,6 +19,7 @@ package p2p import ( "container/heap" "crypto/rand" + "errors" "fmt" "net" "time" @@ -26,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/netutil" ) const ( @@ -48,6 +50,7 @@ const ( type dialstate struct { maxDynDials int ntab discoverTable + netrestrict *netutil.Netlist lookupRunning bool dialing map[discover.NodeID]connFlag @@ -100,10 +103,11 @@ type waitExpireTask struct { time.Duration } -func newDialState(static []*discover.Node, ntab discoverTable, maxdyn int) *dialstate { +func newDialState(static []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate { s := &dialstate{ maxDynDials: maxdyn, ntab: ntab, + netrestrict: netrestrict, static: make(map[discover.NodeID]*dialTask), dialing: make(map[discover.NodeID]connFlag), randomNodes: make([]*discover.Node, maxdyn/2), @@ -128,12 +132,9 @@ func (s *dialstate) removeStatic(n *discover.Node) { func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { var newtasks []task - isDialing := func(id discover.NodeID) bool { - _, found := s.dialing[id] - return found || peers[id] != nil || s.hist.contains(id) - } addDial := func(flag connFlag, n *discover.Node) bool { - if isDialing(n.ID) { + if err := s.checkDial(n, peers); err != nil { + glog.V(logger.Debug).Infof("skipping dial candidate %x@%v:%d: %v", n.ID[:8], n.IP, n.TCP, err) return false } s.dialing[n.ID] = flag @@ -159,7 +160,12 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now // Create dials for static nodes if they are not connected. for id, t := range s.static { - if !isDialing(id) { + err := s.checkDial(t.dest, peers) + switch err { + case errNotWhitelisted, errSelf: + glog.V(logger.Debug).Infof("removing static dial candidate %x@%v:%d: %v", t.dest.ID[:8], t.dest.IP, t.dest.TCP, err) + delete(s.static, t.dest.ID) + case nil: s.dialing[id] = t.flags newtasks = append(newtasks, t) } @@ -202,6 +208,31 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now return newtasks } +var ( + errSelf = errors.New("is self") + errAlreadyDialing = errors.New("already dialing") + errAlreadyConnected = errors.New("already connected") + errRecentlyDialed = errors.New("recently dialed") + errNotWhitelisted = errors.New("not contained in netrestrict whitelist") +) + +func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer) error { + _, dialing := s.dialing[n.ID] + switch { + case dialing: + return errAlreadyDialing + case peers[n.ID] != nil: + return errAlreadyConnected + case s.ntab != nil && n.ID == s.ntab.Self().ID: + return errSelf + case s.netrestrict != nil && !s.netrestrict.Contains(n.IP): + return errNotWhitelisted + case s.hist.contains(n.ID): + return errRecentlyDialed + } + return nil +} + func (s *dialstate) taskDone(t task, now time.Time) { switch t := t.(type) { case *dialTask: diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go index 74758b6fd..e09c63ffb 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/rlp" ) @@ -126,8 +127,16 @@ func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint { return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} } -func nodeFromRPC(rn rpcNode) (*Node, error) { - // TODO: don't accept localhost, LAN addresses from internet hosts +func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) { + if rn.UDP <= 1024 { + return nil, errors.New("low port") + } + if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { + return nil, err + } + if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) { + return nil, errors.New("not contained in netrestrict whitelist") + } n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP) err := n.validateComplete() return n, err @@ -151,6 +160,7 @@ type conn interface { // udp implements the RPC protocol. type udp struct { conn conn + netrestrict *netutil.Netlist priv *ecdsa.PrivateKey ourEndpoint rpcEndpoint @@ -201,7 +211,7 @@ type reply struct { } // ListenUDP returns a new table that listens for UDP packets on laddr. -func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string) (*Table, error) { +func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, error) { addr, err := net.ResolveUDPAddr("udp", laddr) if err != nil { return nil, err @@ -210,7 +220,7 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP if err != nil { return nil, err } - tab, _, err := newUDP(priv, conn, natm, nodeDBPath) + tab, _, err := newUDP(priv, conn, natm, nodeDBPath, netrestrict) if err != nil { return nil, err } @@ -218,13 +228,14 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP return tab, nil } -func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string) (*Table, *udp, error) { +func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, *udp, error) { udp := &udp{ - conn: c, - priv: priv, - closing: make(chan struct{}), - gotreply: make(chan reply), - addpending: make(chan *pending), + conn: c, + priv: priv, + netrestrict: netrestrict, + closing: make(chan struct{}), + gotreply: make(chan reply), + addpending: make(chan *pending), } realaddr := c.LocalAddr().(*net.UDPAddr) if natm != nil { @@ -281,9 +292,12 @@ func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node reply := r.(*neighbors) for _, rn := range reply.Nodes { nreceived++ - if n, err := nodeFromRPC(rn); err == nil { - nodes = append(nodes, n) + n, err := t.nodeFromRPC(toaddr, rn) + if err != nil { + glog.V(logger.Detail).Infof("invalid neighbor node (%v) from %v: %v", rn.IP, toaddr, err) + continue } + nodes = append(nodes, n) } return nreceived >= bucketSize }) @@ -479,13 +493,6 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte, return packet, nil } -func isTemporaryError(err error) bool { - tempErr, ok := err.(interface { - Temporary() bool - }) - return ok && tempErr.Temporary() || isPacketTooBig(err) -} - // readLoop runs in its own goroutine. it handles incoming UDP packets. func (t *udp) readLoop() { defer t.conn.Close() @@ -495,7 +502,7 @@ func (t *udp) readLoop() { buf := make([]byte, 1280) for { nbytes, from, err := t.conn.ReadFromUDP(buf) - if isTemporaryError(err) { + if netutil.IsTemporaryError(err) { // Ignore temporary read errors. glog.V(logger.Debug).Infof("Temporary read error: %v", err) continue @@ -602,6 +609,9 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte // Send neighbors in chunks with at most maxNeighbors per packet // to stay below the 1280 byte limit. for i, n := range closest { + if netutil.CheckRelayIP(from.IP, n.IP) != nil { + continue + } p.Nodes = append(p.Nodes, nodeToRPC(n)) if len(p.Nodes) == maxNeighbors || i == len(closest)-1 { t.send(from, neighborsPacket, p) diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_windows.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_windows.go deleted file mode 100644 index 66bbf9597..000000000 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_windows.go +++ /dev/null @@ -1,40 +0,0 @@ -// 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 . - -//+build windows - -package discover - -import ( - "net" - "os" - "syscall" -) - -const _WSAEMSGSIZE = syscall.Errno(10040) - -// reports whether err indicates that a UDP packet didn't -// fit the receive buffer. On Windows, WSARecvFrom returns -// code WSAEMSGSIZE and no data if this happens. -func isPacketTooBig(err error) bool { - if opErr, ok := err.(*net.OpError); ok { - if scErr, ok := opErr.Err.(*os.SyscallError); ok { - return scErr.Err == _WSAEMSGSIZE - } - return opErr.Err == _WSAEMSGSIZE - } - return false -} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go index 7ad6f1e5b..d1c48904e 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/rlp" ) @@ -45,6 +46,7 @@ const ( bucketRefreshInterval = 1 * time.Minute seedCount = 30 seedMaxAge = 5 * 24 * time.Hour + lowPort = 1024 ) const testTopic = "foo" @@ -62,8 +64,9 @@ func debugLog(s string) { // Network manages the table and all protocol interaction. type Network struct { - db *nodeDB // database of known nodes - conn transport + db *nodeDB // database of known nodes + conn transport + netrestrict *netutil.Netlist closed chan struct{} // closed when loop is done closeReq chan struct{} // 'request to close' @@ -132,7 +135,7 @@ type timeoutEvent struct { node *Node } -func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, dbPath string) (*Network, error) { +func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, dbPath string, netrestrict *netutil.Netlist) (*Network, error) { ourID := PubkeyID(&ourPubkey) var db *nodeDB @@ -147,6 +150,7 @@ func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, d net := &Network{ db: db, conn: conn, + netrestrict: netrestrict, tab: tab, topictab: newTopicTable(db, tab.self), ticketStore: newTicketStore(), @@ -684,16 +688,22 @@ func (net *Network) internNodeFromDB(dbn *Node) *Node { return n } -func (net *Network) internNodeFromNeighbours(rn rpcNode) (n *Node, err error) { +func (net *Network) internNodeFromNeighbours(sender *net.UDPAddr, rn rpcNode) (n *Node, err error) { if rn.ID == net.tab.self.ID { return nil, errors.New("is self") } + if rn.UDP <= lowPort { + return nil, errors.New("low port") + } n = net.nodes[rn.ID] if n == nil { // We haven't seen this node before. - n, err = nodeFromRPC(rn) - n.state = unknown + n, err = nodeFromRPC(sender, rn) + if net.netrestrict != nil && !net.netrestrict.Contains(n.IP) { + return n, errors.New("not contained in netrestrict whitelist") + } if err == nil { + n.state = unknown net.nodes[n.ID] = n } return n, err @@ -1095,7 +1105,7 @@ func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) net.conn.sendNeighbours(n, results) return n.state, nil case neighborsPacket: - err := net.handleNeighboursPacket(n, pkt.data.(*neighbors)) + err := net.handleNeighboursPacket(n, pkt) return n.state, err case neighboursTimeout: if n.pendingNeighbours != nil { @@ -1182,17 +1192,18 @@ func rlpHash(x interface{}) (h common.Hash) { return h } -func (net *Network) handleNeighboursPacket(n *Node, req *neighbors) error { +func (net *Network) handleNeighboursPacket(n *Node, pkt *ingressPacket) error { if n.pendingNeighbours == nil { return errNoQuery } net.abortTimedEvent(n, neighboursTimeout) + req := pkt.data.(*neighbors) nodes := make([]*Node, len(req.Nodes)) for i, rn := range req.Nodes { - nn, err := net.internNodeFromNeighbours(rn) + nn, err := net.internNodeFromNeighbours(pkt.remoteAddr, rn) if err != nil { - glog.V(logger.Debug).Infof("invalid neighbour from %x: %v", n.ID[:8], err) + glog.V(logger.Debug).Infof("invalid neighbour (%v) from %x@%v: %v", rn.IP, n.ID[:8], pkt.remoteAddr, err) continue } nodes[i] = nn diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go index 46d3200bf..a6114e032 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/rlp" ) @@ -198,8 +199,10 @@ func (e1 rpcEndpoint) equal(e2 rpcEndpoint) bool { return e1.UDP == e2.UDP && e1.TCP == e2.TCP && bytes.Equal(e1.IP, e2.IP) } -func nodeFromRPC(rn rpcNode) (*Node, error) { - // TODO: don't accept localhost, LAN addresses from internet hosts +func nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) { + if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { + return nil, err + } n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP) err := n.validateComplete() return n, err @@ -235,12 +238,12 @@ type udp struct { } // ListenUDP returns a new table that listens for UDP packets on laddr. -func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string) (*Network, error) { +func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Network, error) { transport, err := listenUDP(priv, laddr) if err != nil { return nil, err } - net, err := newNetwork(transport, priv.PublicKey, natm, nodeDBPath) + net, err := newNetwork(transport, priv.PublicKey, natm, nodeDBPath, netrestrict) if err != nil { return nil, err } @@ -327,6 +330,9 @@ func (t *udp) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) return } for i, result := range nodes { + if netutil.CheckRelayIP(remote.IP, result.IP) != nil { + continue + } p.Nodes = append(p.Nodes, nodeToRPC(result)) if len(p.Nodes) == maxTopicNodes || i == len(nodes)-1 { t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) @@ -385,7 +391,7 @@ func (t *udp) readLoop() { buf := make([]byte, 1280) for { nbytes, from, err := t.conn.ReadFromUDP(buf) - if isTemporaryError(err) { + if netutil.IsTemporaryError(err) { // Ignore temporary read errors. glog.V(logger.Debug).Infof("Temporary read error: %v", err) continue @@ -398,13 +404,6 @@ func (t *udp) readLoop() { } } -func isTemporaryError(err error) bool { - tempErr, ok := err.(interface { - Temporary() bool - }) - return ok && tempErr.Temporary() || isPacketTooBig(err) -} - func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error { pkt := ingressPacket{remoteAddr: from} if err := decodePacket(buf, &pkt); err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_notwindows.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error.go similarity index 75% rename from vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_notwindows.go rename to vendor/github.com/ethereum/go-ethereum/p2p/netutil/error.go index 4da18d0f6..cb21b9cd4 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_notwindows.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error.go @@ -14,13 +14,12 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//+build !windows +package netutil -package discv5 - -// reports whether err indicates that a UDP packet didn't -// fit the receive buffer. There is no such error on -// non-Windows platforms. -func isPacketTooBig(err error) bool { - return false +// IsTemporaryError checks whether the given error should be considered temporary. +func IsTemporaryError(err error) bool { + tempErr, ok := err.(interface { + Temporary() bool + }) + return ok && tempErr.Temporary() || isPacketTooBig(err) } diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net.go new file mode 100644 index 000000000..3c3715788 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net.go @@ -0,0 +1,166 @@ +// 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 . + +// Package netutil contains extensions to the net package. +package netutil + +import ( + "errors" + "net" + "strings" +) + +var lan4, lan6, special4, special6 Netlist + +func init() { + // Lists from RFC 5735, RFC 5156, + // https://www.iana.org/assignments/iana-ipv4-special-registry/ + lan4.Add("0.0.0.0/8") // "This" network + lan4.Add("10.0.0.0/8") // Private Use + lan4.Add("172.16.0.0/12") // Private Use + lan4.Add("192.168.0.0/16") // Private Use + lan6.Add("fe80::/10") // Link-Local + lan6.Add("fc00::/7") // Unique-Local + special4.Add("192.0.0.0/29") // IPv4 Service Continuity + special4.Add("192.0.0.9/32") // PCP Anycast + special4.Add("192.0.0.170/32") // NAT64/DNS64 Discovery + special4.Add("192.0.0.171/32") // NAT64/DNS64 Discovery + special4.Add("192.0.2.0/24") // TEST-NET-1 + special4.Add("192.31.196.0/24") // AS112 + special4.Add("192.52.193.0/24") // AMT + special4.Add("192.88.99.0/24") // 6to4 Relay Anycast + special4.Add("192.175.48.0/24") // AS112 + special4.Add("198.18.0.0/15") // Device Benchmark Testing + special4.Add("198.51.100.0/24") // TEST-NET-2 + special4.Add("203.0.113.0/24") // TEST-NET-3 + special4.Add("255.255.255.255/32") // Limited Broadcast + + // http://www.iana.org/assignments/iana-ipv6-special-registry/ + special6.Add("100::/64") + special6.Add("2001::/32") + special6.Add("2001:1::1/128") + special6.Add("2001:2::/48") + special6.Add("2001:3::/32") + special6.Add("2001:4:112::/48") + special6.Add("2001:5::/32") + special6.Add("2001:10::/28") + special6.Add("2001:20::/28") + special6.Add("2001:db8::/32") + special6.Add("2002::/16") +} + +// Netlist is a list of IP networks. +type Netlist []net.IPNet + +// ParseNetlist parses a comma-separated list of CIDR masks. +// Whitespace and extra commas are ignored. +func ParseNetlist(s string) (*Netlist, error) { + ws := strings.NewReplacer(" ", "", "\n", "", "\t", "") + masks := strings.Split(ws.Replace(s), ",") + l := make(Netlist, 0) + for _, mask := range masks { + if mask == "" { + continue + } + _, n, err := net.ParseCIDR(mask) + if err != nil { + return nil, err + } + l = append(l, *n) + } + return &l, nil +} + +// Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is +// intended to be used for setting up static lists. +func (l *Netlist) Add(cidr string) { + _, n, err := net.ParseCIDR(cidr) + if err != nil { + panic(err) + } + *l = append(*l, *n) +} + +// Contains reports whether the given IP is contained in the list. +func (l *Netlist) Contains(ip net.IP) bool { + if l == nil { + return false + } + for _, net := range *l { + if net.Contains(ip) { + return true + } + } + return false +} + +// IsLAN reports whether an IP is a local network address. +func IsLAN(ip net.IP) bool { + if ip.IsLoopback() { + return true + } + if v4 := ip.To4(); v4 != nil { + return lan4.Contains(v4) + } + return lan6.Contains(ip) +} + +// IsSpecialNetwork reports whether an IP is located in a special-use network range +// This includes broadcast, multicast and documentation addresses. +func IsSpecialNetwork(ip net.IP) bool { + if ip.IsMulticast() { + return true + } + if v4 := ip.To4(); v4 != nil { + return special4.Contains(v4) + } + return special6.Contains(ip) +} + +var ( + errInvalid = errors.New("invalid IP") + errUnspecified = errors.New("zero address") + errSpecial = errors.New("special network") + errLoopback = errors.New("loopback address from non-loopback host") + errLAN = errors.New("LAN address from WAN host") +) + +// CheckRelayIP reports whether an IP relayed from the given sender IP +// is a valid connection target. +// +// There are four rules: +// - Special network addresses are never valid. +// - Loopback addresses are OK if relayed by a loopback host. +// - LAN addresses are OK if relayed by a LAN host. +// - All other addresses are always acceptable. +func CheckRelayIP(sender, addr net.IP) error { + if len(addr) != net.IPv4len && len(addr) != net.IPv6len { + return errInvalid + } + if addr.IsUnspecified() { + return errUnspecified + } + if IsSpecialNetwork(addr) { + return errSpecial + } + if addr.IsLoopback() && !sender.IsLoopback() { + return errLoopback + } + if IsLAN(addr) && !IsLAN(sender) { + return errLAN + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_notwindows.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_notwindows.go similarity index 91% rename from vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_notwindows.go rename to vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_notwindows.go index e9de83aa9..47b643857 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_notwindows.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_notwindows.go @@ -16,9 +16,9 @@ //+build !windows -package discover +package netutil -// reports whether err indicates that a UDP packet didn't +// isPacketTooBig reports whether err indicates that a UDP packet didn't // fit the receive buffer. There is no such error on // non-Windows platforms. func isPacketTooBig(err error) bool { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_windows.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_windows.go similarity index 93% rename from vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_windows.go rename to vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_windows.go index 1ab9d655e..dfbb6d44f 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_windows.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_windows.go @@ -16,7 +16,7 @@ //+build windows -package discv5 +package netutil import ( "net" @@ -26,7 +26,7 @@ import ( const _WSAEMSGSIZE = syscall.Errno(10040) -// reports whether err indicates that a UDP packet didn't +// isPacketTooBig reports whether err indicates that a UDP packet didn't // fit the receive buffer. On Windows, WSARecvFrom returns // code WSAEMSGSIZE and no data if this happens. func isPacketTooBig(err error) bool { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/server.go b/vendor/github.com/ethereum/go-ethereum/p2p/server.go index 7381127dc..cf9672e2d 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/server.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/server.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" ) const ( @@ -101,6 +102,11 @@ type Config struct { // allowed to connect, even above the peer limit. TrustedNodes []*discover.Node + // Connectivity can be restricted to certain IP networks. + // If this option is set to a non-nil value, only hosts which match one of the + // IP networks contained in the list are considered. + NetRestrict *netutil.Netlist + // NodeDatabase is the path to the database containing the previously seen // live nodes in the network. NodeDatabase string @@ -356,7 +362,7 @@ func (srv *Server) Start() (err error) { // node table if srv.Discovery { - ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase) + ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict) if err != nil { return err } @@ -367,7 +373,7 @@ func (srv *Server) Start() (err error) { } if srv.DiscoveryV5 { - ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "") //srv.NodeDatabase) + ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "", srv.NetRestrict) //srv.NodeDatabase) if err != nil { return err } @@ -381,7 +387,7 @@ func (srv *Server) Start() (err error) { if !srv.Discovery { dynPeers = 0 } - dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers) + dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers, srv.NetRestrict) // handshake srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)} @@ -634,8 +640,19 @@ func (srv *Server) listenLoop() { } break } + + // Reject connections that do not match NetRestrict. + if srv.NetRestrict != nil { + if tcp, ok := fd.RemoteAddr().(*net.TCPAddr); ok && !srv.NetRestrict.Contains(tcp.IP) { + glog.V(logger.Debug).Infof("Rejected conn %v because it is not whitelisted in NetRestrict", fd.RemoteAddr()) + fd.Close() + slots <- struct{}{} + continue + } + } + fd = newMeteredConn(fd, true) - glog.V(logger.Debug).Infof("Accepted conn %v\n", fd.RemoteAddr()) + glog.V(logger.Debug).Infof("Accepted conn %v", fd.RemoteAddr()) // Spawn the handler. It will give the slot back when the connection // has been established. diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go b/vendor/github.com/ethereum/go-ethereum/params/version.go similarity index 51% rename from vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go rename to vendor/github.com/ethereum/go-ethereum/params/version.go index 3070d10b5..f8c0d3c9a 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go +++ b/vendor/github.com/ethereum/go-ethereum/params/version.go @@ -14,23 +14,14 @@ // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . -// Package utils contains internal helper functions for go-ethereum commands. -package utils +package params -import ( - "fmt" - "runtime" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" -) +import "fmt" const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 5 // Minor version component of the current release - VersionPatch = 4 // Patch version component of the current release + VersionPatch = 5 // Patch version component of the current release VersionMeta = "unstable" // Version metadata to append to the version string ) @@ -42,23 +33,3 @@ var Version = func() string { } return v }() - -// MakeDefaultExtraData returns the default Ethereum block extra data blob. -func MakeDefaultExtraData(clientIdentifier string) []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 -} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go index c04a015ef..14a559c75 100644 --- a/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go @@ -35,9 +35,8 @@ const ( port = "8500" ) -// by default ens root is north internal var ( - toyNetEnsRoot = common.HexToAddress("0xd344889e0be3e9ef6c26b0f60ef66a32e83c1b69") + ensRootAddress = common.HexToAddress("0x112234455c3a32fd11230c42e7bccd4a84e02010") ) // separate bzz directories @@ -54,11 +53,12 @@ type Config struct { PublicKey string BzzKey string EnsRoot common.Address + NetworkId uint64 } // config is agnostic to where private key is coming from // so managing accounts is outside swarm and left to wrappers -func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (self *Config, err error) { +func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, networkId uint64) (self *Config, err error) { address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes())) err = os.MkdirAll(dirpath, os.ModePerm) @@ -81,7 +81,8 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) ( Swap: swap.DefaultSwapParams(contract, prvKey), PublicKey: pubkeyhex, BzzKey: keyhex, - EnsRoot: toyNetEnsRoot, + EnsRoot: ensRootAddress, + NetworkId: networkId, } data, err = ioutil.ReadFile(confpath) if err != nil { @@ -111,7 +112,7 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) ( self.Swap.SetKey(prvKey) if (self.EnsRoot == common.Address{}) { - self.EnsRoot = toyNetEnsRoot + self.EnsRoot = ensRootAddress } return diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go index f5ebdd008..f81761b97 100644 --- a/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/swarm/network/kademlia" "github.com/ethereum/go-ethereum/swarm/storage" ) @@ -288,6 +289,10 @@ func newNodeRecord(addr *peerAddr) *kademlia.NodeRecord { func (self *Hive) HandlePeersMsg(req *peersMsgData, from *peer) { var nrs []*kademlia.NodeRecord for _, p := range req.Peers { + if err := netutil.CheckRelayIP(from.remoteAddr.IP, p.IP); err != nil { + glog.V(logger.Detail).Infof("invalid peer IP %v from %v: %v", from.remoteAddr.IP, p.IP, err) + continue + } nrs = append(nrs, newNodeRecord(p)) } self.kad.Add(nrs) diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go index 5e65108d6..a3ffd338f 100644 --- a/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go @@ -95,6 +95,7 @@ type bzz struct { errors *errs.Errors // errors table backend chequebook.Backend lastActive time.Time + NetworkId uint64 swap *swap.Swap // swap instance for the peer connection swapParams *bzzswap.SwapParams // swap settings both local and remote @@ -126,7 +127,7 @@ on each peer connection The Run function of the Bzz protocol class creates a bzz instance which will represent the peer for the swarm hive and all peer-aware components */ -func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams) (p2p.Protocol, error) { +func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64) (p2p.Protocol, error) { // a single global request db is created for all peer connections // this is to persist delivery backlog and aid syncronisation @@ -134,13 +135,15 @@ func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess if err != nil { return p2p.Protocol{}, fmt.Errorf("error setting up request db: %v", err) } - + if networkId == 0 { + networkId = NetworkId + } return p2p.Protocol{ Name: "bzz", Version: Version, Length: ProtocolLength, Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { - return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, p, rw) + return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, networkId, p, rw) }, }, nil } @@ -157,7 +160,7 @@ the main protocol loop that * whenever the loop terminates, the peer will disconnect with Subprotocol error * whenever handlers return an error the loop terminates */ -func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) { +func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) { self := &bzz{ storage: depo, @@ -175,6 +178,7 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook syncParams: sy, swapEnabled: hive.swapEnabled, syncEnabled: true, + NetworkId: networkId, } // handle handshake @@ -340,7 +344,7 @@ func (self *bzz) handleStatus() (err error) { Version: uint64(Version), ID: "honey", Addr: self.selfAddr(), - NetworkId: uint64(NetworkId), + NetworkId: uint64(self.NetworkId), Swap: &bzzswap.SwapProfile{ Profile: self.swapParams.Profile, PayProfile: self.swapParams.PayProfile, @@ -372,8 +376,8 @@ func (self *bzz) handleStatus() (err error) { return self.protoError(ErrDecode, " %v: %v", msg, err) } - if status.NetworkId != NetworkId { - return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId) + if status.NetworkId != self.NetworkId { + return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, self.NetworkId) } if Version != status.Version { diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go b/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go index 1dce8cafd..7e38944de 100644 --- a/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go +++ b/vendor/github.com/ethereum/go-ethereum/swarm/swarm.go @@ -209,7 +209,7 @@ func (self *Swarm) Stop() error { // implements the node.Service interface func (self *Swarm) Protocols() []p2p.Protocol { - proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams) + proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams, self.config.NetworkId) if err != nil { return nil } @@ -279,7 +279,7 @@ func NewLocalSwarm(datadir, port string) (self *Swarm, err error) { return } - config, err := api.NewConfig(datadir, common.Address{}, prvKey) + config, err := api.NewConfig(datadir, common.Address{}, prvKey, network.NetworkId) if err != nil { return } diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go b/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go index 50a8eb34a..6ed3e17c2 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go @@ -149,13 +149,13 @@ func (api *PublicWhisperAPI) DeleteSymKey(name string) error { // NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. // Returns the ID of the newly created Filter. -func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (*rpc.HexNumber, error) { +func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (uint32, error) { if api.whisper == nil { - return nil, whisperOffLineErr + return 0, whisperOffLineErr } filter := whisperv5.Filter{ - Src: crypto.ToECDSAPub(args.From), + Src: crypto.ToECDSAPub(common.FromHex(args.From)), KeySym: api.whisper.GetSymKey(args.KeyName), PoW: args.PoW, Messages: make(map[common.Hash]*whisperv5.ReceivedMessage), @@ -173,39 +173,39 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (*rpc.HexNumber, if len(args.Topics) == 0 { info := "NewFilter: at least one topic must be specified" glog.V(logger.Error).Infof(info) - return nil, errors.New(info) + return 0, errors.New(info) } if len(args.KeyName) != 0 && len(filter.KeySym) == 0 { info := "NewFilter: key was not found by name: " + args.KeyName glog.V(logger.Error).Infof(info) - return nil, errors.New(info) + return 0, errors.New(info) } if len(args.To) == 0 && len(filter.KeySym) == 0 { info := "NewFilter: filter must contain either symmetric or asymmetric key" glog.V(logger.Error).Infof(info) - return nil, errors.New(info) + return 0, errors.New(info) } if len(args.To) != 0 && len(filter.KeySym) != 0 { info := "NewFilter: filter must not contain both symmetric and asymmetric key" glog.V(logger.Error).Infof(info) - return nil, errors.New(info) + return 0, errors.New(info) } if len(args.To) > 0 { - dst := crypto.ToECDSAPub(args.To) + dst := crypto.ToECDSAPub(common.FromHex(args.To)) if !whisperv5.ValidatePublicKey(dst) { info := "NewFilter: Invalid 'To' address" glog.V(logger.Error).Infof(info) - return nil, errors.New(info) + return 0, errors.New(info) } filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) if filter.KeyAsym == nil { info := "NewFilter: non-existent identity provided" glog.V(logger.Error).Infof(info) - return nil, errors.New(info) + return 0, errors.New(info) } } @@ -213,22 +213,22 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (*rpc.HexNumber, if !whisperv5.ValidatePublicKey(filter.Src) { info := "NewFilter: Invalid 'From' address" glog.V(logger.Error).Infof(info) - return nil, errors.New(info) + return 0, errors.New(info) } } id := api.whisper.Watch(&filter) - return rpc.NewHexNumber(id), nil + return id, nil } // UninstallFilter disables and removes an existing filter. -func (api *PublicWhisperAPI) UninstallFilter(filterId rpc.HexNumber) { - api.whisper.Unwatch(filterId.Int()) +func (api *PublicWhisperAPI) UninstallFilter(filterId uint32) { + api.whisper.Unwatch(filterId) } // GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. -func (api *PublicWhisperAPI) GetFilterChanges(filterId rpc.HexNumber) []WhisperMessage { - f := api.whisper.GetFilter(filterId.Int()) +func (api *PublicWhisperAPI) GetFilterChanges(filterId uint32) []WhisperMessage { + f := api.whisper.GetFilter(filterId) if f != nil { newMail := f.Retrieve() return toWhisperMessages(newMail) @@ -237,8 +237,8 @@ func (api *PublicWhisperAPI) GetFilterChanges(filterId rpc.HexNumber) []WhisperM } // GetMessages retrieves all the known messages that match a specific filter. -func (api *PublicWhisperAPI) GetMessages(filterId rpc.HexNumber) []WhisperMessage { - all := api.whisper.Messages(filterId.Int()) +func (api *PublicWhisperAPI) GetMessages(filterId uint32) []WhisperMessage { + all := api.whisper.Messages(filterId) return toWhisperMessages(all) } @@ -259,7 +259,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { params := whisperv5.MessageParams{ TTL: args.TTL, - Dst: crypto.ToECDSAPub(args.To), + Dst: crypto.ToECDSAPub(common.FromHex(args.To)), KeySym: api.whisper.GetSymKey(args.KeyName), Topic: args.Topic, Payload: args.Payload, @@ -269,7 +269,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { } if len(args.From) > 0 { - pub := crypto.ToECDSAPub(args.From) + pub := crypto.ToECDSAPub(common.FromHex(args.From)) if !whisperv5.ValidatePublicKey(pub) { info := "Post: Invalid 'From' address" glog.V(logger.Error).Infof(info) @@ -284,7 +284,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { } filter := api.whisper.GetFilter(args.FilterID) - if filter == nil && args.FilterID > -1 { + if filter == nil && args.FilterID > 0 { info := fmt.Sprintf("Post: wrong filter id %d", args.FilterID) glog.V(logger.Error).Infof(info) return errors.New(info) @@ -321,13 +321,13 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { return errors.New(info) } - if len(args.To) == 0 && len(args.KeyName) == 0 { + if len(args.To) == 0 && len(params.KeySym) == 0 { info := "Post: message must be encrypted either symmetrically or asymmetrically" glog.V(logger.Error).Infof(info) return errors.New(info) } - if len(args.To) != 0 && len(args.KeyName) != 0 { + if len(args.To) != 0 && len(params.KeySym) != 0 { info := "Post: ambigous encryption method requested" glog.V(logger.Error).Infof(info) return errors.New(info) @@ -368,60 +368,21 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { type PostArgs struct { TTL uint32 `json:"ttl"` - From rpc.HexBytes `json:"from"` - To rpc.HexBytes `json:"to"` + From string `json:"from"` + To string `json:"to"` KeyName string `json:"keyname"` Topic whisperv5.TopicType `json:"topic"` Padding rpc.HexBytes `json:"padding"` Payload rpc.HexBytes `json:"payload"` WorkTime uint32 `json:"worktime"` PoW float64 `json:"pow"` - FilterID int `json:"filter"` - PeerID rpc.HexBytes `json:"directP2P"` -} - -func (args *PostArgs) UnmarshalJSON(data []byte) (err error) { - var obj struct { - TTL uint32 `json:"ttl"` - From rpc.HexBytes `json:"from"` - To rpc.HexBytes `json:"to"` - KeyName string `json:"keyname"` - Topic whisperv5.TopicType `json:"topic"` - Payload rpc.HexBytes `json:"payload"` - Padding rpc.HexBytes `json:"padding"` - WorkTime uint32 `json:"worktime"` - PoW float64 `json:"pow"` - FilterID rpc.HexBytes `json:"filter"` - PeerID rpc.HexBytes `json:"directP2P"` - } - - if err := json.Unmarshal(data, &obj); err != nil { - return err - } - - args.TTL = obj.TTL - args.From = obj.From - args.To = obj.To - args.KeyName = obj.KeyName - args.Topic = obj.Topic - args.Payload = obj.Payload - args.Padding = obj.Padding - args.WorkTime = obj.WorkTime - args.PoW = obj.PoW - args.FilterID = -1 - args.PeerID = obj.PeerID - - if obj.FilterID != nil { - x := whisperv5.BytesToIntBigEndian(obj.FilterID) - args.FilterID = int(x) - } - - return nil + FilterID uint32 `json:"filterID"` + PeerID rpc.HexBytes `json:"peerID"` } type WhisperFilterArgs struct { - To []byte - From []byte + To string + From string KeyName string PoW float64 Topics []whisperv5.TopicType @@ -433,8 +394,8 @@ type WhisperFilterArgs struct { func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { // Unmarshal the JSON message and sanity check var obj struct { - To rpc.HexBytes `json:"to"` - From rpc.HexBytes `json:"from"` + To string `json:"to"` + From string `json:"from"` KeyName string `json:"keyname"` PoW float64 `json:"pow"` Topics []interface{} `json:"topics"` diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go index ef3b93d12..223d8246e 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go @@ -46,15 +46,16 @@ const ( messagesCode = 1 p2pCode = 2 mailRequestCode = 3 - NumberOfMessageCodes = 4 + NumberOfMessageCodes = 32 paddingMask = byte(3) signatureFlag = byte(4) - TopicLength = 4 - signatureLength = 65 - aesKeyLength = 32 - saltLength = 12 + TopicLength = 4 + signatureLength = 65 + aesKeyLength = 32 + saltLength = 12 + AESNonceMaxLength = 12 MaxMessageLength = 0xFFFF // todo: remove this restriction after testing in morden and analizing stats. this should be regulated by MinimumPoW. MinimumPoW = 10.0 // todo: review diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go index 57d454a08..3d048bb44 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go @@ -73,7 +73,7 @@ func NewEnvelope(ttl uint32, topic TopicType, salt []byte, aesNonce []byte, msg } func (e *Envelope) IsSymmetric() bool { - return e.AESNonce != nil + return len(e.AESNonce) > 0 } func (e *Envelope) isAsymmetric() bool { @@ -131,7 +131,7 @@ func (e *Envelope) calculatePoW(diff uint32) { h = crypto.Keccak256(buf) firstBit := common.FirstBitSet(common.BigD(h)) x := math.Pow(2, float64(firstBit)) - x /= float64(len(e.Data)) + x /= float64(len(e.Data)) // we only count e.Data, other variable-sized members are checked in Whisper.add() x /= float64(e.TTL + diff) e.pow = x } diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go index 5cc7be587..fd5f5083f 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go @@ -37,20 +37,20 @@ type Filter struct { } type Filters struct { - id int - watchers map[int]*Filter + id uint32 // can contain any value except zero + watchers map[uint32]*Filter whisper *Whisper mutex sync.RWMutex } func NewFilters(w *Whisper) *Filters { return &Filters{ - watchers: make(map[int]*Filter), + watchers: make(map[uint32]*Filter), whisper: w, } } -func (fs *Filters) Install(watcher *Filter) int { +func (fs *Filters) Install(watcher *Filter) uint32 { if watcher.Messages == nil { watcher.Messages = make(map[common.Hash]*ReceivedMessage) } @@ -58,19 +58,18 @@ func (fs *Filters) Install(watcher *Filter) int { fs.mutex.Lock() defer fs.mutex.Unlock() - fs.watchers[fs.id] = watcher - ret := fs.id fs.id++ - return ret + fs.watchers[fs.id] = watcher + return fs.id } -func (fs *Filters) Uninstall(id int) { +func (fs *Filters) Uninstall(id uint32) { fs.mutex.Lock() defer fs.mutex.Unlock() delete(fs.watchers, id) } -func (fs *Filters) Get(i int) *Filter { +func (fs *Filters) Get(i uint32) *Filter { fs.mutex.RLock() defer fs.mutex.RUnlock() return fs.watchers[i] @@ -143,18 +142,9 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { } if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { - // if Dst match, ignore the topic - return isPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) + return isPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic) } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { - // check if that both the key and the topic match - if f.SymKeyHash == msg.SymKeyHash { - for _, t := range f.Topics { - if t == msg.Topic { - return true - } - } - return false - } + return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic) } return false } @@ -164,25 +154,25 @@ func (f *Filter) MatchEnvelope(envelope *Envelope) bool { return false } - encryptionMethodMatch := false if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() { - encryptionMethodMatch = true - if f.Topics == nil { - // wildcard + return f.MatchTopic(envelope.Topic) + } else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() { + return f.MatchTopic(envelope.Topic) + } + return false +} + +func (f *Filter) MatchTopic(topic TopicType) bool { + if len(f.Topics) == 0 { + // any topic matches + return true + } + + for _, t := range f.Topics { + if t == topic { return true } - } else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() { - encryptionMethodMatch = true } - - if encryptionMethodMatch { - for _, t := range f.Topics { - if t == envelope.Topic { - return true - } - } - } - return false } diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go index 680d1f8a2..f3812b1d8 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go @@ -130,7 +130,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) { panic("please fix the padding algorithm before releasing new version") } buf := make([]byte, padSize) - randomize(buf[1:]) // change to: err = mrand.Read(buf[1:]) + randomize(buf[1:]) buf[0] = byte(padSize) if params.Padding != nil { copy(buf[1:], params.Padding) @@ -208,7 +208,10 @@ func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte, _, err = crand.Read(nonce) if err != nil { return nil, nil, err + } else if !validateSymmetricKey(nonce) { + return nil, nil, errors.New("crypto/rand failed to generate nonce") } + msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) return salt, nonce, nil } diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go index fc1afb6d6..4273cfce1 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go @@ -165,6 +165,9 @@ func (p *Peer) broadcast() error { p.mark(envelope) } } + if len(transmit) == 0 { + return nil + } // Transmit the unknown batch (potentially empty) if err := p2p.Send(p.ws, messagesCode, transmit); err != nil { return err diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go index 836810824..dc9571f6e 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go @@ -177,12 +177,13 @@ func (w *Whisper) GetIdentity(pubKey string) *ecdsa.PrivateKey { } func (w *Whisper) GenerateSymKey(name string) error { - buf := make([]byte, aesKeyLength*2) - _, err := crand.Read(buf) // todo: check how safe is this function + const size = aesKeyLength * 2 + buf := make([]byte, size) + _, err := crand.Read(buf) if err != nil { return err } else if !validateSymmetricKey(buf) { - return fmt.Errorf("crypto/rand failed to generate random data") + return fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data") } key := buf[:aesKeyLength] @@ -245,16 +246,16 @@ func (w *Whisper) GetSymKey(name string) []byte { // Watch installs a new message handler to run in case a matching packet arrives // from the whisper network. -func (w *Whisper) Watch(f *Filter) int { +func (w *Whisper) Watch(f *Filter) uint32 { return w.filters.Install(f) } -func (w *Whisper) GetFilter(id int) *Filter { +func (w *Whisper) GetFilter(id uint32) *Filter { return w.filters.Get(id) } // Unwatch removes an installed message handler. -func (w *Whisper) Unwatch(id int) { +func (w *Whisper) Unwatch(id uint32) { w.filters.Uninstall(id) } @@ -404,7 +405,7 @@ func (wh *Whisper) add(envelope *Envelope) error { return fmt.Errorf("oversized Version") } - if len(envelope.AESNonce) > 12 { + if len(envelope.AESNonce) > AESNonceMaxLength { // the standard AES GSM nonce size is 12, // but const gcmStandardNonceSize cannot be accessed directly return fmt.Errorf("oversized AESNonce") @@ -507,7 +508,7 @@ func (w *Whisper) Envelopes() []*Envelope { } // Messages retrieves all the decrypted messages matching a filter id. -func (w *Whisper) Messages(id int) []*ReceivedMessage { +func (w *Whisper) Messages(id uint32) []*ReceivedMessage { result := make([]*ReceivedMessage, 0) w.poolMu.RLock() defer w.poolMu.RUnlock()