Merge branch 'release/1.1.0'
This commit is contained in:
commit
20cb0c64dd
5
Makefile
5
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
449
geth/node.go
449
geth/node.go
|
@ -1,304 +1,249 @@
|
|||
package geth
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
package geth
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris windows
|
||||
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris
|
||||
|
||||
package accounts
|
||||
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +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.
|
||||
|
|
|
@ -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:<IP>)")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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: "<genesisPath>",
|
||||
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.")
|
||||
|
|
|
@ -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: "<blockNum> <outputDir>",
|
||||
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: "<genesisPath>",
|
||||
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 <block number> <outputdir>`)
|
||||
}
|
||||
switch {
|
||||
case len(args) == 2:
|
||||
blockNum, err := strconv.ParseUint(args[0], 0, 64)
|
||||
dir := args[1]
|
||||
if err != nil {
|
||||
wrongArgs()
|
||||
} else {
|
||||
dir = filepath.Clean(dir)
|
||||
// seems to require a trailing slash
|
||||
if !strings.HasSuffix(dir, "/") {
|
||||
dir = dir + "/"
|
||||
}
|
||||
_, err = ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't find dir")
|
||||
}
|
||||
fmt.Println("making DAG, this could take awhile...")
|
||||
ethash.MakeDAG(blockNum, dir)
|
||||
}
|
||||
default:
|
||||
wrongArgs()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func 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 <http://www.gnu.org/licenses/>.
|
||||
`)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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: "<blockNum> <outputDir>",
|
||||
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 <block number> <outputdir>`)
|
||||
}
|
||||
switch {
|
||||
case len(args) == 2:
|
||||
blockNum, err := strconv.ParseUint(args[0], 0, 64)
|
||||
dir := args[1]
|
||||
if err != nil {
|
||||
wrongArgs()
|
||||
} else {
|
||||
dir = filepath.Clean(dir)
|
||||
// seems to require a trailing slash
|
||||
if !strings.HasSuffix(dir, "/") {
|
||||
dir = dir + "/"
|
||||
}
|
||||
_, err = ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't find dir")
|
||||
}
|
||||
fmt.Println("making DAG, this could take awhile...")
|
||||
ethash.MakeDAG(blockNum, dir)
|
||||
}
|
||||
default:
|
||||
wrongArgs()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func 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 <http://www.gnu.org/licenses/>.
|
||||
`)
|
||||
return nil
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package 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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
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
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<X> 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):], ""
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@ type ChainEvent struct {
|
|||
|
||||
type ChainSideEvent struct {
|
||||
Block *types.Block
|
||||
Logs vm.Logs
|
||||
}
|
||||
|
||||
type PendingBlockEvent struct {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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) {}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{})
|
||||
}
|
||||
|
|
|
@ -15,104 +15,3 @@
|
|||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
|
||||
func dagFiles(epoch uint64) (string, string) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//+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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//+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)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -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.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue