diff --git a/Makefile b/Makefile index 4dbaf99a7..ca759685f 100644 --- a/Makefile +++ b/Makefile @@ -5,30 +5,47 @@ GOBIN = build/bin GO ?= latest statusgo: - build/env.sh go build -i -o $(GOBIN)/statusgo -v $(shell build/flags.sh) ./cmd/status + build/env.sh go build -i -o $(GOBIN)/statusgo -v $(shell build/testnet-flags.sh) ./cmd/status @echo "status go compilation done." @echo "Run \"build/bin/statusgo\" to view available commands" statusgo-cross: statusgo-android statusgo-ios - @echo "Full cross compilation done:" + @echo "Full cross compilation done." @ls -ld $(GOBIN)/statusgo-* statusgo-android: xgo - build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/flags.sh) ./cmd/status - @echo "Android cross compilation done:" + build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/testnet-flags.sh) ./cmd/status + @echo "Android cross compilation done." statusgo-ios: xgo - build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/flags.sh) ./cmd/status - @echo "iOS framework cross compilation done:" + build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/testnet-flags.sh) ./cmd/status + @echo "iOS framework cross compilation done." statusgo-ios-simulator: xgo - build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/flags.sh) ./cmd/status - @echo "iOS framework cross compilation done:" + build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/testnet-flags.sh) ./cmd/status + @echo "iOS framework cross compilation done." xgo: build/env.sh docker pull farazdagi/xgo build/env.sh go get github.com/karalabe/xgo +statusgo-mainnet: + build/env.sh go build -i -o $(GOBIN)/statusgo -v $(shell build/mainnet-flags.sh) ./cmd/status + @echo "status go compilation done (mainnet)." + @echo "Run \"build/bin/statusgo\" to view available commands" + +statusgo-android-mainnet: xgo + build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/mainnet-flags.sh) ./cmd/status + @echo "Android cross compilation done (mainnet)." + +statusgo-ios-mainnet: xgo + build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/mainnet-flags.sh) ./cmd/status + @echo "iOS framework cross compilation done (mainnet)." + +statusgo-ios-simulator-mainnet: xgo + build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/mainnet-flags.sh) ./cmd/status + @echo "iOS framework cross compilation done (mainnet)." + ci: build/env.sh go test -v -cover ./geth build/env.sh go test -v -cover ./jail @@ -42,6 +59,8 @@ test-all: @build/env.sh tail -n +2 coverage.out >> coverage-all.out build/env.sh go test -coverprofile=coverage.out -covermode=set ./extkeys @build/env.sh tail -n +2 coverage.out >> coverage-all.out + build/env.sh go test -coverprofile=coverage.out -covermode=set ./cmd/status + @build/env.sh tail -n +2 coverage.out >> coverage-all.out @build/env.sh go tool cover -html=coverage-all.out -o coverage.html @build/env.sh go tool cover -func=coverage-all.out diff --git a/build/mainnet-flags.sh b/build/mainnet-flags.sh new file mode 100755 index 000000000..b078b9fb7 --- /dev/null +++ b/build/mainnet-flags.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +if [ ! -f "build/env.sh" ]; then + echo "$0 must be run from the root of the repository." + exit 2 +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)'"; +fi diff --git a/build/flags.sh b/build/testnet-flags.sh old mode 100644 new mode 100755 similarity index 55% rename from build/flags.sh rename to build/testnet-flags.sh index b3d294fcf..2b04db662 --- a/build/flags.sh +++ b/build/testnet-flags.sh @@ -9,5 +9,5 @@ fi # set gitCommit when running from a Git checkout. if [ -f ".git/HEAD" ]; then - echo "-ldflags '-X 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.UseTestnet=true -X main.buildStamp=`date -u '+%Y-%m-%d.%H:%M:%S'` -X main.gitCommit=$(git rev-parse HEAD)'"; fi diff --git a/cmd/status/library.go b/cmd/status/library.go index b56540409..f14c19e36 100644 --- a/cmd/status/library.go +++ b/cmd/status/library.go @@ -6,7 +6,7 @@ import ( "fmt" "os" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "github.com/status-im/status-go/geth" "github.com/status-im/status-go/jail" ) @@ -117,28 +117,6 @@ func Logout() *C.char { return C.CString(string(outBytes)) } -//export UnlockAccount -func UnlockAccount(address, password *C.char, seconds int) *C.char { - - // This is equivalent to unlocking an account from the command line, - // just modified to unlock the account for the currently running geth node - // based on the provided arguments - err := geth.UnlockAccount(C.GoString(address), C.GoString(password), seconds) - - errString := "" - if err != nil { - fmt.Fprintln(os.Stderr, err) - errString = err.Error() - } - - out := geth.JSONError{ - Error: errString, - } - outBytes, _ := json.Marshal(&out) - - return C.CString(string(outBytes)) -} - //export CompleteTransaction func CompleteTransaction(id, password *C.char) *C.char { txHash, err := geth.CompleteTransaction(C.GoString(id), C.GoString(password)) @@ -150,6 +128,7 @@ func CompleteTransaction(id, password *C.char) *C.char { } out := geth.CompleteTransactionResult{ + Id: C.GoString(id), Hash: txHash.Hex(), Error: errString, } @@ -158,6 +137,66 @@ func CompleteTransaction(id, password *C.char) *C.char { return C.CString(string(outBytes)) } +//export CompleteTransactions +func CompleteTransactions(ids, password *C.char) *C.char { + out := geth.CompleteTransactionsResult{} + out.Results = make(map[string]geth.CompleteTransactionResult) + + results := geth.CompleteTransactions(C.GoString(ids), C.GoString(password)) + for txId, result := range results { + txResult := geth.CompleteTransactionResult{ + Id: txId, + Hash: result.Hash.Hex(), + } + if result.Error != nil { + txResult.Error = result.Error.Error() + } + out.Results[txId] = txResult + } + outBytes, _ := json.Marshal(&out) + + return C.CString(string(outBytes)) +} + +//export DiscardTransaction +func DiscardTransaction(id *C.char) *C.char { + err := geth.DiscardTransaction(C.GoString(id)) + + errString := "" + if err != nil { + fmt.Fprintln(os.Stderr, err) + errString = err.Error() + } + + out := geth.DiscardTransactionResult{ + Id: C.GoString(id), + Error: errString, + } + outBytes, _ := json.Marshal(&out) + + return C.CString(string(outBytes)) +} + +//export DiscardTransactions +func DiscardTransactions(ids *C.char) *C.char { + out := geth.DiscardTransactionsResult{} + out.Results = make(map[string]geth.DiscardTransactionResult) + + results := geth.DiscardTransactions(C.GoString(ids)) + for txId, result := range results { + txResult := geth.DiscardTransactionResult{ + Id: txId, + } + if result.Error != nil { + txResult.Error = result.Error.Error() + } + out.Results[txId] = txResult + } + outBytes, _ := json.Marshal(&out) + + return C.CString(string(outBytes)) +} + //export StartNode func StartNode(datadir *C.char) *C.char { // This starts a geth node with the given datadir diff --git a/cmd/status/library_test.go b/cmd/status/library_test.go new file mode 100644 index 000000000..27ceb04de --- /dev/null +++ b/cmd/status/library_test.go @@ -0,0 +1,15 @@ +package main + +import ( + "testing" +) + + +// the actual test functions are in non-_test.go files (so that they can use cgo i.e. import "C") +// the only intent of these wrappers is for gotest can find what tests are exposed. +func TestExportedAPI(t *testing.T) { + allTestsDone := make(chan struct{}, 1) + go testExportedAPI(t, allTestsDone) + + <- allTestsDone +} diff --git a/cmd/status/main.go b/cmd/status/main.go index 12dacd759..2c8721bb2 100644 --- a/cmd/status/main.go +++ b/cmd/status/main.go @@ -2,16 +2,17 @@ package main import ( "fmt" + "github.com/status-im/status-go/geth" ) var ( gitCommit = "rely on linker: -ldflags -X main.GitCommit" buildStamp = "rely on linker: -ldflags -X main.buildStamp" - versionMajor = 0 // Major version component of the current release - versionMinor = 9 // Minor version component of the current release - versionPatch = 1 // Patch version component of the current release - versionMeta = "unstable" // Version metadata to append to the version string + 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() { @@ -22,5 +23,10 @@ func main() { if gitCommit != "" { verString += "-" + gitCommit[:8] } - fmt.Printf("Status\nGit Commit: %s\nBuild Time: %s\nVersion: %s\n", gitCommit, buildStamp, verString) + netVersion := "mainnet" + if geth.UseTestnet == "true" { + netVersion = "testnet" + } + fmt.Printf("Status\nGit Commit: %s\nBuild Time: %s\nVersion: %s\nNetwork: %s\n", + gitCommit, buildStamp, verString, netVersion) } diff --git a/cmd/status/utils.go b/cmd/status/utils.go new file mode 100644 index 000000000..44609cc07 --- /dev/null +++ b/cmd/status/utils.go @@ -0,0 +1,1074 @@ +package main + +import "C" +import ( + "encoding/json" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "reflect" + "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" + "github.com/ethereum/go-ethereum/rpc" + "github.com/status-im/status-go/geth" +) + +const ( + testDataDir = "../../.ethereumtest" + testNodeSyncSeconds = 120 + testAddress = "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" + testAddressPassword = "asdfasdf" + newAccountPassword = "badpassword" + testAddress1 = "0xadd4d1d02e71c7360c53296968e59d57fd15e2ba" + testStatusJsFile = "../../jail/testdata/status.js" +) + +func testExportedAPI(t *testing.T, done chan struct{}) { + <-startTestNode(t) + + tests := []struct { + name string + fn func(t *testing.T) bool + }{ + { + "create main and child accounts", + testCreateChildAccount, + }, + { + "recover account", + testRecoverAccount, + }, + { + "account select/login", + testAccountSelect, + }, + { + "account logout", + testAccountLogout, + }, + { + "complete single queued transaction", + testCompleteTransaction, + }, + { + "test complete multiple queued transactions", + testCompleteMultipleQueuedTransactions, + }, + { + "discard single queued transaction", + testDiscardTransaction, + }, + { + "test discard multiple queued transactions", + testDiscardMultipleQueuedTransactions, + }, + { + "test jail initialization", + testJailInit, + }, + { + "test jailed calls", + testJailFunctionCall, + }, + } + + for _, test := range tests { + if ok := test.fn(t); !ok { + break + } + } + + done <- 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() + if err != nil { + t.Error(err) + return false + } + + // create an account + createAccountResponse := geth.AccountInfo{} + rawResponse := CreateAccount(C.CString(newAccountPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createAccountResponse); err != nil { + t.Errorf("cannot decode CreateAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if createAccountResponse.Error != "" { + t.Errorf("could not create account: %s", err) + return false + } + 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) + if err != nil { + t.Errorf("can not get account from address: %v", err) + return false + } + + // obtain decrypted key, and make sure that extended key (which will be used as root for sub-accounts) is present + account, key, err := accountManager.AccountDecryptedKey(account, newAccountPassword) + if err != nil { + t.Errorf("can not obtain decrypted account key: %v", err) + return false + } + + if key.ExtendedKey == nil { + t.Error("CKD#2 has not been generated for new account") + return false + } + + // try creating sub-account, w/o selecting main account i.e. w/o login to main account + createSubAccountResponse := geth.AccountInfo{} + rawResponse = CreateChildAccount(C.CString(""), C.CString(newAccountPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse); err != nil { + t.Errorf("cannot decode CreateChildAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if createSubAccountResponse.Error != geth.ErrNoAccountSelected.Error() { + t.Errorf("expected error is not returned (tried to create sub-account w/o login): %v", createSubAccountResponse.Error) + return false + } + + err = geth.SelectAccount(address, newAccountPassword) + if err != nil { + t.Errorf("Test failed: could not select account: %v", err) + return false + } + + // try to create sub-account with wrong password + createSubAccountResponse = geth.AccountInfo{} + rawResponse = CreateChildAccount(C.CString(""), C.CString("wrong password")) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse); err != nil { + t.Errorf("cannot decode CreateChildAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if createSubAccountResponse.Error != "cannot retreive a valid key for a given account: could not decrypt key with given passphrase" { + t.Errorf("expected error is not returned (tried to create sub-account with wrong password): %v", createSubAccountResponse.Error) + return false + } + + // create sub-account (from implicit parent) + createSubAccountResponse1 := geth.AccountInfo{} + rawResponse = CreateChildAccount(C.CString(""), C.CString(newAccountPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse1); err != nil { + t.Errorf("cannot decode CreateChildAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if createSubAccountResponse1.Error != "" { + t.Errorf("cannot create sub-account: %v", createSubAccountResponse1.Error) + return false + } + + // make sure that sub-account index automatically progresses + createSubAccountResponse2 := geth.AccountInfo{} + rawResponse = CreateChildAccount(C.CString(""), C.CString(newAccountPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse2); err != nil { + t.Errorf("cannot decode CreateChildAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if createSubAccountResponse2.Error != "" { + t.Errorf("cannot create sub-account: %v", createSubAccountResponse2.Error) + } + + if createSubAccountResponse1.Address == createSubAccountResponse2.Address || createSubAccountResponse1.PubKey == createSubAccountResponse2.PubKey { + t.Error("sub-account index auto-increament failed") + return false + } + + // create sub-account (from explicit parent) + createSubAccountResponse3 := geth.AccountInfo{} + rawResponse = CreateChildAccount(C.CString(createSubAccountResponse2.Address), C.CString(newAccountPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse3); err != nil { + t.Errorf("cannot decode CreateChildAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if createSubAccountResponse3.Error != "" { + t.Errorf("cannot create sub-account: %v", createSubAccountResponse3.Error) + } + + subAccount1, subAccount2, subAccount3 := createSubAccountResponse1.Address, createSubAccountResponse2.Address, createSubAccountResponse3.Address + subPubKey1, subPubKey2, subPubKey3 := createSubAccountResponse1.PubKey, createSubAccountResponse2.PubKey, createSubAccountResponse3.PubKey + + if subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3 { + t.Error("sub-account index auto-increament failed") + return false + } + + return true +} + +func testRecoverAccount(t *testing.T) bool { + accountManager, _ := geth.GetNodeManager().AccountManager() + + // create an account + address, pubKey, mnemonic, err := geth.CreateAccount(newAccountPassword) + if err != nil { + t.Errorf("could not create account: %v", err) + return false + } + t.Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) + + // try recovering using password + mnemonic + recoverAccountResponse := geth.AccountInfo{} + rawResponse := RecoverAccount(C.CString(newAccountPassword), C.CString(mnemonic)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &recoverAccountResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if recoverAccountResponse.Error != "" { + t.Errorf("recover account failed: %v", recoverAccountResponse.Error) + return false + } + addressCheck, pubKeyCheck := recoverAccountResponse.Address, recoverAccountResponse.PubKey + if address != addressCheck || pubKey != pubKeyCheck { + t.Error("recover account details failed to pull the correct details") + } + + // 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) + if err != nil { + t.Errorf("can not get account from address: %v", err) + } + + account, key, err := accountManager.AccountDecryptedKey(account, newAccountPassword) + if err != nil { + t.Errorf("can not obtain decrypted account key: %v", err) + return false + } + extChild2String := key.ExtendedKey.String() + + if err := accountManager.DeleteAccount(account, newAccountPassword); err != nil { + t.Errorf("cannot remove account: %v", err) + } + + recoverAccountResponse = geth.AccountInfo{} + rawResponse = RecoverAccount(C.CString(newAccountPassword), C.CString(mnemonic)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &recoverAccountResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if recoverAccountResponse.Error != "" { + t.Errorf("recover account failed (for non-cached account): %v", recoverAccountResponse.Error) + return false + } + addressCheck, pubKeyCheck = recoverAccountResponse.Address, recoverAccountResponse.PubKey + if address != addressCheck || pubKey != pubKeyCheck { + t.Error("recover account details failed to pull the correct details (for non-cached account)") + } + + // make sure that extended key exists and is imported ok too + account, key, err = accountManager.AccountDecryptedKey(account, newAccountPassword) + if err != nil { + t.Errorf("can not obtain decrypted account key: %v", err) + return false + } + if extChild2String != key.ExtendedKey.String() { + t.Errorf("CKD#2 key mismatch, expected: %s, got: %s", extChild2String, key.ExtendedKey.String()) + } + + // make sure that calling import several times, just returns from cache (no error is expected) + recoverAccountResponse = geth.AccountInfo{} + rawResponse = RecoverAccount(C.CString(newAccountPassword), C.CString(mnemonic)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &recoverAccountResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if recoverAccountResponse.Error != "" { + t.Errorf("recover account failed (for non-cached account): %v", recoverAccountResponse.Error) + return false + } + addressCheck, pubKeyCheck = recoverAccountResponse.Address, recoverAccountResponse.PubKey + if address != addressCheck || pubKey != pubKeyCheck { + t.Error("recover account details failed to pull the correct details (for non-cached account)") + } + + // time to login with recovered data + whisperService, err := geth.GetNodeManager().WhisperService() + if err != nil { + t.Errorf("whisper service not running: %v", err) + } + + // make sure that identity is not (yet injected) + if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKeyCheck))) { + t.Error("identity already present in whisper") + } + err = geth.SelectAccount(addressCheck, newAccountPassword) + if err != nil { + t.Errorf("Test failed: could not select account: %v", err) + return false + } + if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKeyCheck))) { + t.Errorf("identity not injected into whisper: %v", err) + } + + return true +} + +func testAccountSelect(t *testing.T) bool { + // test to see if the account was injected in whisper + whisperService, err := geth.GetNodeManager().WhisperService() + if err != nil { + t.Errorf("whisper service not running: %v", err) + } + + // create an account + address1, pubKey1, _, err := geth.CreateAccount(newAccountPassword) + if err != nil { + t.Errorf("could not create account: %v", err) + return false + } + t.Logf("Account created: {address: %s, key: %s}", address1, pubKey1) + + address2, pubKey2, _, err := geth.CreateAccount(newAccountPassword) + if err != nil { + t.Error("Test failed: could not create account") + return false + } + t.Logf("Account created: {address: %s, key: %s}", address2, pubKey2) + + // make sure that identity is not (yet injected) + if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) { + t.Error("identity already present in whisper") + } + + // try selecting with wrong password + loginResponse := geth.JSONError{} + rawResponse := Login(C.CString(address1), C.CString("wrongPassword")) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if loginResponse.Error == "" { + t.Error("select account is expected to throw error: wrong password used") + return false + } + + loginResponse = geth.JSONError{} + rawResponse = Login(C.CString(address1), C.CString(newAccountPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if loginResponse.Error != "" { + t.Errorf("Test failed: could not select account: %v", err) + return false + } + if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) { + t.Errorf("identity not injected into whisper: %v", err) + } + + // select another account, make sure that previous account is wiped out from Whisper cache + if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) { + t.Error("identity already present in whisper") + } + + loginResponse = geth.JSONError{} + rawResponse = Login(C.CString(address2), C.CString(newAccountPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if loginResponse.Error != "" { + t.Errorf("Test failed: could not select account: %v", loginResponse.Error) + return false + } + if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) { + t.Errorf("identity not injected into whisper: %v", err) + } + if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) { + t.Error("identity should be removed, but it is still present in whisper") + } + + return true +} + +func testAccountLogout(t *testing.T) bool { + whisperService, err := geth.GetNodeManager().WhisperService() + if err != nil { + t.Errorf("whisper service not running: %v", err) + return false + } + + // create an account + address, pubKey, _, err := geth.CreateAccount(newAccountPassword) + if err != nil { + t.Errorf("could not create account: %v", err) + return false + } + + // make sure that identity doesn't exist (yet) in Whisper + if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) { + t.Error("identity already present in whisper") + return false + } + + // select/login + err = geth.SelectAccount(address, newAccountPassword) + if err != nil { + t.Errorf("Test failed: could not select account: %v", err) + return false + } + if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) { + t.Error("identity not injected into whisper") + return false + } + + logoutResponse := geth.JSONError{} + rawResponse := Logout() + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &logoutResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + return false + } + + if logoutResponse.Error != "" { + t.Errorf("cannot logout: %v", logoutResponse.Error) + return false + } + + // now, logout and check if identity is removed indeed + if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) { + t.Error("identity not cleared from whisper") + return false + } + + return true +} + +func testCompleteTransaction(t *testing.T) bool { + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return false + } + backend := lightEthereum.StatusBackend + + // reset queue + backend.TransactionQueue().Reset() + + // make sure you panic if transaction complete doesn't return + queuedTxCompleted := make(chan struct{}, 1) + abortPanic := make(chan struct{}, 1) + geth.PanicAfter(10*time.Second, abortPanic, "testCompleteTransaction") + + // replace transaction notification handler + var txHash = "" + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + t.Logf("transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string)) + time.Sleep(5 * time.Second) + + completeTxResponse := geth.CompleteTransactionResult{} + rawResponse := CompleteTransaction(C.CString(event["id"].(string)), C.CString(testAddressPassword)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &completeTxResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + } + + if completeTxResponse.Error != "" { + t.Errorf("cannot complete queued transation[%v]: %v", event["id"], completeTxResponse.Error) + } + + txHash = completeTxResponse.Hash + + t.Logf("transaction complete: https://testnet.etherscan.io/tx/%s", txHash) + abortPanic <- struct{}{} // so that timeout is aborted + queuedTxCompleted <- struct{}{} + } + }) + + // this call blocks, up until Complete Transaction is called + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != nil { + t.Errorf("Test failed: cannot send transaction: %v", err) + } + + <-queuedTxCompleted // make sure that complete transaction handler completes its magic, before we proceed + + if txHash != txHashCheck.Hex() { + t.Errorf("Transaction hash returned from SendTransaction is invalid: expected %s, got %s", txHashCheck.Hex(), txHash) + return false + } + + if reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("Test failed: transaction was never queued or completed") + return false + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return false + } + + return true +} + +func testCompleteMultipleQueuedTransactions(t *testing.T) bool { + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return false + } + backend := lightEthereum.StatusBackend + + // reset queue + backend.TransactionQueue().Reset() + + // make sure you panic if transaction complete doesn't return + testTxCount := 3 + txIds := make(chan string, testTxCount) + allTestTxCompleted := make(chan struct{}, 1) + + // replace transaction notification handler + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var txId string + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + txId = event["id"].(string) + t.Logf("transaction queued (will be completed in a single call, once aggregated): {id: %s}\n", txId) + + txIds <- txId + } + }) + + // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called + sendTx := func() { + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != nil { + t.Errorf("unexpected error thrown: %v", err) + return + } + + if reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("transaction returned empty hash") + return + } + } + + // wait for transactions, and complete them in a single call + completeTxs := func(txIdStrings string) { + var parsedIds []string + json.Unmarshal([]byte(txIdStrings), &parsedIds) + + parsedIds = append(parsedIds, "invalid-tx-id") + updatedTxIdStrings, _ := json.Marshal(parsedIds) + + // complete + resultsString := CompleteTransactions(C.CString(string(updatedTxIdStrings)), C.CString(testAddressPassword)) + resultsStruct := geth.CompleteTransactionsResult{} + json.Unmarshal([]byte(C.GoString(resultsString)), &resultsStruct) + results := resultsStruct.Results + + if len(results) != (testTxCount+1) || results["invalid-tx-id"].Error != "transaction hash not found" { + t.Errorf("cannot complete txs: %v", results) + return + } + for txId, txResult := range results { + if txId != txResult.Id { + t.Errorf("tx id not set in result: expected id is %s", txId) + return + } + if txResult.Error != "" && txId != "invalid-tx-id" { + t.Errorf("invalid error for %s", txId) + return + } + if txResult.Hash == "0x0000000000000000000000000000000000000000000000000000000000000000" && txId != "invalid-tx-id" { + t.Errorf("invalid hash (expected non empty hash): %s", txId) + return + } + + if txResult.Hash != "0x0000000000000000000000000000000000000000000000000000000000000000" { + t.Logf("transaction complete: https://testnet.etherscan.io/tx/%s", txResult.Hash) + } + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + for _, txId := range parsedIds { + if backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should not have test tx at this point (it should be completed): %s", txId) + return + } + } + } + go func() { + var txIdStrings []string + for i := 0; i < testTxCount; i++ { + txIdStrings = append(txIdStrings, <-txIds) + } + + txIdJSON, _ := json.Marshal(txIdStrings) + completeTxs(string(txIdJSON)) + allTestTxCompleted <- struct{}{} + }() + + // send multiple transactions + for i := 0; i < testTxCount; i++ { + go sendTx() + } + + select { + case <-allTestTxCompleted: + // pass + case <-time.After(20 * time.Second): + t.Error("test timed out") + return false + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return false + } + + return true +} + +func testDiscardTransaction(t *testing.T) bool { + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return false + } + backend := lightEthereum.StatusBackend + + // reset queue + backend.TransactionQueue().Reset() + + // make sure you panic if transaction complete doesn't return + completeQueuedTransaction := make(chan struct{}, 1) + geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestDiscardQueuedTransactions") + + // replace transaction notification handler + var txId string + txFailedEventCalled := false + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + txId = event["id"].(string) + t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txId) + + if !backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should still have test tx: %s", txId) + return + } + + // discard + discardResponse := geth.DiscardTransactionResult{} + rawResponse := DiscardTransaction(C.CString(txId)) + + if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &discardResponse); err != nil { + t.Errorf("cannot decode RecoverAccount reponse (%s): %v", C.GoString(rawResponse), err) + } + + if discardResponse.Error != "" { + t.Errorf("cannot discard tx: %v", discardResponse.Error) + return + } + + // try completing discarded transaction + _, err = geth.CompleteTransaction(txId, testAddressPassword) + if err.Error() != "transaction hash not found" { + t.Error("expects tx not found, but call to CompleteTransaction succeeded") + return + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + if backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txId) + return + } + + completeQueuedTransaction <- struct{}{} // so that timeout is aborted + } + + if envelope.Type == geth.EventTransactionFailed { + event := envelope.Event.(map[string]interface{}) + t.Logf("transaction return event received: {id: %s}\n", event["id"].(string)) + + receivedErrMessage := event["error_message"].(string) + expectedErrMessage := status.ErrQueuedTxDiscarded.Error() + if receivedErrMessage != expectedErrMessage { + t.Errorf("unexpected error message received: got %v", receivedErrMessage) + return + } + + receivedErrCode := event["error_code"].(string) + if receivedErrCode != geth.SendTransactionDiscardedErrorCode { + t.Errorf("unexpected error code received: got %v", receivedErrCode) + return + } + + txFailedEventCalled = true + } + }) + + // this call blocks, and should return when DiscardQueuedTransaction() is called + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != status.ErrQueuedTxDiscarded { + t.Errorf("expected error not thrown: %v", err) + return false + } + + if !reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("transaction returned hash, while it shouldn't") + return false + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return false + } + + if !txFailedEventCalled { + t.Error("expected tx failure signal is not received") + return false + } + + return true +} + +func testDiscardMultipleQueuedTransactions(t *testing.T) bool { + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return false + } + backend := lightEthereum.StatusBackend + + // reset queue + backend.TransactionQueue().Reset() + + // make sure you panic if transaction complete doesn't return + testTxCount := 3 + txIds := make(chan string, testTxCount) + allTestTxDiscarded := make(chan struct{}, 1) + + // replace transaction notification handler + txFailedEventCallCount := 0 + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var txId string + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + txId = event["id"].(string) + t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txId) + + if !backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should still have test tx: %s", txId) + return + } + + txIds <- txId + } + + if envelope.Type == geth.EventTransactionFailed { + event := envelope.Event.(map[string]interface{}) + t.Logf("transaction return event received: {id: %s}\n", event["id"].(string)) + + receivedErrMessage := event["error_message"].(string) + expectedErrMessage := status.ErrQueuedTxDiscarded.Error() + if receivedErrMessage != expectedErrMessage { + t.Errorf("unexpected error message received: got %v", receivedErrMessage) + return + } + + receivedErrCode := event["error_code"].(string) + if receivedErrCode != geth.SendTransactionDiscardedErrorCode { + t.Errorf("unexpected error code received: got %v", receivedErrCode) + return + } + + txFailedEventCallCount++ + if txFailedEventCallCount == testTxCount { + allTestTxDiscarded <- struct{}{} + } + } + }) + + // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called + sendTx := func() { + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != status.ErrQueuedTxDiscarded { + t.Errorf("expected error not thrown: %v", err) + return + } + + if !reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("transaction returned hash, while it shouldn't") + return + } + } + + // wait for transactions, and discard immediately + discardTxs := func(txIdStrings string) { + var parsedIds []string + json.Unmarshal([]byte(txIdStrings), &parsedIds) + + parsedIds = append(parsedIds, "invalid-tx-id") + updatedTxIdStrings, _ := json.Marshal(parsedIds) + + // discard + discardResultsString := DiscardTransactions(C.CString(string(updatedTxIdStrings))) + discardResultsStruct := geth.DiscardTransactionsResult{} + json.Unmarshal([]byte(C.GoString(discardResultsString)), &discardResultsStruct) + discardResults := discardResultsStruct.Results + + if len(discardResults) != 1 || discardResults["invalid-tx-id"].Error != "transaction hash not found" { + t.Errorf("cannot discard txs: %v", discardResults) + return + } + + // try completing discarded transaction + completeResultsString := CompleteTransactions(C.CString(string(updatedTxIdStrings)), C.CString(testAddressPassword)) + completeResultsStruct := geth.CompleteTransactionsResult{} + json.Unmarshal([]byte(C.GoString(completeResultsString)), &completeResultsStruct) + completeResults := completeResultsStruct.Results + + if len(completeResults) != (testTxCount + 1) { + t.Error("unexpected number of errors (call to CompleteTransaction should not succeed)") + } + for txId, txResult := range completeResults { + if txId != txResult.Id { + t.Errorf("tx id not set in result: expected id is %s", txId) + return + } + if txResult.Error != "transaction hash not found" { + t.Errorf("invalid error for %s", txResult.Hash) + return + } + if txResult.Hash != "0x0000000000000000000000000000000000000000000000000000000000000000" { + t.Errorf("invalid hash (expected zero): %s", txResult.Hash) + return + } + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + for _, txId := range parsedIds { + if backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txId) + return + } + } + } + go func() { + var txIdStrings []string + for i := 0; i < testTxCount; i++ { + txIdStrings = append(txIdStrings, <-txIds) + } + + txIdJSON, _ := json.Marshal(txIdStrings) + discardTxs(string(txIdJSON)) + }() + + // send multiple transactions + for i := 0; i < testTxCount; i++ { + go sendTx() + } + + select { + case <-allTestTxDiscarded: + // pass + case <-time.After(20 * time.Second): + t.Error("test timed out") + return false + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return false + } + + return true +} + +func testJailInit(t *testing.T) bool { + initCode := ` + var _status_catalog = { + foo: 'bar' + }; + ` + InitJail(C.CString(initCode)) + + extraCode := ` + var extraFunc = function (x) { + return x * x; + }; + ` + rawResponse := Parse(C.CString("CHAT_ID_INIT_TEST"), C.CString(extraCode)) + parsedResponse := C.GoString(rawResponse) + + expectedResponse := `{"result": {"foo":"bar"}}` + + if !reflect.DeepEqual(expectedResponse, parsedResponse) { + t.Error("expected output not returned from jail.Parse()") + return false + } + + t.Logf("jail inited and parsed: %s", parsedResponse) + + return true +} + +func testJailFunctionCall(t *testing.T) bool { + InitJail(C.CString("")) + + // load Status JS and add test command to it + statusJS := geth.LoadFromFile(testStatusJsFile) + `; + _status_catalog.commands["testCommand"] = function (params) { + return params.val * params.val; + };` + Parse(C.CString("CHAT_ID_CALL_TEST"), C.CString(statusJS)) + + // call with wrong chat id + rawResponse := Call(C.CString("CHAT_IDNON_EXISTENT"), C.CString(""), C.CString("")) + parsedResponse := C.GoString(rawResponse) + expectedError := `{"error":"Cell[CHAT_IDNON_EXISTENT] doesn't exist."}` + if parsedResponse != expectedError { + t.Errorf("expected error is not returned: expected %s, got %s", expectedError, parsedResponse) + return false + } + + // call extraFunc() + rawResponse = Call(C.CString("CHAT_ID_CALL_TEST"), C.CString(`["commands", "testCommand"]`), C.CString(`{"val": 12}`)) + parsedResponse = C.GoString(rawResponse) + expectedResponse := `{"result": 144}` + if parsedResponse != expectedResponse { + t.Errorf("expected response is not returned: expected %s, got %s", expectedResponse, parsedResponse) + return false + } + + t.Logf("jailed method called: %s", parsedResponse) + + return true +} + +func startTestNode(t *testing.T) <-chan struct{} { + syncRequired := false + if _, err := os.Stat(filepath.Join(testDataDir, "testnet")); os.IsNotExist(err) { + syncRequired = true + } + + waitForNodeStart := make(chan struct{}, 1) + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + t.Log(jsonEvent) + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + } + if envelope.Type == geth.EventNodeStarted { + // 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 { + AddPeer(C.CString(enode)) + } + + // sync + if syncRequired { + t.Logf("Sync is required, it will take %d seconds", testNodeSyncSeconds) + time.Sleep(testNodeSyncSeconds * time.Second) // LES syncs headers, so that we are up do date when it is done + } else { + time.Sleep(5 * time.Second) + } + + // now we can proceed with tests + waitForNodeStart <- struct{}{} + } + }) + + go func() { + response := StartNode(C.CString(testDataDir)) + err := geth.JSONError{} + + json.Unmarshal([]byte(C.GoString(response)), &err) + if err.Error != "" { + t.Error("cannot start node") + } + }() + + return waitForNodeStart +} diff --git a/data/morden-test-account.pk b/data/morden-test-account.pk new file mode 100644 index 000000000..7ea0a9af4 --- /dev/null +++ b/data/morden-test-account.pk @@ -0,0 +1 @@ +{"address":"89b50b2b26947ccad43accaef76c21d175ad85f4","crypto":{"cipher":"aes-128-ctr","ciphertext":"cc8f600a59f8c5ac3d6ab849722a6602f61de0adc5c9617a1cd014d3d9638a95","cipherparams":{"iv":"836bb6b5df64c29a84f95e7628510e71"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4cc1f86eaa707ee37dc3b686712f09be49b7175a2046c63cfd1d7a3e7ebee8ab"},"mac":"164f2e53c67b5c85267b79e4fc8b2f9117a66b5e0546933403d85d52fffa1f52"},"id":"3ade037d-722b-467a-ad8e-2bcae28b9642","version":3,"whisperenabled":true} diff --git a/data/static-nodes.json b/data/static-nodes.json index 0f6b4b642..00f81dbb2 100644 --- a/data/static-nodes.json +++ b/data/static-nodes.json @@ -1,3 +1,6 @@ [ - "enode://4e2bb6b09aa34375ae2df23fa063edfe7aaec952dba972449158ae0980a4abd375aca3c06a519d4f562ff298565afd288a0ed165944974b2557e6ff2c31424de@138.68.73.175:30303" + "enode://e19d89e6faf2772e2f250e9625478ee7f313fcc0bb5e9310d5d407371496d9d7d73ccecd9f226cc2a8be34484525f72ba9db9d26f0222f4efc3c6d9d995ee224@198.199.105.122:30303", + "enode://5f23bf4913dd005ce945648cb12d3ef970069818d8563a3fe054e5e1dc3898b9cb83e0af1f51b2dce75eaffc76e93f996caf538e21c5b64db5fa324958d59630@95.85.40.211:30303", + "enode://b9de2532421f15ac55da9d9a7cddc0dc08b0d646d631fd7ab2a170bd2163fb86b095dd8bde66b857592812f7cd9539f2919b6c64bc1a784a1d1c6ec8137681ed@188.166.229.119:30303", + "enode://1ad53266faaa9258ae71eef4d162022ba0d39498e1a3488e6c65fd86e0fb528e2aa68ad0e199da69fd39f4a3a38e9e8e95ac53ba5cc7676dfeaacf5fd6c0ad27@139.59.212.114:30303" ] diff --git a/data/test-account.pk b/data/test-account.pk index 7ea0a9af4..20d5bf033 100644 --- a/data/test-account.pk +++ b/data/test-account.pk @@ -1 +1 @@ -{"address":"89b50b2b26947ccad43accaef76c21d175ad85f4","crypto":{"cipher":"aes-128-ctr","ciphertext":"cc8f600a59f8c5ac3d6ab849722a6602f61de0adc5c9617a1cd014d3d9638a95","cipherparams":{"iv":"836bb6b5df64c29a84f95e7628510e71"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4cc1f86eaa707ee37dc3b686712f09be49b7175a2046c63cfd1d7a3e7ebee8ab"},"mac":"164f2e53c67b5c85267b79e4fc8b2f9117a66b5e0546933403d85d52fffa1f52"},"id":"3ade037d-722b-467a-ad8e-2bcae28b9642","version":3,"whisperenabled":true} +{"address":"adaf150b905cf5e6a778e553e15a139b6618bbb7","crypto":{"cipher":"aes-128-ctr","ciphertext":"e6b4a87b62eca6b654b45ec806a19b5a8fa2ee1b39d2ca17406f11fb81428455","cipherparams":{"iv":"863515dcd60f8b9fec7d5ba59f2a895e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"83e4d852dbd1d4bb93c1989736dd4cf4443b0842978a6eec3069a13406cb3605"},"mac":"7d00f96aeb1684717248fbcbdfec2d6da69dadea60e65239d20642e39097b923"},"id":"bc042f57-ad91-4459-a056-607ce80a760d","version":3,"whisperenabled":true,"extendedkey":{"cipher":"","ciphertext":"","cipherparams":{"iv":""},"kdf":"","kdfparams":null,"mac":""},"subaccountindex":0} \ No newline at end of file diff --git a/geth/accounts_test.go b/geth/accounts_test.go index 71c32638e..4f269d60c 100644 --- a/geth/accounts_test.go +++ b/geth/accounts_test.go @@ -2,15 +2,12 @@ package geth_test import ( "errors" - "fmt" "reflect" "testing" "github.com/ethereum/go-ethereum/cmd/utils" "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" "github.com/status-im/status-go/geth" ) @@ -145,7 +142,7 @@ func TestCreateChildAccount(t *testing.T) { t.Errorf("could not create account: %v", err) return } - glog.V(logger.Info).Infof("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) + t.Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) account, err := utils.MakeAddress(accountManager, address) if err != nil { @@ -228,7 +225,7 @@ func TestRecoverAccount(t *testing.T) { t.Errorf("could not create account: %v", err) return } - glog.V(logger.Info).Infof("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) + t.Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) // try recovering using password + mnemonic addressCheck, pubKeyCheck, err := geth.RecoverAccount(newAccountPassword, mnemonic) @@ -326,15 +323,14 @@ func TestAccountSelect(t *testing.T) { t.Errorf("could not create account: %v", err) return } - glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address1, pubKey1) + t.Logf("Account created: {address: %s, key: %s}", address1, pubKey1) address2, pubKey2, _, err := geth.CreateAccount(newAccountPassword) if err != nil { - fmt.Println(err.Error()) t.Error("Test failed: could not create account") return } - glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address2, pubKey2) + t.Logf("Account created: {address: %s, key: %s}", address2, pubKey2) // make sure that identity is not (yet injected) if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) { diff --git a/geth/ios.go b/geth/ios.go new file mode 100644 index 000000000..c8bd4f5e6 --- /dev/null +++ b/geth/ios.go @@ -0,0 +1,12 @@ +// +build darwin,cgo + +package geth + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation +#include +#include +extern bool StatusServiceSignalEvent( const char *jsonEvent ); +*/ +import "C" diff --git a/geth/node.go b/geth/node.go index ee47354a6..1dcd090cd 100644 --- a/geth/node.go +++ b/geth/node.go @@ -11,6 +11,8 @@ import ( "errors" "flag" "fmt" + "io/ioutil" + "path/filepath" "runtime" "sync" @@ -23,10 +25,9 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/release" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "gopkg.in/urfave/cli.v1" ) @@ -37,9 +38,8 @@ const ( versionPatch = 0 // Patch version component of the current release versionMeta = "unstable" // Version metadata to append to the version string - versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle - - RPCPort = 8545 // RPC port (replaced in unit tests) + RPCPort = 8545 // RPC port (replaced in unit tests) + NetworkPort = 30303 EventNodeStarted = "node.started" ) @@ -51,6 +51,7 @@ var ( 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") ) @@ -61,24 +62,28 @@ type SelectedExtKey struct { } 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 - SelectedAccount *SelectedExtKey // account that was processed during the last call to SelectAccount() - whisperService *whisper.Whisper // Whisper service - client *rpc.ClientRestartWrapper // RPC client - nodeStarted chan struct{} // channel to wait for node to start + 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 } var ( + UseTestnet = "true" // can be overridden via -ldflags '-X geth.UseTestnet' nodeManagerInstance *NodeManager createOnce sync.Once ) func NewNodeManager(datadir string, rpcport int) *NodeManager { createOnce.Do(func() { - nodeManagerInstance = &NodeManager{} + nodeManagerInstance = &NodeManager{ + jailedRequestQueue: NewJailedRequestsQueue(), + } nodeManagerInstance.MakeNode(datadir, rpcport) }) @@ -111,7 +116,9 @@ func (m *NodeManager) MakeNode(datadir string, rpcport int) *node.Node { 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") - set.Bool("testnet", true, "light test network") + 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") @@ -120,24 +127,14 @@ func (m *NodeManager) MakeNode(datadir string, rpcport int) *node.Node { 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) - // Construct the textual version string from the individual components - vString := fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch) - - // Construct the version release oracle configuration - var rConfig release.Config - rConfig.Oracle = common.HexToAddress(versionOracle) - - rConfig.Major = uint32(versionMajor) - rConfig.Minor = uint32(versionMinor) - rConfig.Patch = uint32(versionPatch) - utils.DebugSetup(m.ctx) // create node and start requested protocols - m.currentNode = utils.MakeNode(m.ctx, clientIdentifier, vString) - utils.RegisterEthService(m.ctx, m.currentNode, rConfig, makeDefaultExtra()) + 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) @@ -170,14 +167,16 @@ func (m *NodeManager) RunNode() { // setup handlers m.lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest) m.lightEthereum.StatusBackend.SetAccountsFilterHandler(onAccountsListRequest) + m.lightEthereum.StatusBackend.SetTransactionReturnHandler(onSendTransactionReturn) - m.client = rpc.NewClientRestartWrapper(func() *rpc.Client { - client, err := m.currentNode.Attach() - if err != nil { - return nil - } - return client - }) + 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() @@ -270,22 +269,38 @@ func (m *NodeManager) LightEthereumService() (*les.LightEthereum, error) { return m.lightEthereum, nil } -func (m *NodeManager) HasClientRestartWrapper() bool { +func (m *NodeManager) HasRPCClient() bool { return m.client != nil } -func (m *NodeManager) ClientRestartWrapper() (*rpc.ClientRestartWrapper, error) { +func (m *NodeManager) RPCClient() (*rpc.Client, error) { if m == nil || !m.HasNode() { return nil, ErrInvalidGethNode } - if !m.HasClientRestartWrapper() { + if !m.HasRPCClient() { return nil, ErrInvalidClient } 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 +} + func makeDefaultExtra() []byte { var clientInfo = struct { Version uint @@ -306,3 +321,19 @@ 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 + } + + for _, enode := range enodes { + m.AddPeer(enode) + } +} diff --git a/geth/node_test.go b/geth/node_test.go index 5ef437994..0568c39bb 100644 --- a/geth/node_test.go +++ b/geth/node_test.go @@ -2,17 +2,18 @@ package geth_test import ( "os" + "path/filepath" "testing" "time" - "path/filepath" "github.com/status-im/status-go/geth" ) const ( - testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4" - testAddressPassword = "asdf" + testAddress = "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" + testAddressPassword = "asdfasdf" newAccountPassword = "badpassword" + testAddress1 = "0xadd4d1d02e71c7360c53296968e59d57fd15e2ba" whisperMessage1 = "test message 1 (K1 -> K1)" whisperMessage2 = "test message 2 (K1 -> '')" @@ -28,7 +29,7 @@ func TestMain(m *testing.M) { } // make sure you panic if node start signal is not received signalRecieved := make(chan struct{}, 1) - abortPanic := make(chan bool, 1) + abortPanic := make(chan struct{}, 1) if syncRequired { geth.PanicAfter(geth.TestNodeSyncSeconds*time.Second, abortPanic, "TestNodeSetup") } else { @@ -44,11 +45,10 @@ func TestMain(m *testing.M) { err := geth.PrepareTestNode() if err != nil { panic(err) - return } <-signalRecieved // block and wait for either panic or successful signal - abortPanic <- true + abortPanic <- struct{}{} os.Exit(m.Run()) } diff --git a/geth/signals.c b/geth/signals.c index cb9a7c7e9..f8b12cad8 100644 --- a/geth/signals.c +++ b/geth/signals.c @@ -5,10 +5,43 @@ #include #include +#include +#include +#include +#include +static id statusServiceClassRef = nil; +static SEL statusServiceSelector = nil; + +static bool initLibrary() { + if (statusServiceClassRef == nil) { + statusServiceClassRef = objc_getClass("Status"); + if (statusServiceClassRef == nil) return false; + } + + if (statusServiceSelector == nil) { + statusServiceSelector = sel_getUid("signalEvent:"); + if (statusServiceSelector == nil) return false; + } + + return true; +} + + +/*! + * @brief Calls static method signalEvent of class GethService. + * + * @param jsonEvent - UTF8 string + * + * @note Definition of signalEvent method. + * + (void)signalEvent:(const char *)json + */ bool StatusServiceSignalEvent(const char *jsonEvent) { - // code for sending JSON notification up to iOS app - return true; + if (!initLibrary()) return false; + + objc_msgSend(statusServiceClassRef, statusServiceSelector, jsonEvent); + + return true; } #elif defined(ANDROID_DEPLOYMENT) @@ -94,7 +127,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { bool JniLibraryInit(JNIEnv *env) { int i; - JavaClassPtr_StatusService = (*env)->FindClass(env, "com/statusim/module/StatusService"); + JavaClassPtr_StatusService = (*env)->FindClass(env, "im/status/ethereum/module/StatusService"); if (JavaClassPtr_StatusService == NULL) return false; JavaClassPtr_StatusService = (jclass)(*env)->NewGlobalRef(env, JavaClassPtr_StatusService); @@ -132,7 +165,7 @@ bool JniLibraryInit(JNIEnv *env) { } /*! - * @brief Calls static method signalEvent of class com.statusim.module.StatusService. + * @brief Calls static method signalEvent of class im.status.ethereum.module.StatusService. * * @param jsonEvent - UTF8 string */ diff --git a/geth/txqueue.go b/geth/txqueue.go index 090ba2816..72a93caae 100644 --- a/geth/txqueue.go +++ b/geth/txqueue.go @@ -8,22 +8,39 @@ extern bool StatusServiceSignalEvent( const char *jsonEvent ); import "C" import ( + "context" "encoding/json" + "math/big" + "strconv" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/les/status" + "github.com/ethereum/go-ethereum/rpc" + "github.com/robertkrimen/otto" ) const ( EventTransactionQueued = "transaction.queued" + EventTransactionFailed = "transaction.failed" + SendTransactionRequest = "eth_sendTransaction" + MessageIdKey = "message_id" + + // tx error codes + SendTransactionNoErrorCode = "0" + SendTransactionDefaultErrorCode = "1" + SendTransactionPasswordErrorCode = "2" + SendTransactionTimeoutErrorCode = "3" + SendTransactionDiscardedErrorCode = "4" ) func onSendTransactionRequest(queuedTx status.QueuedTx) { event := GethEvent{ Type: EventTransactionQueued, Event: SendTransactionEvent{ - Id: string(queuedTx.Id), - Args: queuedTx.Args, + Id: string(queuedTx.Id), + Args: queuedTx.Args, + MessageId: messageIdFromContext(queuedTx.Context), }, } @@ -31,6 +48,49 @@ func onSendTransactionRequest(queuedTx status.QueuedTx) { C.StatusServiceSignalEvent(C.CString(string(body))) } +func onSendTransactionReturn(queuedTx *status.QueuedTx, err error) { + if err == nil { + return + } + + // discard notifications with empty tx + if queuedTx == nil { + return + } + + // error occurred, signal up to application + event := GethEvent{ + Type: EventTransactionFailed, + Event: ReturnSendTransactionEvent{ + Id: string(queuedTx.Id), + Args: queuedTx.Args, + MessageId: messageIdFromContext(queuedTx.Context), + ErrorMessage: err.Error(), + ErrorCode: sendTransactionErrorCode(err), + }, + } + + body, _ := json.Marshal(&event) + C.StatusServiceSignalEvent(C.CString(string(body))) +} + +func sendTransactionErrorCode(err error) string { + if err == nil { + return SendTransactionNoErrorCode + } + + switch err { + case accounts.ErrDecrypt: + return SendTransactionPasswordErrorCode + case status.ErrQueuedTxTimedOut: + return SendTransactionTimeoutErrorCode + case status.ErrQueuedTxDiscarded: + return SendTransactionDiscardedErrorCode + default: + return SendTransactionDefaultErrorCode + } +} + func CompleteTransaction(id, password string) (common.Hash, error) { lightEthereum, err := GetNodeManager().LightEthereumService() if err != nil { @@ -41,3 +101,190 @@ func CompleteTransaction(id, password string) (common.Hash, error) { return backend.CompleteQueuedTransaction(status.QueuedTxId(id), password) } + +func CompleteTransactions(ids, password string) map[string]RawCompleteTransactionResult { + results := make(map[string]RawCompleteTransactionResult) + + parsedIds, err := parseJSONArray(ids) + if err != nil { + results["none"] = RawCompleteTransactionResult{ + Error: err, + } + return results + } + + for _, txId := range parsedIds { + txHash, txErr := CompleteTransaction(txId, password) + results[txId] = RawCompleteTransactionResult{ + Hash: txHash, + Error: txErr, + } + } + + return results +} + +func DiscardTransaction(id string) error { + lightEthereum, err := GetNodeManager().LightEthereumService() + if err != nil { + return err + } + + backend := lightEthereum.StatusBackend + + return backend.DiscardQueuedTransaction(status.QueuedTxId(id)) +} + +func DiscardTransactions(ids string) map[string]RawDiscardTransactionResult { + var parsedIds []string + results := make(map[string]RawDiscardTransactionResult) + + parsedIds, err := parseJSONArray(ids) + if err != nil { + results["none"] = RawDiscardTransactionResult{ + Error: err, + } + return results + } + + for _, txId := range parsedIds { + err := DiscardTransaction(txId) + if err != nil { + results[txId] = RawDiscardTransactionResult{ + Error: err, + } + } + } + + return results +} + +func messageIdFromContext(ctx context.Context) string { + if ctx == nil { + return "" + } + if messageId, ok := ctx.Value(MessageIdKey).(string); ok { + return messageId + } + + return "" +} + +type JailedRequestQueue struct{} + +func NewJailedRequestsQueue() *JailedRequestQueue { + return &JailedRequestQueue{} +} + +func (q *JailedRequestQueue) PreProcessRequest(vm *otto.Otto, req RPCCall) (string, error) { + messageId := currentMessageId(vm.Context()) + + return messageId, nil +} + +func (q *JailedRequestQueue) PostProcessRequest(vm *otto.Otto, req RPCCall, messageId string) { + if len(messageId) > 0 { + vm.Call("addContext", nil, messageId, MessageIdKey, messageId) + } + + // set extra markers for queued transaction requests + if req.Method == SendTransactionRequest { + vm.Call("addContext", nil, messageId, SendTransactionRequest, true) + } +} + +func (q *JailedRequestQueue) ProcessSendTransactionRequest(vm *otto.Otto, req RPCCall) (common.Hash, error) { + // obtain status backend from LES service + lightEthereum, err := GetNodeManager().LightEthereumService() + if err != nil { + return common.Hash{}, err + } + backend := lightEthereum.StatusBackend + + messageId, err := q.PreProcessRequest(vm, req) + if err != nil { + return common.Hash{}, err + } + // onSendTransactionRequest() will use context to obtain and release ticket + ctx := context.Background() + ctx = context.WithValue(ctx, MessageIdKey, messageId) + + // this call blocks, up until Complete Transaction is called + txHash, err := backend.SendTransaction(ctx, sendTxArgsFromRPCCall(req)) + if err != nil { + return common.Hash{}, err + } + + // invoke post processing + q.PostProcessRequest(vm, req, messageId) + + return txHash, nil +} + +// currentMessageId looks for `status.message_id` variable in current JS context +func currentMessageId(ctx otto.Context) string { + if statusObj, ok := ctx.Symbols["status"]; ok { + messageId, err := statusObj.Object().Get("message_id") + if err != nil { + return "" + } + if messageId, err := messageId.ToString(); err == nil { + return messageId + } + } + + return "" +} + +func sendTxArgsFromRPCCall(req RPCCall) status.SendTxArgs { + if req.Method != SendTransactionRequest { // no need to persist extra state for other requests + return status.SendTxArgs{} + } + + params, ok := req.Params[0].(map[string]interface{}) + if !ok { + return status.SendTxArgs{} + } + + from, ok := params["from"].(string) + if !ok { + from = "" + } + + to, ok := params["to"].(string) + if !ok { + to = "" + } + + param, ok := params["value"].(string) + if !ok { + param = "0x0" + } + value, err := strconv.ParseInt(param, 0, 64) + if err != nil { + return status.SendTxArgs{} + } + + data, ok := params["data"].(string) + if !ok { + data = "" + } + + toAddress := common.HexToAddress(to) + return status.SendTxArgs{ + From: common.HexToAddress(from), + To: &toAddress, + Value: rpc.NewHexNumber(big.NewInt(value)), + Data: data, + } +} + +func parseJSONArray(items string) ([]string, error) { + var parsedItems []string + err := json.Unmarshal([]byte(items), &parsedItems) + if err != nil { + return nil, err + } + + return parsedItems, nil +} diff --git a/geth/txqueue_test.go b/geth/txqueue_test.go index 4f3d229fd..b49c21eab 100644 --- a/geth/txqueue_test.go +++ b/geth/txqueue_test.go @@ -7,11 +7,8 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/les/status" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rpc" "github.com/status-im/status-go/geth" ) @@ -23,20 +20,7 @@ func TestQueuedTransactions(t *testing.T) { return } - accountManager, err := geth.GetNodeManager().AccountManager() - if err != nil { - t.Errorf(err.Error()) - return - } - - // create an account - address, _, _, err := geth.CreateAccount(newAccountPassword) - if err != nil { - t.Errorf("could not create account: %v", err) - return - } - - // test transaction queueing + // obtain reference to status backend lightEthereum, err := geth.GetNodeManager().LightEthereumService() if err != nil { t.Errorf("Test failed: LES service is not running: %v", err) @@ -45,7 +29,7 @@ func TestQueuedTransactions(t *testing.T) { backend := lightEthereum.StatusBackend // make sure you panic if transaction complete doesn't return - completeQueuedTransaction := make(chan bool, 1) + completeQueuedTransaction := make(chan struct{}, 1) geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions") // replace transaction notification handler @@ -58,7 +42,7 @@ func TestQueuedTransactions(t *testing.T) { } if envelope.Type == geth.EventTransactionQueued { event := envelope.Event.(map[string]interface{}) - glog.V(logger.Info).Infof("Transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string)) + t.Logf("transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string)) time.Sleep(5 * time.Second) if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != nil { @@ -66,34 +50,15 @@ func TestQueuedTransactions(t *testing.T) { return } - glog.V(logger.Info).Infof("Transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex()) - completeQueuedTransaction <- true // so that timeout is aborted + t.Logf("transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex()) + completeQueuedTransaction <- struct{}{} // so that timeout is aborted } }) - // try completing non-existing transaction - if _, err := geth.CompleteTransaction("some-bad-transaction-id", testAddressPassword); err == nil { - t.Error("error expected and not recieved") - return - } - - // send normal transaction - from, err := utils.MakeAddress(accountManager, testAddress) - if err != nil { - t.Errorf("could not retrieve account from address: %v", err) - return - } - - to, err := utils.MakeAddress(accountManager, address) - if err != nil { - t.Errorf("could not retrieve account from address: %v", err) - return - } - // this call blocks, up until Complete Transaction is called txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ - From: from.Address, - To: &to.Address, + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), Value: rpc.NewHexNumber(big.NewInt(1000000000000)), }) if err != nil { @@ -101,7 +66,7 @@ func TestQueuedTransactions(t *testing.T) { } if !reflect.DeepEqual(txHash, txHashCheck) { - t.Error("Transaction hash returned from SendTransaction is invalid") + t.Errorf("Transaction hash returned from SendTransaction is invalid: expected %s, got %s", txHashCheck, txHash) return } @@ -112,11 +77,623 @@ func TestQueuedTransactions(t *testing.T) { return } - // now test eviction queue + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return + } +} + +func TestDoubleCompleteQueuedTransactions(t *testing.T) { + err := geth.PrepareTestNode() + if err != nil { + t.Error(err) + return + } + + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return + } + backend := lightEthereum.StatusBackend + + // make sure you panic if transaction complete doesn't return + completeQueuedTransaction := make(chan struct{}, 1) + geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions") + + // replace transaction notification handler + var txId string + txFailedEventCalled := false + txHash := common.Hash{} + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + txId = event["id"].(string) + t.Logf("transaction queued (will be failed and completed on the second call): {id: %s}\n", txId) + + // try with wrong password + // make sure that tx is NOT removed from the queue (by re-trying with the correct password) + if _, err = geth.CompleteTransaction(txId, testAddressPassword+"wrong"); err == nil { + t.Error("expects wrong password error, but call succeeded") + return + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + if txCount := backend.TransactionQueue().Count(); txCount != 1 { + t.Errorf("txqueue cannot be empty, as tx has failed: expected = 1, got = %d", txCount) + return + } + + // now try to complete transaction, but with the correct password + t.Log("allow 5 seconds before sedning the second CompleteTransaction") + time.Sleep(5 * time.Second) + if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != nil { + t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err) + return + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + if txCount := backend.TransactionQueue().Count(); txCount != 0 { + t.Errorf("txqueue must be empty, as tx has completed: expected = 0, got = %d", txCount) + return + } + + t.Logf("transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex()) + completeQueuedTransaction <- struct{}{} // so that timeout is aborted + } + + if envelope.Type == geth.EventTransactionFailed { + event := envelope.Event.(map[string]interface{}) + t.Logf("transaction return event received: {id: %s}\n", event["id"].(string)) + + receivedErrMessage := event["error_message"].(string) + expectedErrMessage := "could not decrypt key with given passphrase" + if receivedErrMessage != expectedErrMessage { + t.Errorf("unexpected error message received: got %v", receivedErrMessage) + return + } + + receivedErrCode := event["error_code"].(string) + if receivedErrCode != geth.SendTransactionPasswordErrorCode { + t.Errorf("unexpected error code received: got %v", receivedErrCode) + return + } + + txFailedEventCalled = true + } + }) + + // this call blocks, and should return on *second* attempt to CompleteTransaction (w/ the correct password) + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != nil { + t.Errorf("cannot send transaction: %v", err) + return + } + + if !reflect.DeepEqual(txHash, txHashCheck) { + t.Errorf("tx hash returned from SendTransaction is invalid: expected %s, got %s", txHashCheck, txHash) + return + } + + if reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("transaction was never queued or completed") + return + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return + } + + if !txFailedEventCalled { + t.Error("expected tx failure signal is not received") + return + } + + t.Log("sleep extra time, to allow sync") + time.Sleep(5 * time.Second) +} + +func TestDiscardQueuedTransactions(t *testing.T) { + err := geth.PrepareTestNode() + if err != nil { + t.Error(err) + return + } + + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return + } + backend := lightEthereum.StatusBackend + + // reset queue + backend.TransactionQueue().Reset() + + // make sure you panic if transaction complete doesn't return + completeQueuedTransaction := make(chan struct{}, 1) + geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestDiscardQueuedTransactions") + + // replace transaction notification handler + var txId string + txFailedEventCalled := false + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + txId = event["id"].(string) + t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txId) + + if !backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should still have test tx: %s", txId) + return + } + + // discard + err := geth.DiscardTransaction(txId) + if err != nil { + t.Errorf("cannot discard tx: %v", err) + return + } + + // try completing discarded transaction + _, err = geth.CompleteTransaction(txId, testAddressPassword) + if err.Error() != "transaction hash not found" { + t.Error("expects tx not found, but call to CompleteTransaction succeeded") + return + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + if backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txId) + return + } + + completeQueuedTransaction <- struct{}{} // so that timeout is aborted + } + + if envelope.Type == geth.EventTransactionFailed { + event := envelope.Event.(map[string]interface{}) + t.Logf("transaction return event received: {id: %s}\n", event["id"].(string)) + + receivedErrMessage := event["error_message"].(string) + expectedErrMessage := status.ErrQueuedTxDiscarded.Error() + if receivedErrMessage != expectedErrMessage { + t.Errorf("unexpected error message received: got %v", receivedErrMessage) + return + } + + receivedErrCode := event["error_code"].(string) + if receivedErrCode != geth.SendTransactionDiscardedErrorCode { + t.Errorf("unexpected error code received: got %v", receivedErrCode) + return + } + + txFailedEventCalled = true + } + }) + + // this call blocks, and should return when DiscardQueuedTransaction() is called + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != status.ErrQueuedTxDiscarded { + t.Errorf("expected error not thrown: %v", err) + return + } + + if !reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("transaction returned hash, while it shouldn't") + return + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return + } + + if !txFailedEventCalled { + t.Error("expected tx failure signal is not received") + return + } + + t.Log("sleep extra time, to allow sync") + time.Sleep(5 * time.Second) +} + +func TestCompleteMultipleQueuedTransactions(t *testing.T) { + err := geth.PrepareTestNode() + if err != nil { + t.Error(err) + return + } + + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return + } + backend := lightEthereum.StatusBackend + + // reset queue + backend.TransactionQueue().Reset() + + // make sure you panic if transaction complete doesn't return + testTxCount := 3 + txIds := make(chan string, testTxCount) + allTestTxCompleted := make(chan struct{}, 1) + + // replace transaction notification handler + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var txId string + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + txId = event["id"].(string) + t.Logf("transaction queued (will be completed in a single call, once aggregated): {id: %s}\n", txId) + + txIds <- txId + } + }) + + // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called + sendTx := func() { + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != nil { + t.Errorf("unexpected error thrown: %v", err) + return + } + + if reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("transaction returned empty hash") + return + } + } + + // wait for transactions, and complete them in a single call + completeTxs := func(txIdStrings string) { + var parsedIds []string + json.Unmarshal([]byte(txIdStrings), &parsedIds) + + parsedIds = append(parsedIds, "invalid-tx-id") + updatedTxIdStrings, _ := json.Marshal(parsedIds) + + // complete + results := geth.CompleteTransactions(string(updatedTxIdStrings), testAddressPassword) + if len(results) != (testTxCount+1) || results["invalid-tx-id"].Error.Error() != "transaction hash not found" { + t.Errorf("cannot complete txs: %v", results) + return + } + for txId, txResult := range results { + if txResult.Error != nil && txId != "invalid-tx-id" { + t.Errorf("invalid error for %s", txId) + return + } + if txResult.Hash.Hex() == "0x0000000000000000000000000000000000000000000000000000000000000000" && txId != "invalid-tx-id" { + t.Errorf("invalid hash (expected non empty hash): %s", txId) + return + } + + if txResult.Hash.Hex() != "0x0000000000000000000000000000000000000000000000000000000000000000" { + t.Logf("transaction complete: https://testnet.etherscan.io/tx/%s", txResult.Hash.Hex()) + } + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + for _, txId := range parsedIds { + if backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should not have test tx at this point (it should be completed): %s", txId) + return + } + } + } + go func() { + var txIdStrings []string + for i := 0; i < testTxCount; i++ { + txIdStrings = append(txIdStrings, <-txIds) + } + + txIdJSON, _ := json.Marshal(txIdStrings) + completeTxs(string(txIdJSON)) + allTestTxCompleted <- struct{}{} + }() + + // send multiple transactions + for i := 0; i < testTxCount; i++ { + go sendTx() + } + + select { + case <-allTestTxCompleted: + // pass + case <-time.After(20 * time.Second): + t.Error("test timed out") + return + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return + } +} + +func TestDiscardMultipleQueuedTransactions(t *testing.T) { + err := geth.PrepareTestNode() + if err != nil { + t.Error(err) + return + } + + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return + } + backend := lightEthereum.StatusBackend + + // reset queue + backend.TransactionQueue().Reset() + + // make sure you panic if transaction complete doesn't return + testTxCount := 3 + txIds := make(chan string, testTxCount) + allTestTxDiscarded := make(chan struct{}, 1) + + // replace transaction notification handler + txFailedEventCallCount := 0 + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var txId string + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + txId = event["id"].(string) + t.Logf("transaction queued (will be discarded soon): {id: %s}\n", txId) + + if !backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should still have test tx: %s", txId) + return + } + + txIds <- txId + } + + if envelope.Type == geth.EventTransactionFailed { + event := envelope.Event.(map[string]interface{}) + t.Logf("transaction return event received: {id: %s}\n", event["id"].(string)) + + receivedErrMessage := event["error_message"].(string) + expectedErrMessage := status.ErrQueuedTxDiscarded.Error() + if receivedErrMessage != expectedErrMessage { + t.Errorf("unexpected error message received: got %v", receivedErrMessage) + return + } + + receivedErrCode := event["error_code"].(string) + if receivedErrCode != geth.SendTransactionDiscardedErrorCode { + t.Errorf("unexpected error code received: got %v", receivedErrCode) + return + } + + txFailedEventCallCount++ + if txFailedEventCallCount == testTxCount { + allTestTxDiscarded <- struct{}{} + } + } + }) + + // this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called + sendTx := func() { + txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{ + From: geth.FromAddress(testAddress), + To: geth.ToAddress(testAddress1), + Value: rpc.NewHexNumber(big.NewInt(1000000000000)), + }) + if err != status.ErrQueuedTxDiscarded { + t.Errorf("expected error not thrown: %v", err) + return + } + + if !reflect.DeepEqual(txHashCheck, common.Hash{}) { + t.Error("transaction returned hash, while it shouldn't") + return + } + } + + // wait for transactions, and discard immediately + discardTxs := func(txIdStrings string) { + var parsedIds []string + json.Unmarshal([]byte(txIdStrings), &parsedIds) + + parsedIds = append(parsedIds, "invalid-tx-id") + updatedTxIdStrings, _ := json.Marshal(parsedIds) + + // discard + discardResults := geth.DiscardTransactions(string(updatedTxIdStrings)) + if len(discardResults) != 1 || discardResults["invalid-tx-id"].Error.Error() != "transaction hash not found" { + t.Errorf("cannot discard txs: %v", discardResults) + return + } + + // try completing discarded transaction + completeResults := geth.CompleteTransactions(string(updatedTxIdStrings), testAddressPassword) + if len(completeResults) != (testTxCount + 1) { + t.Error("unexpected number of errors (call to CompleteTransaction should not succeed)") + } + for _, txResult := range completeResults { + if txResult.Error.Error() != "transaction hash not found" { + t.Errorf("invalid error for %s", txResult.Hash.Hex()) + return + } + if txResult.Hash.Hex() != "0x0000000000000000000000000000000000000000000000000000000000000000" { + t.Errorf("invalid hash (expected zero): %s", txResult.Hash.Hex()) + return + } + } + + time.Sleep(1 * time.Second) // make sure that tx complete signal propagates + for _, txId := range parsedIds { + if backend.TransactionQueue().Has(status.QueuedTxId(txId)) { + t.Errorf("txqueue should not have test tx at this point (it should be discarded): %s", txId) + return + } + } + } + go func() { + var txIdStrings []string + for i := 0; i < testTxCount; i++ { + txIdStrings = append(txIdStrings, <-txIds) + } + + txIdJSON, _ := json.Marshal(txIdStrings) + discardTxs(string(txIdJSON)) + }() + + // send multiple transactions + for i := 0; i < testTxCount; i++ { + go sendTx() + } + + select { + case <-allTestTxDiscarded: + // pass + case <-time.After(20 * time.Second): + t.Error("test timed out") + return + } + + if backend.TransactionQueue().Count() != 0 { + t.Error("tx queue must be empty at this point") + return + } +} + +func TestNonExistentQueuedTransactions(t *testing.T) { + err := geth.PrepareTestNode() + if err != nil { + t.Error(err) + return + } + + // make sure you panic if transaction complete doesn't return + completeQueuedTransaction := make(chan struct{}, 1) + geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions") + + // replace transaction notification handler + var txHash = common.Hash{} + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + t.Logf("Transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string)) + time.Sleep(5 * time.Second) + + // next call is the very same one, but with the correct password + if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != nil { + t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err) + return + } + + t.Logf("Transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex()) + completeQueuedTransaction <- struct{}{} // so that timeout is aborted + } + }) + + // try completing non-existing transaction + if _, err = geth.CompleteTransaction("some-bad-transaction-id", testAddressPassword); err == nil { + t.Error("error expected and not recieved") + return + } + if err != status.ErrQueuedTxIdNotFound { + t.Errorf("unexpected error recieved: expected '%s', got: '%s'", status.ErrQueuedTxIdNotFound.Error(), err.Error()) + return + } +} + +func TestEvictionOfQueuedTransactions(t *testing.T) { + err := geth.PrepareTestNode() + if err != nil { + t.Error(err) + return + } + + // obtain reference to status backend + lightEthereum, err := geth.GetNodeManager().LightEthereumService() + if err != nil { + t.Errorf("Test failed: LES service is not running: %v", err) + return + } + backend := lightEthereum.StatusBackend + + // make sure you panic if transaction complete doesn't return + completeQueuedTransaction := make(chan struct{}, 1) + geth.PanicAfter(20*time.Second, completeQueuedTransaction, "TestQueuedTransactions") + + // replace transaction notification handler + var txHash = common.Hash{} + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + t.Logf("Transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string)) + time.Sleep(5 * time.Second) + + // next call is the very same one, but with the correct password + if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != nil { + t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err) + return + } + + t.Logf("Transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex()) + completeQueuedTransaction <- struct{}{} // so that timeout is aborted + } + }) + txQueue := backend.TransactionQueue() var i = 0 + txIds := [status.DefaultTxQueueCap + 5 + 10]status.QueuedTxId{} backend.SetTransactionQueueHandler(func(queuedTx status.QueuedTx) { - //glog.V(logger.Info).Infof("%d. Transaction queued (queue size: %d): {id: %v}\n", i, txQueue.Count(), queuedTx.Id) + t.Logf("%d. Transaction queued (queue size: %d): {id: %v}\n", i, txQueue.Count(), queuedTx.Id) + txIds[i] = queuedTx.Id i++ }) @@ -142,8 +719,17 @@ func TestQueuedTransactions(t *testing.T) { } time.Sleep(5 * time.Second) - if txQueue.Count() != status.DefaultTxQueueCap && txQueue.Count() != (status.DefaultTxQueueCap-1) { + if txQueue.Count() > status.DefaultTxQueueCap { t.Errorf("transaction count should be %d (or %d): got %d", status.DefaultTxQueueCap, status.DefaultTxQueueCap-1, txQueue.Count()) return } + + for _, txId := range txIds { + txQueue.Remove(txId) + } + + if txQueue.Count() != 0 { + t.Errorf("transaction count should be zero: %d", txQueue.Count()) + return + } } diff --git a/geth/types.go b/geth/types.go index 94a499d23..9d782f0df 100644 --- a/geth/types.go +++ b/geth/types.go @@ -1,6 +1,7 @@ package geth import ( + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/les/status" ) @@ -35,16 +36,54 @@ type WhisperMessageEvent struct { } type SendTransactionEvent struct { - Id string `json:"id"` - Args status.SendTxArgs `json:"args"` + Id string `json:"id"` + Args status.SendTxArgs `json:"args"` + MessageId string `json:"message_id"` +} + +type ReturnSendTransactionEvent struct { + Id string `json:"id"` + Args status.SendTxArgs `json:"args"` + MessageId string `json:"message_id"` + ErrorMessage string `json:"error_message"` + ErrorCode string `json:"error_code"` } type CompleteTransactionResult struct { + Id string `json:"id"` Hash string `json:"hash"` Error string `json:"error"` } +type RawCompleteTransactionResult struct { + Hash common.Hash + Error error +} + +type CompleteTransactionsResult struct { + Results map[string]CompleteTransactionResult `json:"results"` +} + +type RawDiscardTransactionResult struct { + Error error +} + +type DiscardTransactionResult struct { + Id string `json:"id"` + Error string `json:"error"` +} + +type DiscardTransactionsResult struct { + Results map[string]DiscardTransactionResult `json:"results"` +} + type GethEvent struct { Type string `json:"type"` Event interface{} `json:"event"` } + +type RPCCall struct { + Id int64 + Method string + Params []interface{} +} diff --git a/geth/utils.go b/geth/utils.go index f466619ac..6dfe9a4d7 100644 --- a/geth/utils.go +++ b/geth/utils.go @@ -15,6 +15,8 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) @@ -23,7 +25,7 @@ var muPrepareTestNode sync.Mutex const ( TestDataDir = "../.ethereumtest" - TestNodeSyncSeconds = 480 + TestNodeSyncSeconds = 120 ) type NodeNotificationHandler func(jsonEvent string) @@ -119,7 +121,7 @@ func PrepareTestNode() (err error) { if !manager.HasNode() { panic(ErrInvalidGethNode) } - if !manager.HasClientRestartWrapper() { + if !manager.HasRPCClient() { panic(ErrInvalidGethNode) } if !manager.HasWhisperService() { @@ -129,8 +131,6 @@ func PrepareTestNode() (err error) { panic(ErrInvalidGethNode) } - manager.AddPeer("enode://409772c7dea96fa59a912186ad5bcdb5e51b80556b3fe447d940f99d9eaadb51d4f0ffedb68efad232b52475dd7bd59b51cee99968b3cc79e2d5684b33c4090c@139.162.166.59:30303") - if syncRequired { glog.V(logger.Warn).Infof("Sync is required, it will take %d seconds", TestNodeSyncSeconds) time.Sleep(TestNodeSyncSeconds * time.Second) // LES syncs headers, so that we are up do date when it is done @@ -169,21 +169,41 @@ func PreprocessDataDir(dataDir string) (string, error) { } // PanicAfter throws panic() after waitSeconds, unless abort channel receives notification -func PanicAfter(waitSeconds time.Duration, abort chan bool, desc string) { - // panic if function takes too long - timeout := make(chan bool, 1) - - go func() { - time.Sleep(waitSeconds) - timeout <- true - }() - +func PanicAfter(waitSeconds time.Duration, abort chan struct{}, desc string) { go func() { select { case <-abort: return - case <-timeout: + case <-time.After(waitSeconds): panic("whatever you were doing takes toooo long: " + desc) } }() } + +func FromAddress(accountAddress string) common.Address { + accountManager, err := GetNodeManager().AccountManager() + if err != nil { + return common.Address{} + } + + from, err := utils.MakeAddress(accountManager, accountAddress) + if err != nil { + return common.Address{} + } + + return from.Address +} + +func ToAddress(accountAddress string) *common.Address { + accountManager, err := GetNodeManager().AccountManager() + if err != nil { + return nil + } + + to, err := utils.MakeAddress(accountManager, accountAddress) + if err != nil { + return nil + } + + return &to.Address +} diff --git a/geth/whisper.go b/geth/whisper.go index 4548108e2..19125e8da 100644 --- a/geth/whisper.go +++ b/geth/whisper.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" ) var ( diff --git a/geth/whisper_test.go b/geth/whisper_test.go index 5e17e722b..57aff0ad8 100644 --- a/geth/whisper_test.go +++ b/geth/whisper_test.go @@ -7,9 +7,7 @@ import ( "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" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "github.com/status-im/status-go/geth" ) @@ -43,7 +41,7 @@ func TestWhisperMessaging(t *testing.T) { t.Error("Test failed: could not create account") return } - glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address1, pubKey1) + t.Logf("Account created: {address: %s, key: %s}", address1, pubKey1) address2, pubKey2, _, err := geth.CreateAccount(newAccountPassword) if err != nil { @@ -51,7 +49,7 @@ func TestWhisperMessaging(t *testing.T) { t.Error("Test failed: could not create account") return } - glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address2, pubKey2) + t.Logf("Account created: {address: %s, key: %s}", address2, pubKey2) // start watchers var receivedMessages = map[string]bool{ @@ -65,7 +63,7 @@ func TestWhisperMessaging(t *testing.T) { //From: crypto.ToECDSAPub(common.FromHex(pubKey1)), //To: crypto.ToECDSAPub(common.FromHex(pubKey2)), Fn: func(msg *whisper.Message) { - //glog.V(logger.Info).Infof("Whisper message received: %s", msg.Payload) + //t.Logf("Whisper message received: %s", msg.Payload) receivedMessages[string(msg.Payload)] = true }, }) diff --git a/jail/jail.go b/jail/jail.go index 01ec53fdc..dcc02efff 100644 --- a/jail/jail.go +++ b/jail/jail.go @@ -5,7 +5,9 @@ import ( "errors" "fmt" "sync" + "time" + "github.com/eapache/go-resiliency/semaphore" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rpc" @@ -13,14 +15,26 @@ import ( "github.com/status-im/status-go/geth" ) +const ( + JailedRuntimeRequestTimeout = time.Second * 60 +) + var ( ErrInvalidJail = errors.New("jail environment is not properly initialized") ) type Jail struct { - client *rpc.ClientRestartWrapper // lazy inited on the first call to jail.ClientRestartWrapper() - VMs map[string]*otto.Otto - statusJS string + sync.RWMutex + client *rpc.Client // lazy inited on the first call + cells map[string]*JailedRuntime // jail supports running many isolated instances of jailed runtime + statusJS string + requestQueue *geth.JailedRequestQueue +} + +type JailedRuntime struct { + id string + vm *otto.Otto + sem *semaphore.Semaphore } var jailInstance *Jail @@ -29,7 +43,7 @@ var once sync.Once func New() *Jail { once.Do(func() { jailInstance = &Jail{ - VMs: make(map[string]*otto.Otto), + cells: make(map[string]*JailedRuntime), } }) @@ -47,20 +61,36 @@ func GetInstance() *Jail { return New() // singleton, we will always get the same reference } +func NewJailedRuntime(id string) *JailedRuntime { + return &JailedRuntime{ + id: id, + vm: otto.New(), + sem: semaphore.New(1, JailedRuntimeRequestTimeout), + } +} + func (jail *Jail) Parse(chatId string, js string) string { if jail == nil { return printError(ErrInvalidJail.Error()) } - vm := otto.New() + jail.Lock() + defer jail.Unlock() + + jail.cells[chatId] = NewJailedRuntime(chatId) + vm := jail.cells[chatId].vm + initJjs := jail.statusJS + ";" - jail.VMs[chatId] = vm _, err := vm.Run(initJjs) vm.Set("jeth", struct{}{}) + sendHandler := func(call otto.FunctionCall) (response otto.Value) { + return jail.Send(chatId, call) + } + jethObj, _ := vm.Get("jeth") - jethObj.Object().Set("send", jail.Send) - jethObj.Object().Set("sendAsync", jail.Send) + jethObj.Object().Set("send", sendHandler) + jethObj.Object().Set("sendAsync", sendHandler) jjs := Web3_JS + ` var Web3 = require('web3'); @@ -78,16 +108,20 @@ func (jail *Jail) Parse(chatId string, js string) string { } func (jail *Jail) Call(chatId string, path string, args string) string { - _, err := jail.ClientRestartWrapper() + _, err := jail.RPCClient() if err != nil { return printError(err.Error()) } - vm, ok := jail.VMs[chatId] + jail.RLock() + cell, ok := jail.cells[chatId] if !ok { - return printError(fmt.Sprintf("VM[%s] doesn't exist.", chatId)) + jail.RUnlock() + return printError(fmt.Sprintf("Cell[%s] doesn't exist.", chatId)) } + jail.RUnlock() + vm := cell.vm.Copy() // isolate VM to allow concurrent access res, err := vm.Call("call", nil, path, args) return printResult(res.String(), err) @@ -98,23 +132,25 @@ func (jail *Jail) GetVM(chatId string) (*otto.Otto, error) { return nil, ErrInvalidJail } - vm, ok := jail.VMs[chatId] + jail.RLock() + defer jail.RUnlock() + + cell, ok := jail.cells[chatId] if !ok { - return nil, fmt.Errorf("VM[%s] doesn't exist.", chatId) + return nil, fmt.Errorf("Cell[%s] doesn't exist.", chatId) } - return vm, nil -} - -type jsonrpcCall struct { - Id int64 - Method string - Params []interface{} + return cell.vm, nil } // Send will serialize the first argument, send it to the node and returns the response. -func (jail *Jail) Send(call otto.FunctionCall) (response otto.Value) { - clientFactory, err := jail.ClientRestartWrapper() +func (jail *Jail) Send(chatId string, call otto.FunctionCall) (response otto.Value) { + client, err := jail.RPCClient() + if err != nil { + return newErrorResponse(call, -32603, err.Error(), nil) + } + + requestQueue, err := jail.RequestQueue() if err != nil { return newErrorResponse(call, -32603, err.Error(), nil) } @@ -127,7 +163,7 @@ func (jail *Jail) Send(call otto.FunctionCall) (response otto.Value) { } var ( rawReq = []byte(reqVal.String()) - reqs []jsonrpcCall + reqs []geth.RPCCall batch bool ) if rawReq[0] == '[' { @@ -135,7 +171,7 @@ func (jail *Jail) Send(call otto.FunctionCall) (response otto.Value) { json.Unmarshal(rawReq, &reqs) } else { batch = false - reqs = make([]jsonrpcCall, 1) + reqs = make([]geth.RPCCall, 1) json.Unmarshal(rawReq, &reqs[0]) } @@ -146,7 +182,25 @@ func (jail *Jail) Send(call otto.FunctionCall) (response otto.Value) { resp.Set("id", req.Id) var result json.RawMessage - client := clientFactory.Client() + // execute directly w/o RPC call to node + if req.Method == geth.SendTransactionRequest { + txHash, err := requestQueue.ProcessSendTransactionRequest(call.Otto, req) + resp.Set("result", txHash.Hex()) + if err != nil { + resp = newErrorResponse(call, -32603, err.Error(), &req.Id).Object() + } + resps.Call("push", resp) + continue + } + + // do extra request pre processing (persist message id) + // within function semaphore will be acquired and released, + // so that no more than one client (per cell) can enter + messageId, err := requestQueue.PreProcessRequest(call.Otto, req) + if err != nil { + return newErrorResponse(call, -32603, err.Error(), nil) + } + errc := make(chan error, 1) errc2 := make(chan error) go func() { @@ -178,6 +232,9 @@ func (jail *Jail) Send(call otto.FunctionCall) (response otto.Value) { resp = newErrorResponse(call, -32603, err.Error(), &req.Id).Object() } resps.Call("push", resp) + + // do extra request post processing (setting back tx context) + requestQueue.PostProcessRequest(call.Otto, req, messageId) } // Return the responses either to the callback (if supplied) @@ -194,7 +251,7 @@ func (jail *Jail) Send(call otto.FunctionCall) (response otto.Value) { return response } -func (jail *Jail) ClientRestartWrapper() (*rpc.ClientRestartWrapper, error) { +func (jail *Jail) RPCClient() (*rpc.Client, error) { if jail == nil { return nil, ErrInvalidJail } @@ -209,7 +266,7 @@ func (jail *Jail) ClientRestartWrapper() (*rpc.ClientRestartWrapper, error) { } // obtain RPC client from running node - client, err := nodeManager.ClientRestartWrapper() + client, err := nodeManager.RPCClient() if err != nil { return nil, err } @@ -218,6 +275,29 @@ func (jail *Jail) ClientRestartWrapper() (*rpc.ClientRestartWrapper, error) { return jail.client, nil } +func (jail *Jail) RequestQueue() (*geth.JailedRequestQueue, error) { + if jail == nil { + return nil, ErrInvalidJail + } + + if jail.requestQueue != nil { + return jail.requestQueue, nil + } + + nodeManager := geth.GetNodeManager() + if !nodeManager.HasNode() { + return nil, geth.ErrInvalidGethNode + } + + requestQueue, err := nodeManager.JailedRequestQueue() + if err != nil { + return nil, err + } + jail.requestQueue = requestQueue + + return jail.requestQueue, nil +} + func newErrorResponse(call otto.FunctionCall, code int, msg string, id interface{}) otto.Value { // Bundle the error into a JSON RPC call response m := map[string]interface{}{"version": "2.0", "id": id, "error": map[string]interface{}{"code": code, msg: msg}} diff --git a/jail/jail_test.go b/jail/jail_test.go index 58b74de3b..69eafc9cd 100644 --- a/jail/jail_test.go +++ b/jail/jail_test.go @@ -1,20 +1,27 @@ package jail_test import ( + "encoding/json" "reflect" + "strings" "testing" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/status-im/status-go/geth" "github.com/status-im/status-go/jail" ) const ( - TEST_ADDRESS = "0x89b50b2b26947ccad43accaef76c21d175ad85f4" - CHAT_ID_INIT = "CHAT_ID_INIT_TEST" - CHAT_ID_CALL = "CHAT_ID_CALL_TEST" - CHAT_ID_NON_EXISTENT = "CHAT_IDNON_EXISTENT" + TEST_ADDRESS = "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" + TEST_ADDRESS_PASSWORD = "asdfasdf" + CHAT_ID_INIT = "CHAT_ID_INIT_TEST" + CHAT_ID_CALL = "CHAT_ID_CALL_TEST" + CHAT_ID_SEND = "CHAT_ID_CALL_SEND" + CHAT_ID_NON_EXISTENT = "CHAT_IDNON_EXISTENT" - TESTDATA_STATUS_JS = "testdata/status.js" + TESTDATA_STATUS_JS = "testdata/status.js" + TESTDATA_TX_SEND_JS = "testdata/tx-send/" ) func TestJailUnInited(t *testing.T) { @@ -40,7 +47,7 @@ func TestJailUnInited(t *testing.T) { t.Errorf("error expected, but got: %v", err) } - _, err = jailInstance.ClientRestartWrapper() + _, err = jailInstance.RPCClient() if err != jail.ErrInvalidJail { t.Errorf("error expected, but got: %v", err) } @@ -128,7 +135,7 @@ func TestJailFunctionCall(t *testing.T) { // call with wrong chat id response := jailInstance.Call(CHAT_ID_NON_EXISTENT, "", "") - expectedError := `{"error":"VM[CHAT_IDNON_EXISTENT] doesn't exist."}` + expectedError := `{"error":"Cell[CHAT_IDNON_EXISTENT] doesn't exist."}` if response != expectedError { t.Errorf("expected error is not returned: expected %s, got %s", expectedError, response) return @@ -163,11 +170,11 @@ func TestJailRPCSend(t *testing.T) { return } + // internally (since we replaced `web3.send` with `jail.Send`) + // all requests to web3 are forwarded to `jail.Send` _, err = vm.Run(` - var data = {"jsonrpc":"2.0","method":"eth_getBalance","params":["` + TEST_ADDRESS + `", "latest"],"id":1}; - var sendResult = web3.currentProvider.send(data) - console.log(JSON.stringify(sendResult)) - var sendResult = web3.fromWei(sendResult.result, "ether") + var balance = web3.eth.getBalance("` + TEST_ADDRESS + `"); + var sendResult = web3.fromWei(balance, "ether") `) if err != nil { t.Errorf("cannot run custom code on VM: %v", err) @@ -194,6 +201,178 @@ func TestJailRPCSend(t *testing.T) { t.Logf("Balance of %.2f ETH found on '%s' account", balance, TEST_ADDRESS) } +func TestJailSendQueuedTransaction(t *testing.T) { + err := geth.PrepareTestNode() + if err != nil { + t.Error(err) + return + } + + txParams := `{ + "from": "` + TEST_ADDRESS + `", + "to": "0xf82da7547534045b4e00442bc89e16186cf8c272", + "value": "0.000001" + }` + + txCompletedSuccessfully := make(chan struct{}) + txCompletedCounter := make(chan struct{}) + txHashes := make(chan common.Hash) + + // replace transaction notification handler + requireMessageId := false + geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope geth.GethEvent + if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil { + t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) + return + } + if envelope.Type == geth.EventTransactionQueued { + event := envelope.Event.(map[string]interface{}) + messageId, ok := event["message_id"].(string) + if !ok { + t.Error("Message id is required, but not found") + return + } + if requireMessageId { + if len(messageId) == 0 { + t.Error("Message id is required, but not provided") + return + } + } else { + if len(messageId) != 0 { + t.Error("Message id is not required, but provided") + return + } + } + t.Logf("Transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string)) + time.Sleep(5 * time.Second) + + var txHash common.Hash + if txHash, err = geth.CompleteTransaction(event["id"].(string), TEST_ADDRESS_PASSWORD); err != nil { + t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err) + } else { + t.Logf("Transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex()) + } + + txCompletedSuccessfully <- struct{}{} // so that timeout is aborted + txHashes <- txHash + txCompletedCounter <- struct{}{} + } + }) + + type testCommand struct { + command string + params string + expectedResponse string + } + type testCase struct { + name string + file string + requireMessageId bool + commands []testCommand + } + + tests := []testCase{ + { + // no context or message id + name: "Case 1: no message id or context in inited JS", + file: "no-message-id-or-context.js", + requireMessageId: false, + commands: []testCommand{ + { + `["commands", "send"]`, + txParams, + `{"result": {"transaction-hash":"TX_HASH"}}`, + }, + { + `["commands", "getBalance"]`, + `{"address": "` + TEST_ADDRESS + `"}`, + `{"result": {"balance":42}}`, + }, + }, + }, + { + // context is present in inited JS (but no message id is there) + name: "Case 2: context is present in inited JS (but no message id is there)", + file: "context-no-message-id.js", + requireMessageId: false, + commands: []testCommand{ + { + `["commands", "send"]`, + txParams, + `{"result": {"context":{"` + geth.SendTransactionRequest + `":true},"result":{"transaction-hash":"TX_HASH"}}}`, + }, + { + `["commands", "getBalance"]`, + `{"address": "` + TEST_ADDRESS + `"}`, + `{"result": {"context":{},"result":{"balance":42}}}`, // note emtpy (but present) context! + }, + }, + }, + { + // message id is present in inited JS, but no context is there + name: "Case 3: message id is present, context is not present", + file: "message-id-no-context.js", + requireMessageId: true, + commands: []testCommand{ + { + `["commands", "send"]`, + txParams, + `{"result": {"transaction-hash":"TX_HASH"}}`, + }, + { + `["commands", "getBalance"]`, + `{"address": "` + TEST_ADDRESS + `"}`, + `{"result": {"balance":42}}`, // note emtpy context! + }, + }, + }, + { + // both message id and context are present in inited JS (this UC is what we normally expect to see) + name: "Case 4: both message id and context are present", + file: "tx-send.js", + requireMessageId: true, + commands: []testCommand{ + { + `["commands", "send"]`, + txParams, + `{"result": {"context":{"eth_sendTransaction":true,"message_id":"foobar"},"result":{"transaction-hash":"TX_HASH"}}}`, + }, + { + `["commands", "getBalance"]`, + `{"address": "` + TEST_ADDRESS + `"}`, + `{"result": {"context":{"message_id":"42"},"result":{"balance":42}}}`, // message id in context, but default one is used! + }, + }, + }, + } + + for _, test := range tests { + jailInstance := jail.Init(geth.LoadFromFile(TESTDATA_TX_SEND_JS + test.file)) + geth.PanicAfter(60*time.Second, txCompletedSuccessfully, test.name) + jailInstance.Parse(CHAT_ID_SEND, ``) + + requireMessageId = test.requireMessageId + + for _, command := range test.commands { + go func(jail *jail.Jail, test testCase, command testCommand) { + t.Logf("->%s: %s", test.name, command.command) + response := jail.Call(CHAT_ID_SEND, command.command, command.params) + var txHash common.Hash + if command.command == `["commands", "send"]` { + txHash = <-txHashes + } + expectedResponse := strings.Replace(command.expectedResponse, "TX_HASH", txHash.Hex(), 1) + if response != expectedResponse { + t.Errorf("expected response is not returned: expected %s, got %s", expectedResponse, response) + return + } + }(jailInstance, test, command) + } + <-txCompletedCounter + } +} + func TestJailMultipleInitSingletonJail(t *testing.T) { err := geth.PrepareTestNode() if err != nil { @@ -226,7 +405,7 @@ func TestJailGetVM(t *testing.T) { jailInstance := jail.Init("") - expectedError := `VM[` + CHAT_ID_NON_EXISTENT + `] doesn't exist.` + expectedError := `Cell[` + CHAT_ID_NON_EXISTENT + `] doesn't exist.` _, err = jailInstance.GetVM(CHAT_ID_NON_EXISTENT) if err == nil || err.Error() != expectedError { t.Error("expected error, but call succeeded") diff --git a/jail/testdata/tx-send/context-no-message-id.js b/jail/testdata/tx-send/context-no-message-id.js new file mode 100644 index 000000000..86027e93a --- /dev/null +++ b/jail/testdata/tx-send/context-no-message-id.js @@ -0,0 +1,67 @@ +var _status_catalog = { + commands: {}, + responses: {} +}; + +var context = {}; +function addContext(ns, key, value) { + // we ignore ns here (as ns is message id, and in this UC we do not have one) + context[key] = value; +} + +function call(pathStr, paramsStr) { + var params = JSON.parse(paramsStr), + path = JSON.parse(pathStr), + fn, res; + + context = {}; + + fn = path.reduce(function(catalog, name) { + if (catalog && catalog[name]) { + return catalog[name]; + } + }, _status_catalog); + + if (!fn) { + return null; + } + + // while fn wll be executed context will be populated + // by addContext calls from status-go + callResult = fn(params); + res = { + result: callResult, + // so context could contain + // {transaction-sent: true} + context: context + }; + + return JSON.stringify(res); +} + +function sendTransaction(params) { + var data = { + from: params.from, + to: params.to, + value: web3.toWei(params.value, "ether") + }; + + // Blocking call, it will return when transaction is complete. + // While call is executing, status-go will call up the application, + // allowing it to validate and complete transaction + var hash = web3.eth.sendTransaction(data); + + return {"transaction-hash": hash}; +} + +_status_catalog.commands['send'] = sendTransaction; +_status_catalog.commands['getBalance'] = function (params) { + var balance = web3.eth.getBalance(params.address); + balance = web3.fromWei(balance, "ether") + if (balance < 90) { + console.log("Unexpected balance (<90): ", balance) + } + // used in tx tests, to check that non-context, non-message-id requests work too, + // so actual balance is not important + return {"balance": 42} +}; diff --git a/jail/testdata/tx-send/message-id-no-context.js b/jail/testdata/tx-send/message-id-no-context.js new file mode 100644 index 000000000..b92fca4ee --- /dev/null +++ b/jail/testdata/tx-send/message-id-no-context.js @@ -0,0 +1,64 @@ +// jail.Send() expects to find the current message id in `status.message_id` +// (if not found message id will not be injected, and operation will proceed) +var status = { + message_id: '42' +}; + +var _status_catalog = { + commands: {}, + responses: {} +}; + +function call(pathStr, paramsStr) { + var params = JSON.parse(paramsStr), + path = JSON.parse(pathStr), + fn, res; + + fn = path.reduce(function(catalog, name) { + if (catalog && catalog[name]) { + return catalog[name]; + } + }, _status_catalog); + + if (!fn) { + return null; + } + + // while fn wll be executed context will be populated + // by addContext calls from status-go + res = fn(params); + + return JSON.stringify(res); +} + +function sendTransaction(params) { + var data = { + from: params.from, + to: params.to, + value: web3.toWei(params.value, "ether") + }; + + // message_id allows you to distinguish between !send invocations + // (when you receive transaction queued event, message_id will be + // attached along the queued transaction id) + status.message_id = 'foobar'; + + // Blocking call, it will return when transaction is complete. + // While call is executing, status-go will call up the application, + // allowing it to validate and complete transaction + var hash = web3.eth.sendTransaction(data); + + return {"transaction-hash": hash}; +} + +_status_catalog.commands['send'] = sendTransaction; +_status_catalog.commands['getBalance'] = function (params) { + var balance = web3.eth.getBalance(params.address); + balance = web3.fromWei(balance, "ether"); + if (balance < 90) { + console.log("Unexpected balance (<90): ", balance) + } + // used in tx tests, to check that non-context, non-message-is requests work too + // so actual balance is not important + return {"balance": 42} +}; diff --git a/jail/testdata/tx-send/no-message-id-or-context.js b/jail/testdata/tx-send/no-message-id-or-context.js new file mode 100644 index 000000000..866db7ba5 --- /dev/null +++ b/jail/testdata/tx-send/no-message-id-or-context.js @@ -0,0 +1,51 @@ + +var _status_catalog = { + commands: {}, + responses: {} +}; + +function call(pathStr, paramsStr) { + var params = JSON.parse(paramsStr), + path = JSON.parse(pathStr), + fn, res; + + fn = path.reduce(function(catalog, name) { + if (catalog && catalog[name]) { + return catalog[name]; + } + }, _status_catalog); + + if (!fn) { + return null; + } + + res = fn(params); + return JSON.stringify(res); +} + +function sendTransaction(params) { + var data = { + from: params.from, + to: params.to, + value: web3.toWei(params.value, "ether") + }; + + // Blocking call, it will return when transaction is complete. + // While call is executing, status-go will call up the application, + // allowing it to validate and complete transaction + var hash = web3.eth.sendTransaction(data); + + return {"transaction-hash": hash}; +} + +_status_catalog.commands['send'] = sendTransaction; +_status_catalog.commands['getBalance'] = function (params) { + var balance = web3.eth.getBalance(params.address); + balance = web3.fromWei(balance, "ether") + if (balance < 90) { + console.log("Unexpected balance (<90): ", balance) + } + // used in tx tests, to check that non-context, non-message-is requests work too + // so actual balance is not important + return {"balance": 42} +}; \ No newline at end of file diff --git a/jail/testdata/tx-send/tx-send.js b/jail/testdata/tx-send/tx-send.js new file mode 100644 index 000000000..8cc7a9614 --- /dev/null +++ b/jail/testdata/tx-send/tx-send.js @@ -0,0 +1,87 @@ +// jail.Send() expects to find the current message id in `status.message_id` +// (if not found message id will not be injected, and operation will proceed) +var status = { + message_id: '42' // global message id, gets replaced in sendTransaction (or any other method) +}; + +var _status_catalog = { + commands: {}, + responses: {} +}; + +var context = {}; +function addContext(ns, key, value) { // this function is expected to be present, as status-go uses it to set context + if (!(ns in context)) { + context[ns] = {} + } + context[ns][key] = value; +} + +function call(pathStr, paramsStr) { + var params = JSON.parse(paramsStr), + path = JSON.parse(pathStr), + fn, res; + + // Since we allow next request to proceed *immediately* after jail obtains message id + // we should be careful overwritting global context variable. + // We probably should limit/scope to context[message_id] = {} + context = {}; + + fn = path.reduce(function(catalog, name) { + if (catalog && catalog[name]) { + return catalog[name]; + } + }, _status_catalog); + + if (!fn) { + return null; + } + + // while fn wll be executed context will be populated + // by addContext calls from status-go + callResult = fn(params); + res = { + result: callResult, + // So, context could contain {eth_transactionSend: true} + // additionally, context gets `message_id` as well. + // You can scope returned context by returning context[message_id], + // however since serialization guard will be released immediately after message id + // is obtained, you need to be careful if you use global message id (it + // works below, in test, it will not work as expected in highly concurrent environment) + context: context[status.message_id] + }; + + return JSON.stringify(res); +} + +function sendTransaction(params) { + var data = { + from: params.from, + to: params.to, + value: web3.toWei(params.value, "ether") + }; + + // message_id allows you to distinguish between !send invocations + // (when you receive transaction queued event, message_id will be + // attached along the queued transaction id) + status.message_id = 'foobar'; + + // Blocking call, it will return when transaction is complete. + // While call is executing, status-go will call up the application, + // allowing it to validate and complete transaction + var hash = web3.eth.sendTransaction(data); + + return {"transaction-hash": hash}; +} + +_status_catalog.commands['send'] = sendTransaction; +_status_catalog.commands['getBalance'] = function (params) { + var balance = web3.eth.getBalance(params.address); + balance = web3.fromWei(balance, "ether"); + if (balance < 90) { + console.log("Unexpected balance (<90): ", balance) + } + // used in tx tests, to check that non-context, non-message-is requests work too + // so actual balance is not important + return {"balance": 42} +}; diff --git a/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s b/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s new file mode 100644 index 000000000..66109f4f3 --- /dev/null +++ b/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s @@ -0,0 +1,6 @@ +// Copyright (C) 2016 Arista Networks, Inc. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the COPYING file. + +// This file is intentionally empty. +// It's a workaround for https://github.com/golang/go/issues/15006 diff --git a/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go b/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go new file mode 100644 index 000000000..efc1b92a6 --- /dev/null +++ b/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go @@ -0,0 +1,24 @@ +// Copyright (C) 2016 Arista Networks, Inc. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the COPYING file. + +// Package monotime provides a fast monotonic clock source. +package monotime + +import ( + _ "unsafe" // required to use //go:linkname +) + +//go:noescape +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +// Now returns the current time in nanoseconds from a monotonic clock. +// The time returned is based on some arbitrary platform-specific point in the +// past. The time returned is guaranteed to increase monotonically at a +// constant rate, unlike time.Now() from the Go standard library, which may +// slow down, speed up, jump forward or backward, due to NTP activity or leap +// seconds. +func Now() uint64 { + return uint64(nanotime()) +} diff --git a/vendor/github.com/cnf/structhash/.travis.yml b/vendor/github.com/cnf/structhash/.travis.yml new file mode 100644 index 000000000..be627f32c --- /dev/null +++ b/vendor/github.com/cnf/structhash/.travis.yml @@ -0,0 +1,4 @@ +language: go +go: + - 1.6 + - tip diff --git a/vendor/github.com/cnf/structhash/LICENSE b/vendor/github.com/cnf/structhash/LICENSE new file mode 100644 index 000000000..8ad679f48 --- /dev/null +++ b/vendor/github.com/cnf/structhash/LICENSE @@ -0,0 +1,6 @@ +ISC license +Copyright (c) 2014, Frank Rosquin + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/cnf/structhash/README.md b/vendor/github.com/cnf/structhash/README.md new file mode 100644 index 000000000..c468ede32 --- /dev/null +++ b/vendor/github.com/cnf/structhash/README.md @@ -0,0 +1,94 @@ +# structhash [![GoDoc](https://godoc.org/github.com/cnf/structhash?status.svg)](https://godoc.org/github.com/cnf/structhash) [![Build Status](https://travis-ci.org/cnf/structhash.svg?branch=master)](https://travis-ci.org/cnf/structhash) + +structhash is a Go library for generating hash strings of arbitrary data structures. + +## Features + +* fields may be ignored or renamed (like in `json.Marshal`, but using different struct tag) +* fields may be versioned +* fields order in struct doesn't matter (unlike `json.Marshal`) +* nil values are treated equally to zero values + +## Installation + +Standard `go get`: + +``` +$ go get github.com/cnf/structhash +``` + +## Documentation + +For usage and examples see the [Godoc](http://godoc.org/github.com/cnf/structhash). + +## Quick start + +```go +package main + +import ( + "fmt" + "crypto/md5" + "crypto/sha1" + "github.com/cnf/structhash" +) + +type S struct { + Str string + Num int +} + +func main() { + s := S{"hello", 123} + + hash, err := structhash.Hash(s, 1) + if err != nil { + panic(err) + } + fmt.Println(hash) + // Prints: v1_41011bfa1a996db6d0b1075981f5aa8f + + fmt.Println(structhash.Version(hash)) + // Prints: 1 + + fmt.Printf("%x\n", structhash.Md5(s, 1)) + // Prints: 41011bfa1a996db6d0b1075981f5aa8f + + fmt.Printf("%x\n", structhash.Sha1(s, 1)) + // Prints: 5ff72df7212ce8c55838fb3ec6ad0c019881a772 + + fmt.Printf("%x\n", md5.Sum(structhash.Dump(s, 1))) + // Prints: 41011bfa1a996db6d0b1075981f5aa8f + + fmt.Printf("%x\n", sha1.Sum(structhash.Dump(s, 1))) + // Prints: 5ff72df7212ce8c55838fb3ec6ad0c019881a772 +} +``` + +## Struct tags + +structhash supports struct tags in the following forms: + +* `hash:"-"`, or +* `hash:"name:{string} version:{number} lastversion:{number}"` + +All fields are optional and may be ommitted. Their semantics is: + +* `-` - ignore field +* `name:{string}` - rename field (may be useful when you want to rename field but keep hashes unchanged for backward compatibility) +* `version:{number}` - ignore field if version passed to structhash is smaller than given number +* `lastversion:{number}` - ignore field if version passed to structhash is greater than given number + +Example: + +```go +type MyStruct struct { + Ignored string `hash:"-"` + Renamed string `hash:"name:OldName version:1"` + Legacy string `hash:"version:1 lastversion:2"` +} +``` + +## Nil values + +When hash is calculated, nil pointers, nil slices, and nil maps are treated equally to zero values of corresponding type. E.g., nil pointer to string is equivalent to empty string, and nil slice is equivalent to empty slice. diff --git a/vendor/github.com/cnf/structhash/doc.go b/vendor/github.com/cnf/structhash/doc.go new file mode 100644 index 000000000..1f04eeed1 --- /dev/null +++ b/vendor/github.com/cnf/structhash/doc.go @@ -0,0 +1,4 @@ +/* +Package structhash creates hash strings from arbitrary go data structures. +*/ +package structhash diff --git a/vendor/github.com/cnf/structhash/structhash.go b/vendor/github.com/cnf/structhash/structhash.go new file mode 100644 index 000000000..ca92190b2 --- /dev/null +++ b/vendor/github.com/cnf/structhash/structhash.go @@ -0,0 +1,240 @@ +package structhash + +import ( + "bytes" + "crypto/md5" + "crypto/sha1" + "fmt" + "reflect" + "sort" + "strconv" + "strings" +) + +// Version returns the version of the supplied hash as an integer +// or -1 on failure +func Version(h string) int { + if h == "" { + return -1 + } + if h[0] != 'v' { + return -1 + } + if spos := strings.IndexRune(h[1:], '_'); spos >= 0 { + n, e := strconv.Atoi(h[1 : spos+1]) + if e != nil { + return -1 + } + return n + } + return -1 +} + +// Hash takes a data structure and returns a hash string of that data structure +// at the version asked. +// +// This function uses md5 hashing function and default formatter. See also Dump() +// function. +func Hash(c interface{}, version int) (string, error) { + return fmt.Sprintf("v%d_%x", version, Md5(c, version)), nil +} + +// Dump takes a data structure and returns its byte representation. This can be +// useful if you need to use your own hashing function or formatter. +func Dump(c interface{}, version int) []byte { + return serialize(c, version) +} + +// Md5 takes a data structure and returns its md5 hash. +// This is a shorthand for md5.Sum(Dump(c, version)). +func Md5(c interface{}, version int) []byte { + sum := md5.Sum(Dump(c, version)) + return sum[:] +} + +// Sha1 takes a data structure and returns its sha1 hash. +// This is a shorthand for sha1.Sum(Dump(c, version)). +func Sha1(c interface{}, version int) []byte { + sum := sha1.Sum(Dump(c, version)) + return sum[:] +} + +type item struct { + name string + value reflect.Value +} + +type itemSorter []item + +func (s itemSorter) Len() int { + return len(s) +} + +func (s itemSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s itemSorter) Less(i, j int) bool { + return s[i].name < s[j].name +} + +type structFieldFilter func(reflect.StructField) (string, bool) + +func writeValue(buf *bytes.Buffer, val reflect.Value, fltr structFieldFilter) { + switch val.Kind() { + case reflect.String: + buf.WriteByte('"') + buf.WriteString(val.String()) + buf.WriteByte('"') + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + buf.WriteString(strconv.FormatInt(val.Int(), 10)) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + buf.WriteString(strconv.FormatUint(val.Uint(), 10)) + case reflect.Bool: + if val.Bool() { + buf.WriteByte('t') + } else { + buf.WriteByte('f') + } + case reflect.Ptr: + if !val.IsNil() || val.Type().Elem().Kind() == reflect.Struct { + writeValue(buf, reflect.Indirect(val), fltr) + } else { + writeValue(buf, reflect.Zero(val.Type().Elem()), fltr) + } + case reflect.Array, reflect.Slice: + buf.WriteByte('[') + len := val.Len() + for i := 0; i < len; i++ { + if i != 0 { + buf.WriteByte(',') + } + writeValue(buf, val.Index(i), fltr) + } + buf.WriteByte(']') + case reflect.Map: + mk := val.MapKeys() + items := make([]item, len(mk), len(mk)) + // Get all values + for i, _ := range items { + items[i].name = formatValue(mk[i], fltr) + items[i].value = val.MapIndex(mk[i]) + } + + // Sort values by key + sort.Sort(itemSorter(items)) + + buf.WriteByte('[') + for i, _ := range items { + if i != 0 { + buf.WriteByte(',') + } + buf.WriteString(items[i].name) + buf.WriteByte(':') + writeValue(buf, items[i].value, fltr) + } + buf.WriteByte(']') + case reflect.Struct: + vtype := val.Type() + flen := vtype.NumField() + items := make([]item, 0, flen) + // Get all fields + for i := 0; i < flen; i++ { + field := vtype.Field(i) + it := item{field.Name, val.Field(i)} + if fltr != nil { + if name, ok := fltr(field); ok { + it.name = name + } else { + continue + } + } + items = append(items, it) + } + // Sort fields by name + sort.Sort(itemSorter(items)) + + buf.WriteByte('{') + for i, _ := range items { + if i != 0 { + buf.WriteByte(',') + } + buf.WriteString(items[i].name) + buf.WriteByte(':') + writeValue(buf, items[i].value, fltr) + } + buf.WriteByte('}') + default: + buf.WriteString(val.String()) + } +} + +func formatValue(val reflect.Value, fltr structFieldFilter) string { + if val.Kind() == reflect.String { + return "\"" + val.Interface().(string) + "\"" + } + + var buf bytes.Buffer + writeValue(&buf, val, fltr) + + return string(buf.Bytes()) +} + +func filterField(f reflect.StructField, version int) (string, bool) { + var err error + name := f.Name + ver := 0 + lastver := -1 + if str := f.Tag.Get("hash"); str != "" { + if str == "-" { + return "", false + } + for _, tag := range strings.Split(str, " ") { + args := strings.Split(strings.TrimSpace(tag), ":") + if len(args) != 2 { + return "", false + } + switch args[0] { + case "name": + name = args[1] + case "version": + if ver, err = strconv.Atoi(args[1]); err != nil { + return "", false + } + case "lastversion": + if lastver, err = strconv.Atoi(args[1]); err != nil { + return "", false + } + } + } + } else { + if str := f.Tag.Get("lastversion"); str != "" { + if lastver, err = strconv.Atoi(str); err != nil { + return "", false + } + } + if str := f.Tag.Get("version"); str != "" { + if ver, err = strconv.Atoi(str); err != nil { + return "", false + } + } + } + if lastver != -1 && lastver < version { + return "", false + } + if ver > version { + return "", false + } + return name, true +} + +func serialize(object interface{}, version int) []byte { + var buf bytes.Buffer + + writeValue(&buf, reflect.ValueOf(object), + func(f reflect.StructField) (string, bool) { + return filterField(f, version) + }) + + return buf.Bytes() +} diff --git a/vendor/github.com/eapache/go-resiliency/.gitignore b/vendor/github.com/eapache/go-resiliency/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/eapache/go-resiliency/.travis.yml b/vendor/github.com/eapache/go-resiliency/.travis.yml new file mode 100644 index 000000000..26c6bf8fe --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.2 + - 1.3 + - 1.4 + - 1.5 diff --git a/vendor/github.com/eapache/go-resiliency/LICENSE b/vendor/github.com/eapache/go-resiliency/LICENSE new file mode 100644 index 000000000..698a3f513 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/eapache/go-resiliency/README.md b/vendor/github.com/eapache/go-resiliency/README.md new file mode 100644 index 000000000..0a0d70111 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/README.md @@ -0,0 +1,21 @@ +go-resiliency +============= + +[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) +[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency?status.svg)](https://godoc.org/github.com/eapache/go-resiliency) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +Resiliency patterns for golang. +Based in part on [Hystrix](https://github.com/Netflix/Hystrix), +[Semian](https://github.com/Shopify/semian), and others. + +Currently implemented patterns include: +- circuit-breaker (in the `breaker` directory) +- semaphore (in the `semaphore` directory) +- deadline/timeout (in the `deadline` directory) +- batching (in the `batcher` directory) +- retriable (in the `retrier` directory) + +Follows semantic versioning using https://gopkg.in/ - import from +[`gopkg.in/eapache/go-resiliency.v1`](https://gopkg.in/eapache/go-resiliency.v1) +for guaranteed API stability. diff --git a/vendor/github.com/eapache/go-resiliency/batcher/README.md b/vendor/github.com/eapache/go-resiliency/batcher/README.md new file mode 100644 index 000000000..60e3b9f0e --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/batcher/README.md @@ -0,0 +1,31 @@ +batcher +======= + +[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) +[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/batcher?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/batcher) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +The batching resiliency pattern for golang. + +Creating a batcher takes two parameters: +- the timeout to wait while collecting a batch +- the function to run once a batch has been collected + +You can also optionally set a prefilter to fail queries before they enter the +batch. + +```go +b := batcher.New(10*time.Millisecond, func(params []interface{}) error { + // do something with the batch of parameters + return nil +}) + +b.Prefilter(func(param interface{}) error { + // do some sort of sanity check on the parameter, and return an error if it fails + return nil +}) + +for i := 0; i < 10; i++ { + go b.Run(i) +} +``` diff --git a/vendor/github.com/eapache/go-resiliency/batcher/batcher.go b/vendor/github.com/eapache/go-resiliency/batcher/batcher.go new file mode 100644 index 000000000..2d1ccb46c --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/batcher/batcher.go @@ -0,0 +1,108 @@ +// Package batcher implements the batching resiliency pattern for Go. +package batcher + +import ( + "sync" + "time" +) + +type work struct { + param interface{} + future chan error +} + +// Batcher implements the batching resiliency pattern +type Batcher struct { + timeout time.Duration + prefilter func(interface{}) error + + lock sync.Mutex + submit chan *work + doWork func([]interface{}) error +} + +// New constructs a new batcher that will batch all calls to Run that occur within +// `timeout` time before calling doWork just once for the entire batch. The doWork +// function must be safe to run concurrently with itself as this may occur, especially +// when the timeout is small. +func New(timeout time.Duration, doWork func([]interface{}) error) *Batcher { + return &Batcher{ + timeout: timeout, + doWork: doWork, + } +} + +// Run runs the work function with the given parameter, possibly +// including it in a batch with other calls to Run that occur within the +// specified timeout. It is safe to call Run concurrently on the same batcher. +func (b *Batcher) Run(param interface{}) error { + if b.prefilter != nil { + if err := b.prefilter(param); err != nil { + return err + } + } + + if b.timeout == 0 { + return b.doWork([]interface{}{param}) + } + + w := &work{ + param: param, + future: make(chan error, 1), + } + + b.submitWork(w) + + return <-w.future +} + +// Prefilter specifies an optional function that can be used to run initial checks on parameters +// passed to Run before being added to the batch. If the prefilter returns a non-nil error, +// that error is returned immediately from Run and the batcher is not invoked. A prefilter +// cannot safely be specified for a batcher if Run has already been invoked. The filter function +// specified must be concurrency-safe. +func (b *Batcher) Prefilter(filter func(interface{}) error) { + b.prefilter = filter +} + +func (b *Batcher) submitWork(w *work) { + b.lock.Lock() + defer b.lock.Unlock() + + if b.submit == nil { + b.submit = make(chan *work, 4) + go b.batch() + } + + b.submit <- w +} + +func (b *Batcher) batch() { + var params []interface{} + var futures []chan error + input := b.submit + + go b.timer() + + for work := range input { + params = append(params, work.param) + futures = append(futures, work.future) + } + + ret := b.doWork(params) + + for _, future := range futures { + future <- ret + close(future) + } +} + +func (b *Batcher) timer() { + time.Sleep(b.timeout) + + b.lock.Lock() + defer b.lock.Unlock() + + close(b.submit) + b.submit = nil +} diff --git a/vendor/github.com/eapache/go-resiliency/breaker/README.md b/vendor/github.com/eapache/go-resiliency/breaker/README.md new file mode 100644 index 000000000..2d1b3d932 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/breaker/README.md @@ -0,0 +1,34 @@ +circuit-breaker +=============== + +[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) +[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/breaker?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/breaker) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +The circuit-breaker resiliency pattern for golang. + +Creating a breaker takes three parameters: +- error threshold (for opening the breaker) +- success threshold (for closing the breaker) +- timeout (how long to keep the breaker open) + +```go +b := breaker.New(3, 1, 5*time.Second) + +for { + result := b.Run(func() error { + // communicate with some external service and + // return an error if the communication failed + return nil + }) + + switch result { + case nil: + // success! + case breaker.ErrBreakerOpen: + // our function wasn't run because the breaker was open + default: + // some other error + } +} +``` diff --git a/vendor/github.com/eapache/go-resiliency/breaker/breaker.go b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go new file mode 100644 index 000000000..f88ca7248 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go @@ -0,0 +1,161 @@ +// Package breaker implements the circuit-breaker resiliency pattern for Go. +package breaker + +import ( + "errors" + "sync" + "sync/atomic" + "time" +) + +// ErrBreakerOpen is the error returned from Run() when the function is not executed +// because the breaker is currently open. +var ErrBreakerOpen = errors.New("circuit breaker is open") + +const ( + closed uint32 = iota + open + halfOpen +) + +// Breaker implements the circuit-breaker resiliency pattern +type Breaker struct { + errorThreshold, successThreshold int + timeout time.Duration + + lock sync.Mutex + state uint32 + errors, successes int + lastError time.Time +} + +// New constructs a new circuit-breaker that starts closed. +// From closed, the breaker opens if "errorThreshold" errors are seen +// without an error-free period of at least "timeout". From open, the +// breaker half-closes after "timeout". From half-open, the breaker closes +// after "successThreshold" consecutive successes, or opens on a single error. +func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker { + return &Breaker{ + errorThreshold: errorThreshold, + successThreshold: successThreshold, + timeout: timeout, + } +} + +// Run will either return ErrBreakerOpen immediately if the circuit-breaker is +// already open, or it will run the given function and pass along its return +// value. It is safe to call Run concurrently on the same Breaker. +func (b *Breaker) Run(work func() error) error { + state := atomic.LoadUint32(&b.state) + + if state == open { + return ErrBreakerOpen + } + + return b.doWork(state, work) +} + +// Go will either return ErrBreakerOpen immediately if the circuit-breaker is +// already open, or it will run the given function in a separate goroutine. +// If the function is run, Go will return nil immediately, and will *not* return +// the return value of the function. It is safe to call Go concurrently on the +// same Breaker. +func (b *Breaker) Go(work func() error) error { + state := atomic.LoadUint32(&b.state) + + if state == open { + return ErrBreakerOpen + } + + // errcheck complains about ignoring the error return value, but + // that's on purpose; if you want an error from a goroutine you have to + // get it over a channel or something + go b.doWork(state, work) + + return nil +} + +func (b *Breaker) doWork(state uint32, work func() error) error { + var panicValue interface{} + + result := func() error { + defer func() { + panicValue = recover() + }() + return work() + }() + + if result == nil && panicValue == nil && state == closed { + // short-circuit the normal, success path without contending + // on the lock + return nil + } + + // oh well, I guess we have to contend on the lock + b.processResult(result, panicValue) + + if panicValue != nil { + // as close as Go lets us come to a "rethrow" although unfortunately + // we lose the original panicing location + panic(panicValue) + } + + return result +} + +func (b *Breaker) processResult(result error, panicValue interface{}) { + b.lock.Lock() + defer b.lock.Unlock() + + if result == nil && panicValue == nil { + if b.state == halfOpen { + b.successes++ + if b.successes == b.successThreshold { + b.closeBreaker() + } + } + } else { + if b.errors > 0 { + expiry := b.lastError.Add(b.timeout) + if time.Now().After(expiry) { + b.errors = 0 + } + } + + switch b.state { + case closed: + b.errors++ + if b.errors == b.errorThreshold { + b.openBreaker() + } else { + b.lastError = time.Now() + } + case halfOpen: + b.openBreaker() + } + } +} + +func (b *Breaker) openBreaker() { + b.changeState(open) + go b.timer() +} + +func (b *Breaker) closeBreaker() { + b.changeState(closed) +} + +func (b *Breaker) timer() { + time.Sleep(b.timeout) + + b.lock.Lock() + defer b.lock.Unlock() + + b.changeState(halfOpen) +} + +func (b *Breaker) changeState(newState uint32) { + b.errors = 0 + b.successes = 0 + atomic.StoreUint32(&b.state, newState) +} diff --git a/vendor/github.com/eapache/go-resiliency/deadline/README.md b/vendor/github.com/eapache/go-resiliency/deadline/README.md new file mode 100644 index 000000000..ac97b460f --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/deadline/README.md @@ -0,0 +1,27 @@ +deadline +======== + +[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) +[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/deadline?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/deadline) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +The deadline/timeout resiliency pattern for golang. + +Creating a deadline takes one parameter: how long to wait. + +```go +dl := deadline.New(1 * time.Second) + +err := dl.Run(func(stopper <-chan struct{}) error { + // do something potentially slow + // give up when the `stopper` channel is closed (indicating a time-out) + return nil +}) + +switch err { +case deadline.ErrTimedOut: + // execution took too long, oops +default: + // some other error +} +``` diff --git a/vendor/github.com/eapache/go-resiliency/deadline/deadline.go b/vendor/github.com/eapache/go-resiliency/deadline/deadline.go new file mode 100644 index 000000000..3a6dfb0ee --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/deadline/deadline.go @@ -0,0 +1,45 @@ +// Package deadline implements the deadline (also known as "timeout") resiliency pattern for Go. +package deadline + +import ( + "errors" + "time" +) + +// ErrTimedOut is the error returned from Run when the deadline expires. +var ErrTimedOut = errors.New("timed out waiting for function to finish") + +// Deadline implements the deadline/timeout resiliency pattern. +type Deadline struct { + timeout time.Duration +} + +// New constructs a new Deadline with the given timeout. +func New(timeout time.Duration) *Deadline { + return &Deadline{ + timeout: timeout, + } +} + +// Run runs the given function, passing it a stopper channel. If the deadline passes before +// the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper +// channel so that the work function can attempt to exit gracefully. It does not (and cannot) +// simply kill the running function, so if it doesn't respect the stopper channel then it may +// keep running after the deadline passes. If the function finishes before the deadline, then +// the return value of the function is returned from Run. +func (d *Deadline) Run(work func(<-chan struct{}) error) error { + result := make(chan error) + stopper := make(chan struct{}) + + go func() { + result <- work(stopper) + }() + + select { + case ret := <-result: + return ret + case <-time.After(d.timeout): + close(stopper) + return ErrTimedOut + } +} diff --git a/vendor/github.com/eapache/go-resiliency/retrier/README.md b/vendor/github.com/eapache/go-resiliency/retrier/README.md new file mode 100644 index 000000000..dd30af7a0 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/retrier/README.md @@ -0,0 +1,26 @@ +retrier +======= + +[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) +[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/retrier?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/retrier) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +The retriable resiliency pattern for golang. + +Creating a retrier takes two parameters: +- the times to back-off between retries (and implicitly the number of times to + retry) +- the classifier that determines which errors to retry + +```go +r := retrier.New(retrier.ConstantBackoff(3, 100*time.Millisecond), nil) + +err := r.Run(func() error { + // do some work + return nil +}) + +if err != nil { + // handle the case where the work failed three times +} +``` diff --git a/vendor/github.com/eapache/go-resiliency/retrier/backoffs.go b/vendor/github.com/eapache/go-resiliency/retrier/backoffs.go new file mode 100644 index 000000000..faf6f8cf9 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/retrier/backoffs.go @@ -0,0 +1,24 @@ +package retrier + +import "time" + +// ConstantBackoff generates a simple back-off strategy of retrying 'n' times, and waiting 'amount' time after each one. +func ConstantBackoff(n int, amount time.Duration) []time.Duration { + ret := make([]time.Duration, n) + for i := range ret { + ret[i] = amount + } + return ret +} + +// ExponentialBackoff generates a simple back-off strategy of retrying 'n' times, and doubling the amount of +// time waited after each one. +func ExponentialBackoff(n int, initialAmount time.Duration) []time.Duration { + ret := make([]time.Duration, n) + next := initialAmount + for i := range ret { + ret[i] = next + next *= 2 + } + return ret +} diff --git a/vendor/github.com/eapache/go-resiliency/retrier/classifier.go b/vendor/github.com/eapache/go-resiliency/retrier/classifier.go new file mode 100644 index 000000000..7dd71c798 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/retrier/classifier.go @@ -0,0 +1,66 @@ +package retrier + +// Action is the type returned by a Classifier to indicate how the Retrier should proceed. +type Action int + +const ( + Succeed Action = iota // Succeed indicates the Retrier should treat this value as a success. + Fail // Fail indicates the Retrier should treat this value as a hard failure and not retry. + Retry // Retry indicates the Retrier should treat this value as a soft failure and retry. +) + +// Classifier is the interface implemented by anything that can classify Errors for a Retrier. +type Classifier interface { + Classify(error) Action +} + +// DefaultClassifier classifies errors in the simplest way possible. If +// the error is nil, it returns Succeed, otherwise it returns Retry. +type DefaultClassifier struct{} + +// Classify implements the Classifier interface. +func (c DefaultClassifier) Classify(err error) Action { + if err == nil { + return Succeed + } + + return Retry +} + +// WhitelistClassifier classifies errors based on a whitelist. If the error is nil, it +// returns Succeed; if the error is in the whitelist, it returns Retry; otherwise, it returns Fail. +type WhitelistClassifier []error + +// Classify implements the Classifier interface. +func (list WhitelistClassifier) Classify(err error) Action { + if err == nil { + return Succeed + } + + for _, pass := range list { + if err == pass { + return Retry + } + } + + return Fail +} + +// BlacklistClassifier classifies errors based on a blacklist. If the error is nil, it +// returns Succeed; if the error is in the blacklist, it returns Fail; otherwise, it returns Retry. +type BlacklistClassifier []error + +// Classify implements the Classifier interface. +func (list BlacklistClassifier) Classify(err error) Action { + if err == nil { + return Succeed + } + + for _, pass := range list { + if err == pass { + return Fail + } + } + + return Retry +} diff --git a/vendor/github.com/eapache/go-resiliency/retrier/retrier.go b/vendor/github.com/eapache/go-resiliency/retrier/retrier.go new file mode 100644 index 000000000..ff328742b --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/retrier/retrier.go @@ -0,0 +1,69 @@ +// Package retrier implements the "retriable" resiliency pattern for Go. +package retrier + +import ( + "math/rand" + "time" +) + +// Retrier implements the "retriable" resiliency pattern, abstracting out the process of retrying a failed action +// a certain number of times with an optional back-off between each retry. +type Retrier struct { + backoff []time.Duration + class Classifier + jitter float64 + rand *rand.Rand +} + +// New constructs a Retrier with the given backoff pattern and classifier. The length of the backoff pattern +// indicates how many times an action will be retried, and the value at each index indicates the amount of time +// waited before each subsequent retry. The classifier is used to determine which errors should be retried and +// which should cause the retrier to fail fast. The DefaultClassifier is used if nil is passed. +func New(backoff []time.Duration, class Classifier) *Retrier { + if class == nil { + class = DefaultClassifier{} + } + + return &Retrier{ + backoff: backoff, + class: class, + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + } +} + +// Run executes the given work function, then classifies its return value based on the classifier used +// to construct the Retrier. If the result is Succeed or Fail, the return value of the work function is +// returned to the caller. If the result is Retry, then Run sleeps according to the its backoff policy +// before retrying. If the total number of retries is exceeded then the return value of the work function +// is returned to the caller regardless. +func (r *Retrier) Run(work func() error) error { + retries := 0 + for { + ret := work() + + switch r.class.Classify(ret) { + case Succeed, Fail: + return ret + case Retry: + if retries >= len(r.backoff) { + return ret + } + time.Sleep(r.calcSleep(retries)) + retries++ + } + } +} + +func (r *Retrier) calcSleep(i int) time.Duration { + // take a random float in the range (-r.jitter, +r.jitter) and multiply it by the base amount + return r.backoff[i] + time.Duration(((r.rand.Float64()*2)-1)*r.jitter*float64(r.backoff[i])) +} + +// SetJitter sets the amount of jitter on each back-off to a factor between 0.0 and 1.0 (values outside this range +// are silently ignored). When a retry occurs, the back-off is adjusted by a random amount up to this value. +func (r *Retrier) SetJitter(jit float64) { + if jit < 0 || jit > 1 { + return + } + r.jitter = jit +} diff --git a/vendor/github.com/eapache/go-resiliency/semaphore/README.md b/vendor/github.com/eapache/go-resiliency/semaphore/README.md new file mode 100644 index 000000000..a4a73ea07 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/semaphore/README.md @@ -0,0 +1,22 @@ +semaphore +========= + +[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) +[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/semaphore?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/semaphore) +[![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) + +The semaphore resiliency pattern for golang. + +Creating a semaphore takes two parameters: +- ticket count (how many tickets to give out at once) +- timeout (how long to wait for a ticket if none are currently available) + +```go +sem := semaphore.New(3, 1*time.Second) + +if err := sem.Acquire(); err != nil { + // could not acquire semaphore + return err +} +defer sem.Release() +``` diff --git a/vendor/github.com/eapache/go-resiliency/semaphore/semaphore.go b/vendor/github.com/eapache/go-resiliency/semaphore/semaphore.go new file mode 100644 index 000000000..90cb1fd15 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/semaphore/semaphore.go @@ -0,0 +1,45 @@ +// Package semaphore implements the semaphore resiliency pattern for Go. +package semaphore + +import ( + "errors" + "time" +) + +// ErrNoTickets is the error returned by Acquire when it could not acquire +// a ticket from the semaphore within the configured timeout. +var ErrNoTickets = errors.New("could not aquire semaphore ticket") + +// Semaphore implements the semaphore resiliency pattern +type Semaphore struct { + sem chan struct{} + timeout time.Duration +} + +// New constructs a new Semaphore with the given ticket-count +// and timeout. +func New(tickets int, timeout time.Duration) *Semaphore { + return &Semaphore{ + sem: make(chan struct{}, tickets), + timeout: timeout, + } +} + +// Acquire tries to acquire a ticket from the semaphore. If it can, it returns nil. +// If it cannot after "timeout" amount of time, it returns ErrNoTickets. It is +// safe to call Acquire concurrently on a single Semaphore. +func (s *Semaphore) Acquire() error { + select { + case s.sem <- struct{}{}: + return nil + case <-time.After(s.timeout): + return ErrNoTickets + } +} + +// Release releases an acquired ticket back to the semaphore. It is safe to call +// Release concurrently on a single Semaphore. It is an error to call Release on +// a Semaphore from which you have not first acquired a ticket. +func (s *Semaphore) Release() { + <-s.sem +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go index 1b07b2f68..2efac1307 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go @@ -77,7 +77,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { return append(method.Id(), arguments...), nil } -// toGoSliceType prses the input and casts it to the proper slice defined by the ABI +// toGoSliceType parses the input and casts it to the proper slice defined by the ABI // argument in T. func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { index := i * 32 @@ -98,28 +98,46 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { case HashTy: // hash must be of slice hash refSlice = reflect.ValueOf([]common.Hash(nil)) case FixedBytesTy: - refSlice = reflect.ValueOf([]byte(nil)) + refSlice = reflect.ValueOf([][]byte(nil)) default: // no other types are supported return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T) } - // get the offset which determines the start of this array ... - offset := int(common.BytesToBig(output[index : index+32]).Uint64()) - if offset+32 > len(output) { - return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32) + + var slice []byte + var size int + var offset int + if t.Type.IsSlice { + + // get the offset which determines the start of this array ... + offset = int(common.BytesToBig(output[index : index+32]).Uint64()) + if offset+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32) + } + + slice = output[offset:] + // ... starting with the size of the array in elements ... + size = int(common.BytesToBig(slice[:32]).Uint64()) + slice = slice[32:] + // ... and make sure that we've at the very least the amount of bytes + // available in the buffer. + if size*32 > len(slice) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32) + } + + // reslice to match the required size + slice = slice[:(size * 32)] + } else if t.Type.IsArray { + //get the number of elements in the array + size = t.Type.SliceSize + + //check to make sure array size matches up + if index+32*size > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size) + } + //slice is there for a fixed amount of times + slice = output[index : index+size*32] } - slice := output[offset:] - // ... starting with the size of the array in elements ... - size := int(common.BytesToBig(slice[:32]).Uint64()) - slice = slice[32:] - // ... and make sure that we've at the very least the amount of bytes - // available in the buffer. - if size*32 > len(slice) { - return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32) - } - - // reslice to match the required size - slice = slice[:(size * 32)] for i := 0; i < size; i++ { var ( inter interface{} // interface type @@ -136,6 +154,8 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { inter = common.BytesToAddress(returnOutput) case HashTy: inter = common.BytesToHash(returnOutput) + case FixedBytesTy: + inter = returnOutput } // append the item to our reflect slice refSlice = reflect.Append(refSlice, reflect.ValueOf(inter)) diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go index 2cf22768c..a20852fca 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go @@ -48,15 +48,15 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { keyAddr := crypto.PubkeyToAddress(key.PublicKey) return &TransactOpts{ From: keyAddr, - Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { if address != keyAddr { return nil, errors.New("not authorized to sign this account") } - signature, err := crypto.Sign(tx.SigHash().Bytes(), key) + signature, err := crypto.SignEthereum(signer.Hash(tx).Bytes(), key) if err != nil { return nil, err } - return tx.WithSignature(signature) + return tx.WithSignature(signer, signature) }, } } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go index bec742c29..4509e222d 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -20,28 +20,52 @@ import ( "errors" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "golang.org/x/net/context" ) -// ErrNoCode is returned by call and transact operations for which the requested -// recipient contract to operate on does not exist in the state db or does not -// have any code associated with it (i.e. suicided). -var ErrNoCode = errors.New("no contract code at given address") +var ( + // ErrNoCode is returned by call and transact operations for which the requested + // recipient contract to operate on does not exist in the state db or does not + // have any code associated with it (i.e. suicided). + ErrNoCode = errors.New("no contract code at given address") + + // This error is raised when attempting to perform a pending state action + // on a backend that doesn't implement PendingContractCaller. + ErrNoPendingState = errors.New("backend does not support pending state") + + // This error is returned by WaitDeployed if contract creation leaves an + // empty contract behind. + ErrNoCodeAfterDeploy = errors.New("no contract code after deployment") +) // ContractCaller defines the methods needed to allow operating with contract on a read // only basis. type ContractCaller interface { - // HasCode checks if the contract at the given address has any code associated - // with it or not. This is needed to differentiate between contract internal - // errors and the local chain being out of sync. - HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) + // CodeAt returns the code of the given account. This is needed to differentiate + // between contract internal errors and the local chain being out of sync. + CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) + // ContractCall executes an Ethereum contract call with the specified data as the + // input. + CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) +} - // ContractCall executes an Ethereum contract call with the specified data as - // the input. The pending flag requests execution against the pending block, not - // the stable head of the chain. - ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) +// DeployBackend wraps the operations needed by WaitMined and WaitDeployed. +type DeployBackend interface { + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) +} + +// PendingContractCaller defines methods to perform contract calls on the pending state. +// Call will try to discover this interface when access to the pending state is requested. +// If the backend does not support the pending state, Call returns ErrNoPendingState. +type PendingContractCaller interface { + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) + // PendingCallContract executes an Ethereum contract call against the pending state. + PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) } // ContractTransactor defines the methods needed to allow operating with contract @@ -49,64 +73,25 @@ type ContractCaller interface { // used when the user does not provide some needed values, but rather leaves it up // to the transactor to decide. type ContractTransactor interface { - // PendingAccountNonce retrieves the current pending nonce associated with an - // account. - PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) - + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + // PendingNonceAt retrieves the current pending nonce associated with an account. + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) // SuggestGasPrice retrieves the currently suggested gas price to allow a timely // execution of a transaction. SuggestGasPrice(ctx context.Context) (*big.Int, error) - - // HasCode checks if the contract at the given address has any code associated - // with it or not. This is needed to differentiate between contract internal - // errors and the local chain being out of sync. - HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) - - // EstimateGasLimit tries to estimate the gas needed to execute a specific + // EstimateGas tries to estimate the gas needed to execute a specific // transaction based on the current pending state of the backend blockchain. // There is no guarantee that this is 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. - EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) - + EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) // SendTransaction injects the transaction into the pending pool for execution. SendTransaction(ctx context.Context, tx *types.Transaction) error } -// ContractBackend defines the methods needed to allow operating with contract -// on a read-write basis. -// -// This interface is essentially the union of ContractCaller and ContractTransactor -// but due to a bug in the Go compiler (https://github.com/golang/go/issues/6977), -// we cannot simply list it as the two interfaces. The other solution is to add a -// third interface containing the common methods, but that convolutes the user API -// as it introduces yet another parameter to require for initialization. +// ContractBackend defines the methods needed to work with contracts on a read-write basis. type ContractBackend interface { - // HasCode checks if the contract at the given address has any code associated - // with it or not. This is needed to differentiate between contract internal - // errors and the local chain being out of sync. - HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) - - // ContractCall executes an Ethereum contract call with the specified data as - // the input. The pending flag requests execution against the pending block, not - // the stable head of the chain. - ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) - - // PendingAccountNonce retrieves the current pending nonce associated with an - // account. - PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) - - // SuggestGasPrice retrieves the currently suggested gas price to allow a timely - // execution of a transaction. - SuggestGasPrice(ctx context.Context) (*big.Int, error) - - // EstimateGasLimit tries to estimate the gas needed to execute a specific - // transaction based on the current pending state of the backend blockchain. - // There is no guarantee that this is 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. - EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) - - // SendTransaction injects the transaction into the pending pool for execution. - SendTransaction(ctx context.Context, tx *types.Transaction) error + ContractCaller + ContractTransactor } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/nil.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/nil.go deleted file mode 100644 index 54b222f1f..000000000 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/nil.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package backends - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "golang.org/x/net/context" -) - -// This nil assignment ensures compile time that nilBackend implements bind.ContractBackend. -var _ bind.ContractBackend = (*nilBackend)(nil) - -// nilBackend implements bind.ContractBackend, but panics on any method call. -// Its sole purpose is to support the binding tests to construct the generated -// wrappers without calling any methods on them. -type nilBackend struct{} - -func (*nilBackend) ContractCall(context.Context, common.Address, []byte, bool) ([]byte, error) { - panic("not implemented") -} -func (*nilBackend) EstimateGasLimit(context.Context, common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) { - panic("not implemented") -} -func (*nilBackend) HasCode(context.Context, common.Address, bool) (bool, error) { - panic("not implemented") -} -func (*nilBackend) SuggestGasPrice(context.Context) (*big.Int, error) { panic("not implemented") } -func (*nilBackend) PendingAccountNonce(context.Context, common.Address) (uint64, error) { - panic("not implemented") -} -func (*nilBackend) SendTransaction(context.Context, *types.Transaction) error { - panic("not implemented") -} - -// NewNilBackend creates a new binding backend that can be used for instantiation -// but will panic on any invocation. Its sole purpose is to help testing. -func NewNilBackend() bind.ContractBackend { - return new(nilBackend) -} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/remote.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/remote.go deleted file mode 100644 index 58edd791a..000000000 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/remote.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package backends - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" -) - -// This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend. -var _ bind.ContractBackend = (*rpcBackend)(nil) - -// rpcBackend implements bind.ContractBackend, and acts as the data provider to -// Ethereum contracts bound to Go structs. It uses an RPC connection to delegate -// all its functionality. -type rpcBackend struct { - client *rpc.Client // RPC client connection to interact with an API server -} - -// NewRPCBackend creates a new binding backend to an RPC provider that can be -// used to interact with remote contracts. -func NewRPCBackend(client *rpc.Client) bind.ContractBackend { - return &rpcBackend{client: client} -} - -// HasCode implements ContractVerifier.HasCode by retrieving any code associated -// with the contract from the remote node, and checking its size. -func (b *rpcBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { - block := "latest" - if pending { - block = "pending" - } - var hex string - err := b.client.CallContext(ctx, &hex, "eth_getCode", contract, block) - if err != nil { - return false, err - } - return len(common.FromHex(hex)) > 0, nil -} - -// ContractCall implements ContractCaller.ContractCall, delegating the execution of -// a contract call to the remote node, returning the reply to for local processing. -func (b *rpcBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { - args := struct { - To common.Address `json:"to"` - Data string `json:"data"` - }{ - To: contract, - Data: common.ToHex(data), - } - block := "latest" - if pending { - block = "pending" - } - var hex string - err := b.client.CallContext(ctx, &hex, "eth_call", args, block) - if err != nil { - return nil, err - } - return common.FromHex(hex), nil - -} - -// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating -// the current account nonce retrieval to the remote node. -func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { - var hex rpc.HexNumber - err := b.client.CallContext(ctx, &hex, "eth_getTransactionCount", account.Hex(), "pending") - if err != nil { - return 0, err - } - return hex.Uint64(), nil -} - -// SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the -// gas price oracle request to the remote node. -func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - var hex rpc.HexNumber - if err := b.client.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { - return nil, err - } - return (*big.Int)(&hex), nil -} - -// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating -// the gas estimation to the remote node. -func (b *rpcBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - args := struct { - From common.Address `json:"from"` - To *common.Address `json:"to"` - Value *rpc.HexNumber `json:"value"` - Data string `json:"data"` - }{ - From: sender, - To: contract, - Data: common.ToHex(data), - Value: rpc.NewHexNumber(value), - } - // Execute the RPC call and retrieve the response - var hex rpc.HexNumber - err := b.client.CallContext(ctx, &hex, "eth_estimateGas", args) - if err != nil { - return nil, err - } - return (*big.Int)(&hex), nil -} - -// SendTransaction implements ContractTransactor.SendTransaction, delegating the -// raw transaction injection to the remote node. -func (b *rpcBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - data, err := rlp.EncodeToBytes(tx) - if err != nil { - return err - } - return b.client.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) -} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go index 687a31bf1..00a8cd3e9 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -17,8 +17,12 @@ package backends import ( + "errors" + "fmt" "math/big" + "sync" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -27,23 +31,29 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "golang.org/x/net/context" ) // Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) -var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} +var chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)} // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) +var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block") + // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in // the background. Its main purpose is to allow easily testing contract bindings. type SimulatedBackend struct { database ethdb.Database // In memory database to store our testing data blockchain *core.BlockChain // Ethereum blockchain to handle the consensus + mu sync.Mutex pendingBlock *types.Block // Currently pending block that will be imported on request pendingState *state.StateDB // Currently pending state that will be the active on on request + + config *params.ChainConfig } // NewSimulatedBackend creates a new binding backend using a simulated blockchain @@ -52,85 +62,131 @@ func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend { database, _ := ethdb.NewMemDatabase() core.WriteGenesisBlockForTesting(database, accounts...) blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux)) - - backend := &SimulatedBackend{ - database: database, - blockchain: blockchain, - } - backend.Rollback() - + backend := &SimulatedBackend{database: database, blockchain: blockchain} + backend.rollback() return backend } // Commit imports all the pending transactions as a single block and starts a // fresh new state. func (b *SimulatedBackend) Commit() { + b.mu.Lock() + defer b.mu.Unlock() + if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil { panic(err) // This cannot happen unless the simulator is wrong, fail in that case } - b.Rollback() + b.rollback() } // Rollback aborts all pending transactions, reverting to the last committed state. func (b *SimulatedBackend) Rollback() { - blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) + b.mu.Lock() + defer b.mu.Unlock() + b.rollback() +} + +func (b *SimulatedBackend) rollback() { + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) } -// HasCode implements ContractVerifier.HasCode, checking whether there is any -// code associated with a certain account in the blockchain. -func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { - if pending { - return len(b.pendingState.GetCode(contract)) > 0, nil +// CodeAt returns the code associated with a certain account in the blockchain. +func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported } statedb, _ := b.blockchain.State() - return len(statedb.GetCode(contract)) > 0, nil + return statedb.GetCode(contract), nil } -// ContractCall implements ContractCaller.ContractCall, executing the specified -// contract with the given input data. -func (b *SimulatedBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { - // Create a copy of the current state db to screw around with - var ( - block *types.Block - statedb *state.StateDB - ) - if pending { - block, statedb = b.pendingBlock, b.pendingState.Copy() - } else { - block = b.blockchain.CurrentBlock() - statedb, _ = b.blockchain.State() - } - // If there's no code to interact with, respond with an appropriate error - if code := statedb.GetCode(contract); len(code) == 0 { - return nil, bind.ErrNoCode - } - // Set infinite balance to the a fake caller account - from := statedb.GetOrNewStateObject(common.Address{}) - from.SetBalance(common.MaxBig) +// BalanceAt returns the wei balance of a certain account in the blockchain. +func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() - // Assemble the call invocation to measure the gas usage - msg := callmsg{ - from: from, - to: &contract, - gasPrice: new(big.Int), - gasLimit: common.MaxBig, - value: new(big.Int), - data: data, + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported } - // Execute the call and return - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) - gaspool := new(core.GasPool).AddGas(common.MaxBig) - - out, _, err := core.ApplyMessage(vmenv, msg, gaspool) - return out, err + statedb, _ := b.blockchain.State() + return statedb.GetBalance(contract), nil } -// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving +// NonceAt returns the nonce of a certain account in the blockchain. +func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return 0, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + return statedb.GetNonce(contract), nil +} + +// StorageAt returns the value of key in the storage of an account in the blockchain. +func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + val := statedb.GetState(contract, key) + return val[:], nil +} + +// TransactionReceipt returns the receipt of a transaction. +func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + return core.GetReceipt(b.database, txHash), nil +} + +// PendingCodeAt returns the code associated with an account in the pending state. +func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return b.pendingState.GetCode(contract), nil +} + +// CallContract executes a contract call. +func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + state, err := b.blockchain.State() + if err != nil { + return nil, err + } + rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + return rval, err +} + +// PendingCallContract executes a contract call on the pending state. +func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) + + rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + return rval, err +} + +// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving // the nonce currently pending for the account. -func (b *SimulatedBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { +func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + b.mu.Lock() + defer b.mu.Unlock() + return b.pendingState.GetOrNewStateObject(account).Nonce(), nil } @@ -140,46 +196,57 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error return big.NewInt(1), nil } -// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the -// requested code against the currently pending block/state and returning the used -// gas. -func (b *SimulatedBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - // Create a copy of the currently pending state db to screw around with - var ( - block = b.pendingBlock - statedb = b.pendingState.Copy() - ) - // If there's no code to interact with, respond with an appropriate error - if contract != nil { - if code := statedb.GetCode(*contract); len(code) == 0 { - return nil, bind.ErrNoCode - } - } - // Set infinite balance to the a fake caller account - from := statedb.GetOrNewStateObject(sender) - from.SetBalance(common.MaxBig) +// EstimateGas executes the requested code against the currently pending block/state and +// returns the used amount of gas. +func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() + defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) - // Assemble the call invocation to measure the gas usage - msg := callmsg{ - from: from, - to: contract, - gasPrice: new(big.Int), - gasLimit: common.MaxBig, - value: value, - data: data, - } - // Execute the call and return - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) - gaspool := new(core.GasPool).AddGas(common.MaxBig) - - _, gas, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) return gas, err } -// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw -// transaction injection to the remote node. +// callContract implemens common code between normal and pending contract calls. +// state is modified during execution, make sure to copy it if necessary. +func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { + // Ensure message is initialized properly. + if call.GasPrice == nil { + call.GasPrice = big.NewInt(1) + } + if call.Gas == nil || call.Gas.BitLen() == 0 { + call.Gas = big.NewInt(50000000) + } + if call.Value == nil { + call.Value = new(big.Int) + } + // Set infinite balance to the fake caller account. + from := statedb.GetOrNewStateObject(call.From) + from.SetBalance(common.MaxBig) + // Execute the call. + msg := callmsg{call} + vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) + gaspool := new(core.GasPool).AddGas(common.MaxBig) + ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + return ret, gasUsed, err +} + +// SendTransaction updates the pending block to include the given transaction. +// It panics if the transaction is invalid. func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { + b.mu.Lock() + defer b.mu.Unlock() + + sender, err := types.Sender(types.HomesteadSigner{}, tx) + if err != nil { + panic(fmt.Errorf("invalid transaction: %v", err)) + } + nonce := b.pendingState.GetNonce(sender) + if tx.Nonce() != nonce { + panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) + } + + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { for _, tx := range b.pendingBlock.Transactions() { block.AddTx(tx) } @@ -187,26 +254,19 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa }) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) - return nil } // callmsg implements core.Message to allow passing it as a transaction simulator. type callmsg struct { - from *state.StateObject - to *common.Address - gasLimit *big.Int - gasPrice *big.Int - value *big.Int - data []byte + ethereum.CallMsg } -func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.to } -func (m callmsg) GasPrice() *big.Int { return m.gasPrice } -func (m callmsg) Gas() *big.Int { return m.gasLimit } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } +func (m callmsg) From() common.Address { return m.CallMsg.From } +func (m callmsg) Nonce() uint64 { return 0 } +func (m callmsg) CheckNonce() bool { return false } +func (m callmsg) To() *common.Address { return m.CallMsg.To } +func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } +func (m callmsg) Value() *big.Int { return m.CallMsg.Value } +func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go index 80948d3f1..7df02e83f 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -20,8 +20,8 @@ import ( "errors" "fmt" "math/big" - "sync/atomic" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -31,7 +31,7 @@ import ( // SignerFn is a signer function callback when a contract requires a method to // sign the transaction before submission. -type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error) +type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error) // CallOpts is the collection of options to fine tune a contract call request. type CallOpts struct { @@ -62,9 +62,6 @@ type BoundContract struct { abi abi.ABI // Reflect based ABI to access the correct Ethereum methods caller ContractCaller // Read interface to interact with the blockchain transactor ContractTransactor // Write interface to interact with the blockchain - - latestHasCode uint32 // Cached verification that the latest state contains code for this contract - pendingHasCode uint32 // Cached verification that the pending state contains code for this contract } // NewBoundContract creates a low level contract interface through which calls @@ -105,25 +102,42 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, if opts == nil { opts = new(CallOpts) } - // Make sure we have a contract to operate on, and bail out otherwise - if (opts.Pending && atomic.LoadUint32(&c.pendingHasCode) == 0) || (!opts.Pending && atomic.LoadUint32(&c.latestHasCode) == 0) { - if code, err := c.caller.HasCode(opts.Context, c.address, opts.Pending); err != nil { - return err - } else if !code { - return ErrNoCode - } - if opts.Pending { - atomic.StoreUint32(&c.pendingHasCode, 1) - } else { - atomic.StoreUint32(&c.latestHasCode, 1) - } - } // Pack the input, call and unpack the results input, err := c.abi.Pack(method, params...) if err != nil { return err } - output, err := c.caller.ContractCall(opts.Context, c.address, input, opts.Pending) + var ( + msg = ethereum.CallMsg{To: &c.address, Data: input} + ctx = ensureContext(opts.Context) + code []byte + output []byte + ) + if opts.Pending { + pb, ok := c.caller.(PendingContractCaller) + if !ok { + return ErrNoPendingState + } + output, err = pb.PendingCallContract(ctx, msg) + if err == nil && len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } else { + output, err = c.caller.CallContract(ctx, msg, nil) + if err == nil && len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = c.caller.CodeAt(ctx, c.address, nil); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } if err != nil { return err } @@ -158,7 +172,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i } nonce := uint64(0) if opts.Nonce == nil { - nonce, err = c.transactor.PendingAccountNonce(opts.Context, opts.From) + nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) if err != nil { return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) } @@ -168,7 +182,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i // Figure out the gas allowance and gas price values gasPrice := opts.GasPrice if gasPrice == nil { - gasPrice, err = c.transactor.SuggestGasPrice(opts.Context) + gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) if err != nil { return nil, fmt.Errorf("failed to suggest gas price: %v", err) } @@ -176,18 +190,18 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i gasLimit := opts.GasLimit if gasLimit == nil { // Gas estimation cannot succeed without code for method invocations - if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 { - if code, err := c.transactor.HasCode(opts.Context, c.address, true); err != nil { + if contract != nil { + if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { return nil, err - } else if !code { + } else if len(code) == 0 { return nil, ErrNoCode } - atomic.StoreUint32(&c.pendingHasCode, 1) } // If the contract surely has code (or code is not needed), estimate the transaction - gasLimit, err = c.transactor.EstimateGasLimit(opts.Context, opts.From, contract, value, input) + msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input} + gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) if err != nil { - return nil, fmt.Errorf("failed to exstimate gas needed: %v", err) + return nil, fmt.Errorf("failed to estimate gas needed: %v", err) } } // Create the transaction, sign it and schedule it for execution @@ -200,12 +214,19 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i if opts.Signer == nil { return nil, errors.New("no signer to authorize the transaction with") } - signedTx, err := opts.Signer(opts.From, rawTx) + signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx) if err != nil { return nil, err } - if err := c.transactor.SendTransaction(opts.Context, signedTx); err != nil { + if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { return nil, err } return signedTx, nil } + +func ensureContext(ctx context.Context) context.Context { + if ctx == nil { + return context.TODO() + } + return ctx +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go index 24fe9f770..84cf22e3c 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go @@ -32,11 +32,20 @@ import ( "golang.org/x/tools/imports" ) +// Lang is a target programming language selector to generate bindings for. +type Lang int + +const ( + LangGo Lang = iota + LangJava + LangObjC +) + // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant // to be used as is in client code, but rather as an intermediate struct which // enforces compile time type safety and naming convention opposed to having to // manually maintain hard coded strings that break on runtime. -func Bind(types []string, abis []string, bytecodes []string, pkg string) (string, error) { +func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) { // Process each individual contract requested binding contracts := make(map[string]*tmplContract) @@ -62,7 +71,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string for _, original := range evmABI.Methods { // Normalize the method for capital cases and non-anonymous inputs/outputs normalized := original - normalized.Name = capitalise(original.Name) + normalized.Name = methodNormalizer[lang](original.Name) normalized.Inputs = make([]abi.Argument, len(original.Inputs)) copy(normalized.Inputs, original.Inputs) @@ -78,7 +87,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string normalized.Outputs[j].Name = capitalise(output.Name) } } - // Append the methos to the call or transact lists + // Append the methods to the call or transact lists if original.Const { calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} } else { @@ -87,7 +96,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string } contracts[types[i]] = &tmplContract{ Type: capitalise(types[i]), - InputABI: strippedABI, + InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), InputBin: strings.TrimSpace(bytecodes[i]), Constructor: evmABI.Constructor, Calls: calls, @@ -102,24 +111,38 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string buffer := new(bytes.Buffer) funcs := map[string]interface{}{ - "bindtype": bindType, + "bindtype": bindType[lang], + "namedtype": namedType[lang], + "capitalise": capitalise, + "decapitalise": decapitalise, } - tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource)) + tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) if err := tmpl.Execute(buffer, data); err != nil { return "", err } - // Pass the code through goimports to clean it up and double check - code, err := imports.Process("", buffer.Bytes(), nil) - if err != nil { - return "", fmt.Errorf("%v\n%s", err, buffer) + // For Go bindings pass the code through goimports to clean it up and double check + if lang == LangGo { + code, err := imports.Process("", buffer.Bytes(), nil) + if err != nil { + return "", fmt.Errorf("%v\n%s", err, buffer) + } + return string(code), nil } - return string(code), nil + // For all others just return as is for now + return string(buffer.Bytes()), nil } -// bindType converts a Solidity type to a Go one. Since there is no clear mapping +// bindType is a set of type binders that convert Solidity types to some supported +// programming language. +var bindType = map[Lang]func(kind abi.Type) string{ + LangGo: bindTypeGo, + LangJava: bindTypeJava, +} + +// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly // mapped will use an upscaled type (e.g. *big.Int). -func bindType(kind abi.Type) string { +func bindTypeGo(kind abi.Type) string { stringKind := kind.String() switch { @@ -160,11 +183,137 @@ func bindType(kind abi.Type) string { } } +// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping +// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly +// mapped will use an upscaled type (e.g. BigDecimal). +func bindTypeJava(kind abi.Type) string { + stringKind := kind.String() + + switch { + case strings.HasPrefix(stringKind, "address"): + parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("Address") + } + return fmt.Sprintf("Addresses") + + case strings.HasPrefix(stringKind, "bytes"): + parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 3 { + return stringKind + } + if parts[2] != "" { + return "byte[][]" + } + return "byte[]" + + case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): + parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 4 { + return stringKind + } + switch parts[2] { + case "8", "16", "32", "64": + if parts[1] == "" { + if parts[3] == "" { + return fmt.Sprintf("int%s", parts[2]) + } + return fmt.Sprintf("int%s[]", parts[2]) + } + } + if parts[3] == "" { + return fmt.Sprintf("BigInt") + } + return fmt.Sprintf("BigInts") + + case strings.HasPrefix(stringKind, "bool"): + parts := regexp.MustCompile("bool(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("bool") + } + return fmt.Sprintf("bool[]") + + case strings.HasPrefix(stringKind, "string"): + parts := regexp.MustCompile("string(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("String") + } + return fmt.Sprintf("String[]") + + default: + return stringKind + } +} + +// namedType is a set of functions that transform language specific types to +// named versions that my be used inside method names. +var namedType = map[Lang]func(string, abi.Type) string{ + LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, + LangJava: namedTypeJava, +} + +// namedTypeJava converts some primitive data types to named variants that can +// be used as parts of method names. +func namedTypeJava(javaKind string, solKind abi.Type) string { + switch javaKind { + case "byte[]": + return "Binary" + case "byte[][]": + return "Binaries" + case "string": + return "String" + case "string[]": + return "Strings" + case "bool": + return "Bool" + case "bool[]": + return "Bools" + case "BigInt": + parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(solKind.String()) + if len(parts) != 4 { + return javaKind + } + switch parts[2] { + case "8", "16", "32", "64": + if parts[3] == "" { + return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2])) + } + return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2])) + + default: + return javaKind + } + default: + return javaKind + } +} + +// methodNormalizer is a name transformer that modifies Solidity method names to +// conform to target language naming concentions. +var methodNormalizer = map[Lang]func(string) string{ + LangGo: capitalise, + LangJava: decapitalise, +} + // capitalise makes the first character of a string upper case. func capitalise(input string) string { return strings.ToUpper(input[:1]) + input[1:] } +// decapitalise makes the first character of a string lower case. +func decapitalise(input string) string { + return strings.ToLower(input[:1]) + input[1:] +} + // structured checks whether a method has enough information to return a proper // Go struct ot if flat returns are needed. func structured(method abi.Method) bool { diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go index 72998bb6d..64dd598c0 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go @@ -42,9 +42,16 @@ type tmplMethod struct { Structured bool // Whether the returns should be accumulated into a contract } -// tmplSource is the Go source template use to generate the contract binding +// tmplSource is language to template mapping containing all the supported +// programming languages the package can generate to. +var tmplSource = map[Lang]string{ + LangGo: tmplSourceGo, + LangJava: tmplSourceJava, +} + +// tmplSourceGo is the Go source template use to generate the contract binding // based on. -const tmplSource = ` +const tmplSourceGo = ` // This file is an automatically generated Go binding. Do not modify as any // change will likely be lost upon the next re-generation! @@ -52,7 +59,7 @@ package {{.Package}} {{range $contract := .Contracts}} // {{.Type}}ABI is the input ABI used to generate the binding from. - const {{.Type}}ABI = ` + "`" + `{{.InputABI}}` + "`" + ` + const {{.Type}}ABI = "{{.InputABI}}" {{if .InputBin}} // {{.Type}}Bin is the compiled bytecode used for deploying new contracts. @@ -127,7 +134,7 @@ package {{.Package}} // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract. func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) { - contract, err := bind{{.Type}}(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor)) + contract, err := bind{{.Type}}(address, backend, backend) if err != nil { return nil, err } @@ -258,3 +265,105 @@ package {{.Package}} {{end}} {{end}} ` + +// tmplSourceJava is the Java source template use to generate the contract binding +// based on. +const tmplSourceJava = ` +// This file is an automatically generated Java binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package {{.Package}}; + +import org.ethereum.geth.*; +import org.ethereum.geth.internal.*; + +{{range $contract := .Contracts}} + public class {{.Type}} { + // ABI is the input ABI used to generate the binding from. + public final static String ABI = "{{.InputABI}}"; + + {{if .InputBin}} + // BYTECODE is the compiled bytecode used for deploying new contracts. + public final static byte[] BYTECODE = "{{.InputBin}}".getBytes(); + + // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it. + public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}}); + {{range $index, $element := .Constructor.Inputs}} + args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + return new {{.Type}}(Geth.deployContract(auth, ABI, BYTECODE, client, args)); + } + + // Internal constructor used by contract deployment. + private {{.Type}}(BoundContract deployment) { + this.Address = deployment.getAddress(); + this.Deployer = deployment.getDeployer(); + this.Contract = deployment; + } + {{end}} + + // Ethereum address where this contract is located at. + public final Address Address; + + // Ethereum transaction in which this contract was deployed (if known!). + public final Transaction Deployer; + + // Contract instance bound to a blockchain address. + private final BoundContract Contract; + + // Creates a new instance of {{.Type}}, bound to a specific deployed contract. + public {{.Type}}(Address address, EthereumClient client) throws Exception { + this(Geth.bindContract(address, ABI, client)); + } + + {{range .Calls}} + {{if gt (len .Normalized.Outputs) 1}} + // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}. + public class {{capitalise .Normalized.Name}}Results { + {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}}; + {{end}} + } + {{end}} + + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); + {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + + Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}}); + {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type) .Type}}(); results.set({{$index}}, result{{$index}}); + {{end}} + + if (opts == null) { + opts = Geth.newCallOpts(); + } + this.Contract.call(opts, results, "{{.Original.Name}}", args); + {{if gt (len .Normalized.Outputs) 1}} + {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results(); + {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type) .Type}}(); + {{end}} + return result; + {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type) .Type}}();{{end}} + {{end}} + } + {{end}} + + {{range .Transacts}} + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); + {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + + return this.Contract.transact(opts, "{{.Original.Name}}" , args); + } + {{end}} + } +{{end}} +` diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go new file mode 100644 index 000000000..bbb6d6a75 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go @@ -0,0 +1,76 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +// WaitMined waits for tx to be mined on the blockchain. +// It stops waiting when the context is canceled. +func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { + queryTicker := time.NewTicker(1 * time.Second) + defer queryTicker.Stop() + loghash := tx.Hash().Hex()[:8] + for { + receipt, err := b.TransactionReceipt(ctx, tx.Hash()) + if receipt != nil { + return receipt, nil + } + if err != nil { + glog.V(logger.Detail).Infof("tx %x error: %v", loghash, err) + } else { + glog.V(logger.Detail).Infof("tx %x not yet mined...", loghash) + } + // Wait for the next round. + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-queryTicker.C: + } + } +} + +// WaitDeployed waits for a contract deployment transaction and returns the on-chain +// contract address when it is mined. It stops waiting when ctx is canceled. +func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) { + if tx.To() != nil { + return common.Address{}, fmt.Errorf("tx is not contract creation") + } + receipt, err := WaitMined(ctx, b, tx) + if err != nil { + return common.Address{}, err + } + if receipt.ContractAddress == (common.Address{}) { + return common.Address{}, fmt.Errorf("zero address") + } + // Check that code has indeed been deployed at the address. + // This matters on pre-Homestead chains: OOG in the constructor + // could leave an empty account behind. + code, err := b.CodeAt(ctx, receipt.ContractAddress, nil) + if err == nil && len(code) == 0 { + err = ErrNoCodeAfterDeploy + } + return receipt.ContractAddress, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go index ab5020200..7970ba8ac 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go @@ -78,10 +78,6 @@ func set(dst, src reflect.Value, output Argument) error { case dstType.AssignableTo(src.Type()): dst.Set(src) case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice: - if !dstType.Elem().AssignableTo(r_byte) { - return fmt.Errorf("abi: cannot unmarshal %v in to array of elem %v", src.Type(), dstType.Elem()) - } - if dst.Len() < output.Type.SliceSize { return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len()) } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go index 2235bad61..39b843f81 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go @@ -170,6 +170,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy { var packed []byte + for i := 0; i < v.Len(); i++ { val, err := t.Elem.pack(v.Index(i)) if err != nil { @@ -177,7 +178,11 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { } packed = append(packed, val...) } - return packBytesSlice(packed, v.Len()), nil + if t.IsSlice { + return packBytesSlice(packed, v.Len()), nil + } else if t.IsArray { + return packed, nil + } } return packElement(t, v), nil @@ -186,5 +191,5 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { // requireLengthPrefix returns whether the type requires any sort of length // prefixing. func (t Type) requiresLengthPrefix() bool { - return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice || t.IsArray) + return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice) } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go b/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go index a295bcee5..faddc7cf0 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/account_manager.go @@ -141,8 +141,11 @@ func (am *Manager) DeleteAccount(a Account, passphrase string) error { return err } -// Sign signs hash with an unlocked private key matching the given address. -func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err error) { +// Sign calculates a ECDSA signature for the given hash. +// Note, Ethereum signatures have a particular format as described in the +// yellow paper. Use the SignEthereum function to calculate a signature +// in Ethereum format. +func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) { am.mu.RLock() defer am.mu.RUnlock() unlockedKey, found := am.unlocked[addr] @@ -152,8 +155,20 @@ func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err return crypto.Sign(hash, unlockedKey.PrivateKey) } -// SignWithPassphrase signs hash if the private key matching the given address can be -// decrypted with the given passphrase. +// SignEthereum calculates a ECDSA signature for the given hash. +// The signature has the format as described in the Ethereum yellow paper. +func (am *Manager) SignEthereum(addr common.Address, hash []byte) ([]byte, error) { + am.mu.RLock() + defer am.mu.RUnlock() + unlockedKey, found := am.unlocked[addr] + if !found { + return nil, ErrLocked + } + return crypto.SignEthereum(hash, unlockedKey.PrivateKey) +} + +// SignWithPassphrase signs hash if the private key matching the given +// address can be decrypted with the given passphrase. func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, hash []byte) (signature []byte, err error) { _, key, err := am.getDecryptedKey(Account{Address: addr}, passphrase) if err != nil { @@ -161,7 +176,7 @@ func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, ha } defer zeroKey(key.PrivateKey) - return crypto.Sign(hash, key.PrivateKey) + return crypto.SignEthereum(hash, key.PrivateKey) } // Unlock unlocks the given account indefinitely. @@ -218,11 +233,17 @@ func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Durati return nil } -func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) { +// Find resolves the given account into a unique entry in the keystore. +func (am *Manager) Find(a Account) (Account, error) { am.cache.maybeReload() am.cache.mu.Lock() a, err := am.cache.find(a) am.cache.mu.Unlock() + return a, err +} + +func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) { + a, err := am.Find(a) if err != nil { return a, nil, err } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go b/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go index 700a4eadc..493602352 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/key_store_passphrase.go @@ -47,12 +47,20 @@ import ( const ( keyHeaderKDF = "scrypt" - // n,r,p = 2^18, 8, 1 uses 256MB memory and approx 1s CPU time on a modern CPU. + // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. StandardScryptN = 1 << 18 + + // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. StandardScryptP = 1 - // n,r,p = 2^12, 8, 6 uses 4MB memory and approx 100ms CPU time on a modern CPU. + // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. LightScryptN = 1 << 12 + + // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. LightScryptP = 6 scryptR = 8 @@ -108,7 +116,8 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { return nil, err } encryptKey := derivedKey[:16] - keyBytes := crypto.FromECDSA(key.PrivateKey) + keyBytes0 := crypto.FromECDSA(key.PrivateKey) + keyBytes := common.LeftPadBytes(keyBytes0, 32) iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go index 9c9ab6d31..dfbd025da 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go @@ -31,14 +31,15 @@ import ( var ( abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") - typFlag = flag.String("type", "", "Go struct name for the binding (default = package name)") + typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested") excFlag = flag.String("exc", "", "Comma separated types to exclude from binding") - pkgFlag = flag.String("pkg", "", "Go package name to generate the binding into") - outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") + pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") + outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") + langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)") ) func main() { @@ -53,7 +54,19 @@ func main() { os.Exit(-1) } if *pkgFlag == "" { - fmt.Printf("No destination Go package specified (--pkg)\n") + fmt.Printf("No destination package specified (--pkg)\n") + os.Exit(-1) + } + var lang bind.Lang + switch *langFlag { + case "go": + lang = bind.LangGo + case "java": + lang = bind.LangJava + case "objc": + lang = bind.LangObjC + default: + fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag) os.Exit(-1) } // If the entire solidity code was specified, build and bind based on that @@ -108,7 +121,7 @@ func main() { types = append(types, kind) } // Generate the contract binding - code, err := bind.Bind(types, abis, bins, *pkgFlag) + code, err := bind.Bind(types, abis, bins, *pkgFlag, lang) if err != nil { fmt.Printf("Failed to generate ABI binding: %v\n", err) os.Exit(-1) diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go index 40d3cdc17..abecac3d8 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" ) @@ -38,6 +39,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:)") + runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") nodeKey *ecdsa.PrivateKey err error @@ -79,8 +81,15 @@ func main() { os.Exit(0) } - if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { - utils.Fatalf("%v", err) + if *runv5 { + if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { + utils.Fatalf("%v", err) + } + } else { + if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { + utils.Fatalf("%v", err) + } } + select {} } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go new file mode 100644 index 000000000..b2f14a4a9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bzzd/main.go @@ -0,0 +1,246 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "os" + "runtime" + "strconv" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "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/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/swarm" + bzzapi "github.com/ethereum/go-ethereum/swarm/api" + "gopkg.in/urfave/cli.v1" +) + +const clientIdentifier = "bzzd" + +var ( + gitCommit string // Git SHA1 commit hash of the release (set via linker flags) + app = utils.NewApp(gitCommit, "Ethereum Swarm server daemon") +) + +var ( + ChequebookAddrFlag = cli.StringFlag{ + Name: "chequebook", + Usage: "chequebook contract address", + } + SwarmAccountFlag = cli.StringFlag{ + Name: "bzzaccount", + Usage: "Swarm account key file", + } + SwarmPortFlag = cli.StringFlag{ + Name: "bzzport", + Usage: "Swarm local http api port", + } + SwarmConfigPathFlag = cli.StringFlag{ + Name: "bzzconfig", + Usage: "Swarm config file path (datadir/bzz)", + } + SwarmSwapDisabled = cli.BoolFlag{ + Name: "bzznoswap", + Usage: "Swarm SWAP disabled (default false)", + } + SwarmSyncDisabled = cli.BoolFlag{ + Name: "bzznosync", + Usage: "Swarm Syncing disabled (default false)", + } + EthAPI = cli.StringFlag{ + Name: "ethapi", + Usage: "URL of the Ethereum API provider", + Value: node.DefaultIPCEndpoint("geth"), + } +) + +var defaultBootnodes = []string{} + +func init() { + // Override flag defaults so bzzd can run alongside geth. + utils.ListenPortFlag.Value = 30399 + utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"} + + // Set up the cli app. + app.Commands = nil + app.Action = bzzd + app.Flags = []cli.Flag{ + utils.IdentityFlag, + utils.DataDirFlag, + utils.BootnodesFlag, + utils.KeyStoreDirFlag, + utils.ListenPortFlag, + utils.MaxPeersFlag, + utils.NATFlag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.IPCDisabledFlag, + utils.IPCApiFlag, + utils.IPCPathFlag, + // bzzd-specific flags + EthAPI, + SwarmConfigPathFlag, + SwarmSwapDisabled, + SwarmSyncDisabled, + SwarmPortFlag, + SwarmAccountFlag, + ChequebookAddrFlag, + } + app.Flags = append(app.Flags, debug.Flags...) + app.Before = func(ctx *cli.Context) error { + runtime.GOMAXPROCS(runtime.NumCPU()) + return debug.Setup(ctx) + } + app.After = func(ctx *cli.Context) error { + debug.Exit() + return nil + } +} + +func main() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func bzzd(ctx *cli.Context) error { + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + registerBzzService(ctx, stack) + utils.StartNode(stack) + + // Add bootnodes as initial peers. + if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { + injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name)) + } else { + injectBootnodes(stack.Server(), defaultBootnodes) + } + + stack.Wait() + return nil +} + +func registerBzzService(ctx *cli.Context, stack *node.Node) { + prvkey := getAccount(ctx, stack) + + chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name)) + bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name) + if bzzdir == "" { + bzzdir = stack.InstanceDir() + } + bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey) + if err != nil { + utils.Fatalf("unable to configure swarm: %v", err) + } + bzzport := ctx.GlobalString(SwarmPortFlag.Name) + if len(bzzport) > 0 { + bzzconfig.Port = bzzport + } + swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name) + syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.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) + if err != nil { + utils.Fatalf("Can't connect: %v", err) + } + return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled) + } + if err := stack.Register(boot); err != nil { + utils.Fatalf("Failed to register the Swarm service: %v", err) + } +} + +func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { + keyid := ctx.GlobalString(SwarmAccountFlag.Name) + if keyid == "" { + utils.Fatalf("Option %q is required", SwarmAccountFlag.Name) + } + // Try to load the arg as a hex key file. + if key, err := crypto.LoadECDSA(keyid); err == nil { + glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey)) + return key + } + // Otherwise try getting it from the keystore. + return decryptStoreAccount(stack.AccountManager(), keyid) +} + +func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey { + var a accounts.Account + var err error + if common.IsHexAddress(account) { + a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)}) + } else if ix, ixerr := strconv.Atoi(account); ixerr == nil { + a, err = accman.AccountByIndex(ix) + } else { + utils.Fatalf("Can't find swarm account key %s", account) + } + if err != nil { + utils.Fatalf("Can't find swarm account key: %v", err) + } + keyjson, err := ioutil.ReadFile(a.File) + if err != nil { + utils.Fatalf("Can't load swarm account key: %v", err) + } + for i := 1; i <= 3; i++ { + passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i)) + key, err := accounts.DecryptKey(keyjson, passphrase) + if err == nil { + return key.PrivateKey + } + } + utils.Fatalf("Can't decrypt swarm account key") + return nil +} + +func promptPassphrase(prompt string) string { + if prompt != "" { + fmt.Println(prompt) + } + password, err := console.Stdin.PromptPassword("Passphrase: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } + return password +} + +func injectBootnodes(srv *p2p.Server, nodes []string) { + for _, url := range nodes { + n, err := discover.ParseNode(url) + if err != nil { + glog.Errorf("invalid bootnode %q", err) + } + srv.AddPeer(n) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bzzhash/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bzzhash/main.go new file mode 100644 index 000000000..0ae99acc0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bzzhash/main.go @@ -0,0 +1,49 @@ +// 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 . + +// Command bzzhash computes a swarm tree hash. +package main + +import ( + "fmt" + "os" + "runtime" + + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + + if len(os.Args) < 2 { + fmt.Println("Usage: bzzhash ") + os.Exit(0) + } + f, err := os.Open(os.Args[1]) + if err != nil { + fmt.Println("Error opening file " + os.Args[1]) + os.Exit(1) + } + + stat, _ := f.Stat() + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + key, err := chunker.Split(f, stat.Size(), nil, nil, nil) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + } else { + fmt.Printf("%v\n", key) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bzzup/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bzzup/main.go new file mode 100644 index 000000000..7d251aadb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bzzup/main.go @@ -0,0 +1,161 @@ +// 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 . + +// Command bzzup uploads files to the swarm HTTP API. +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "mime" + "net/http" + "os" + "path/filepath" + "strings" +) + +func main() { + var ( + bzzapiFlag = flag.String("bzzapi", "http://127.0.0.1:8500", "Swarm HTTP endpoint") + recursiveFlag = flag.Bool("recursive", false, "Upload directories recursively") + manifestFlag = flag.Bool("manifest", true, "Skip automatic manifest upload") + ) + log.SetOutput(os.Stderr) + log.SetFlags(0) + flag.Parse() + if flag.NArg() != 1 { + log.Fatal("need filename as the first and only argument") + } + + var ( + file = flag.Arg(0) + client = &client{api: *bzzapiFlag} + mroot manifest + ) + fi, err := os.Stat(file) + if err != nil { + log.Fatal(err) + } + if fi.IsDir() { + if !*recursiveFlag { + log.Fatal("argument is a directory and recursive upload is disabled") + } + mroot, err = client.uploadDirectory(file) + } else { + mroot, err = client.uploadFile(file, fi) + if *manifestFlag { + // Wrap the raw file entry in a proper manifest so both hashes get printed. + mroot = manifest{Entries: []manifest{mroot}} + } + } + if err != nil { + log.Fatalln("upload failed:", err) + } + if *manifestFlag { + hash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + mroot.Hash = hash + } + + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) +} + +// client wraps interaction with the swarm HTTP gateway. +type client struct { + api string +} + +// manifest is the JSON representation of a swarm manifest. +type manifest struct { + Hash string `json:"hash,omitempty"` + ContentType string `json:"contentType,omitempty"` + Path string `json:"path,omitempty"` + Entries []manifest `json:"entries,omitempty"` +} + +func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) { + hash, err := c.uploadFileContent(file, fi) + m := manifest{ + Hash: hash, + ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), + } + return m, err +} + +func (c *client) uploadDirectory(dir string) (manifest, error) { + dirm := manifest{} + prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" + err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || fi.IsDir() { + return err + } + if !strings.HasPrefix(path, dir) { + return fmt.Errorf("path %s outside directory %s", path, dir) + } + entry, err := c.uploadFile(path, fi) + entry.Path = strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix) + dirm.Entries = append(dirm.Entries, entry) + return err + }) + return dirm, err +} + +func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { + fd, err := os.Open(file) + if err != nil { + return "", err + } + defer fd.Close() + log.Printf("uploading file %s (%d bytes)", file, fi.Size()) + return c.postRaw("application/octet-stream", fi.Size(), fd) +} + +func (c *client) uploadManifest(m manifest) (string, error) { + jsm, err := json.Marshal(m) + if err != nil { + panic(err) + } + log.Println("uploading manifest") + return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) +} + +func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { + req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) + if err != nil { + return "", err + } + req.Header.Set("content-type", mimetype) + req.ContentLength = size + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return "", fmt.Errorf("bad status: %s", resp.Status) + } + content, err := ioutil.ReadAll(resp.Body) + return string(content), err +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/code.txt b/vendor/github.com/ethereum/go-ethereum/cmd/evm/code.txt deleted file mode 100644 index a964ad9d2..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/code.txt +++ /dev/null @@ -1 +0,0 @@ -60006102ff5360003560001a60008114156103395760013560405260216040516020025990590160009052606052604051602002816060513760405160200281019050506002604051121561005957604051602002606051f35b604051602002599059016000905260a052600060c052604051602002599059016000905260e0526000610100526001610120525b604051610120511215610109576060515161012051602002606051015112156100d8576101205160200260605101516101005160200260e051015260016101005101610100526100f9565b61012051602002606051015160c05160200260a0510152600160c0510160c0525b600161012051016101205261008d565b60216020599059016000905260c051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260a0518260005b8381101561016657808301518186015260208101905061014b565b50505050825160200281019050604059905901600090526102405281610240515283602061024051015261024051905090509050905060c05160200280599059016000905281816020850151855160003060195a03f1508090509050905060a05260216020599059016000905261010051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260e0518260005b8381101561022557808301518186015260208101905061020a565b50505050825160200281019050604059905901600090526102c052816102c051528360206102c05101526102c05190509050905090506101005160200280599059016000905281816020850151855160003060195a03f1508090509050905060e05260405160200259905901600090526102e0526000610120525b610100516101205112156102d7576101205160200260e0510151610120516020026102e051015260016101205101610120526102a0565b60605151610100516020026102e05101526000610120525b60c05161012051121561032d576101205160200260a05101516101205160016101005101016020026102e051015260016101205101610120526102ef565b6040516020026102e051f35b50 diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/input.txt b/vendor/github.com/ethereum/go-ethereum/cmd/evm/input.txt deleted file mode 100644 index 1b6857ab9..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/input.txt +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000023000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002700000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003700000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004100000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000043000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000004700000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000049000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000004b000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000004d000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000004f0000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000053000000000000000000000000000000000000000000000000000000000000005400000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000056000000000000000000000000000000000000000000000000000000000000005700000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000059000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006100000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000063000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000066000000000000000000000000000000000000000000000000000000000000006700000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000069000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000006b000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000006f0000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007100000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000073000000000000000000000000000000000000000000000000000000000000007400000000000000000000000000000000000000000000000000000000000000750000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000007700000000000000000000000000000000000000000000000000000000000000780000000000000000000000000000000000000000000000000000000000000079000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000007d000000000000000000000000000000000000000000000000000000000000007e000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008100000000000000000000000000000000000000000000000000000000000000820000000000000000000000000000000000000000000000000000000000000083000000000000000000000000000000000000000000000000000000000000008400000000000000000000000000000000000000000000000000000000000000850000000000000000000000000000000000000000000000000000000000000086000000000000000000000000000000000000000000000000000000000000008700000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000000000000000000000000089000000000000000000000000000000000000000000000000000000000000008a000000000000000000000000000000000000000000000000000000000000008b000000000000000000000000000000000000000000000000000000000000008c000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000008f0000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009100000000000000000000000000000000000000000000000000000000000000920000000000000000000000000000000000000000000000000000000000000093000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000950000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000009700000000000000000000000000000000000000000000000000000000000000980000000000000000000000000000000000000000000000000000000000000099000000000000000000000000000000000000000000000000000000000000009a000000000000000000000000000000000000000000000000000000000000009b000000000000000000000000000000000000000000000000000000000000009c000000000000000000000000000000000000000000000000000000000000009d000000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000009f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000a100000000000000000000000000000000000000000000000000000000000000a200000000000000000000000000000000000000000000000000000000000000a300000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000a500000000000000000000000000000000000000000000000000000000000000a600000000000000000000000000000000000000000000000000000000000000a700000000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000000000000000a900000000000000000000000000000000000000000000000000000000000000aa00000000000000000000000000000000000000000000000000000000000000ab00000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000ad00000000000000000000000000000000000000000000000000000000000000ae00000000000000000000000000000000000000000000000000000000000000af00000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000b100000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000b300000000000000000000000000000000000000000000000000000000000000b400000000000000000000000000000000000000000000000000000000000000b500000000000000000000000000000000000000000000000000000000000000b600000000000000000000000000000000000000000000000000000000000000b700000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000b900000000000000000000000000000000000000000000000000000000000000ba00000000000000000000000000000000000000000000000000000000000000bb00000000000000000000000000000000000000000000000000000000000000bc00000000000000000000000000000000000000000000000000000000000000bd00000000000000000000000000000000000000000000000000000000000000be00000000000000000000000000000000000000000000000000000000000000bf00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c100000000000000000000000000000000000000000000000000000000000000c200000000000000000000000000000000000000000000000000000000000000c300000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c500000000000000000000000000000000000000000000000000000000000000c600000000000000000000000000000000000000000000000000000000000000c7 diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go index aa48f6ede..2c4329fa5 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go @@ -19,6 +19,7 @@ package main import ( "fmt" + "io/ioutil" "math/big" "os" "runtime" @@ -30,13 +31,18 @@ import ( "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/glog" + "github.com/ethereum/go-ethereum/params" "gopkg.in/urfave/cli.v1" ) +var gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags) + var ( - app *cli.App + app = utils.NewApp(gitCommit, "the evm command line interface") + DebugFlag = cli.BoolFlag{ Name: "debug", Usage: "output full trace logs", @@ -53,6 +59,10 @@ var ( Name: "code", Usage: "EVM code", } + CodeFileFlag = cli.StringFlag{ + Name: "codefile", + Usage: "file containing EVM code", + } GasFlag = cli.StringFlag{ Name: "gas", Usage: "gas limit for the evm", @@ -91,7 +101,6 @@ var ( ) func init() { - app = utils.NewApp("0.2", "the evm command line interface") app.Flags = []cli.Flag{ CreateFlag, DebugFlag, @@ -100,6 +109,7 @@ func init() { DisableJitFlag, SysStatFlag, CodeFlag, + CodeFileFlag, GasFlag, PriceFlag, ValueFlag, @@ -117,21 +127,47 @@ func run(ctx *cli.Context) error { statedb, _ := state.New(common.Hash{}, db) sender := statedb.CreateAccount(common.StringToAddress("sender")) + 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 ( - ret []byte - err error + code []byte + ret []byte + err error ) + if ctx.GlobalString(CodeFlag.Name) != "" { + code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) + } else { + var hexcode []byte + if ctx.GlobalString(CodeFileFlag.Name) != "" { + var err error + hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name)) + if err != nil { + fmt.Printf("Could not load code from file: %v\n", err) + os.Exit(1) + } + } else { + var err error + hexcode, err = ioutil.ReadAll(os.Stdin) + if err != nil { + fmt.Printf("Could not load code from stdin: %v\n", err) + os.Exit(1) + } + } + code = common.Hex2Bytes(string(hexcode[:])) + } + if ctx.GlobalBool(CreateFlag.Name) { - input := append(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) + input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) ret, _, err = vmenv.Create( sender, input, @@ -141,7 +177,8 @@ func run(ctx *cli.Context) error { ) } else { receiver := statedb.CreateAccount(common.StringToAddress("receiver")) - receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) + + receiver.SetCode(crypto.Keccak256Hash(code), code) ret, err = vmenv.Call( sender, receiver.Address(), @@ -154,10 +191,10 @@ func run(ctx *cli.Context) error { vmdone := time.Since(tstart) if ctx.GlobalBool(DumpFlag.Name) { - statedb.Commit() + statedb.Commit(true) fmt.Println(string(statedb.Dump())) } - vm.StdErrFormat(vmenv.StructLogs()) + vm.StdErrFormat(logger.StructLogs()) if ctx.GlobalBool(SysStatFlag.Name) { var mem runtime.MemStats @@ -209,45 +246,41 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg value: value, time: big.NewInt(time.Now().Unix()), } - cfg.Logger.Collector = env env.evm = vm.New(env, cfg) return env } -// ruleSet implements vm.RuleSet and will always default to the homestead rule set. +// 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) RuleSet() vm.RuleSet { return ruleSet{} } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() } -func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) } -func (self *VMEnv) Origin() common.Address { return *self.transactor } -func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 } -func (self *VMEnv) Coinbase() common.Address { return *self.transactor } -func (self *VMEnv) Time() *big.Int { return self.time } -func (self *VMEnv) Difficulty() *big.Int { return common.Big1 } -func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } -func (self *VMEnv) Value() *big.Int { return self.value } -func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } -func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } -func (self *VMEnv) Depth() int { return 0 } -func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) 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) AddStructLog(log vm.StructLog) { - self.logs = append(self.logs, log) -} -func (self *VMEnv) StructLogs() []vm.StructLog { - return self.logs -} func (self *VMEnv) AddLog(log *vm.Log) { self.state.AddLog(log) } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go index b0a45dd41..6d489b123 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go @@ -31,30 +31,37 @@ import ( var ( walletCommand = cli.Command{ - Name: "wallet", - Usage: "ethereum presale wallet", - Subcommands: []cli.Command{ - { - Action: importWallet, - Name: "import", - Usage: "import ethereum presale wallet", - }, - }, + Name: "wallet", + Usage: "Manage Ethereum presale wallets", + ArgsUsage: "", + Category: "ACCOUNT COMMANDS", Description: ` - - get wallet import /path/to/my/presale.wallet + geth wallet import /path/to/my/presale.wallet will prompt for your password and imports your ether presale account. It can be used non-interactively with the --password option taking a passwordfile as argument containing the wallet password in plaintext. -`} +`, + Subcommands: []cli.Command{ + { + Action: importWallet, + Name: "import", + Usage: "Import Ethereum presale wallet", + ArgsUsage: "", + Description: ` +TODO: Please write this +`, + }, + }, + } accountCommand = cli.Command{ - Action: accountList, - Name: "account", - Usage: "manage accounts", + Action: accountList, + Name: "account", + Usage: "Manage accounts", + ArgsUsage: "", + Category: "ACCOUNT COMMANDS", Description: ` - Manage accounts lets you create new accounts, list all existing accounts, import a private key into a new account. @@ -86,17 +93,21 @@ And finally. DO NOT FORGET YOUR PASSWORD. `, Subcommands: []cli.Command{ { - Action: accountList, - Name: "list", - Usage: "print account addresses", + Action: accountList, + Name: "list", + Usage: "Print account addresses", + ArgsUsage: " ", + Description: ` +TODO: Please write this +`, }, { - Action: accountCreate, - Name: "new", - Usage: "create a new account", + Action: accountCreate, + Name: "new", + Usage: "Create a new account", + ArgsUsage: " ", Description: ` - - ethereum account new + geth account new Creates a new account. Prints the address. @@ -106,19 +117,19 @@ You must remember this passphrase to unlock your account in the future. For non-interactive use the passphrase can be specified with the --password flag: - ethereum --password account new + geth --password account new Note, this is meant to be used for testing only, it is a bad idea to save your password to file or expose in any other way. - `, +`, }, { - Action: accountUpdate, - Name: "update", - Usage: "update an existing account", + Action: accountUpdate, + Name: "update", + Usage: "Update an existing account", + ArgsUsage: "
", Description: ` - - ethereum account update
+ geth account update
Update an existing account. @@ -130,19 +141,19 @@ format to the newest format or change the password for an account. For non-interactive use the passphrase can be specified with the --password flag: - ethereum --password account update
+ geth --password account update
Since only one password can be given, only format update can be performed, changing your password is only possible interactively. - `, +`, }, { - Action: accountImport, - Name: "import", - Usage: "import a private key into a new account", + Action: accountImport, + Name: "import", + Usage: "Import a private key into a new account", + ArgsUsage: "", Description: ` - - ethereum account import + geth account import Imports an unencrypted private key from and creates a new account. Prints the address. @@ -155,20 +166,20 @@ You must remember this passphrase to unlock your account in the future. For non-interactive use the passphrase can be specified with the -password flag: - ethereum --password account import + geth --password account import Note: As you can directly copy your encrypted accounts to another ethereum instance, this import mechanism is not needed when you transfer an account between nodes. - `, +`, }, }, } ) func accountList(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) for i, acct := range stack.AccountManager().Accounts() { fmt.Printf("Account #%d: {%x} %s\n", i, acct.Address, acct.File) } @@ -278,7 +289,7 @@ func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrErro // accountCreate creates a new account into the keystore defined by the CLI flags. func accountCreate(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) whisper := getWhisperYesNo("You can also choose to enable your new account as a Whisper identity.") @@ -297,7 +308,7 @@ func accountUpdate(ctx *cli.Context) error { if len(ctx.Args()) == 0 { utils.Fatalf("No accounts specified to update") } - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) account, oldPassword := unlockAccount(ctx, stack.AccountManager(), ctx.Args().First(), 0, nil) newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil) if err := stack.AccountManager().Update(account, oldPassword, newPassword); err != nil { @@ -316,7 +327,7 @@ func importWallet(ctx *cli.Context) error { utils.Fatalf("Could not read wallet file: %v", err) } - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx)) acct, err := stack.AccountManager().ImportPreSaleKey(keyJson, passphrase) if err != nil { @@ -335,7 +346,7 @@ func accountImport(ctx *cli.Context) error { if err != nil { utils.Fatalf("Failed to load the private key: %v", err) } - stack := utils.MakeNode(ctx, clientIdentifier, verString) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) acct, err := stack.AccountManager().ImportECDSA(key, passphrase) if err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go index fe63dfcca..1a7a9eb18 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go @@ -20,7 +20,9 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strconv" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/cmd/utils" @@ -31,40 +33,61 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/trie" + "github.com/syndtr/goleveldb/leveldb/util" "gopkg.in/urfave/cli.v1" ) var ( importCommand = cli.Command{ - Action: importChain, - Name: "import", - Usage: `import a blockchain file`, + Action: importChain, + Name: "import", + Usage: "Import a blockchain file", + ArgsUsage: "", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, } exportCommand = cli.Command{ - Action: exportChain, - Name: "export", - Usage: `export blockchain into file`, + Action: exportChain, + Name: "export", + Usage: "Export blockchain into file", + ArgsUsage: " [ ]", + Category: "BLOCKCHAIN COMMANDS", Description: ` Requires a first argument of the file to write to. Optional second and third arguments control the first and last block to write. In this mode, the file will be appended if already existing. - `, +`, } upgradedbCommand = cli.Command{ - Action: upgradeDB, - Name: "upgradedb", - Usage: "upgrade chainblock database", + Action: upgradeDB, + Name: "upgradedb", + Usage: "Upgrade chainblock database", + ArgsUsage: " ", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, } removedbCommand = cli.Command{ - Action: removeDB, - Name: "removedb", - Usage: "Remove blockchain and state databases", + Action: removeDB, + Name: "removedb", + Usage: "Remove blockchain and state databases", + ArgsUsage: " ", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, } dumpCommand = cli.Command{ - Action: dump, - Name: "dump", - Usage: `dump a specific block from storage`, + Action: dump, + Name: "dump", + Usage: "Dump a specific block from storage", + ArgsUsage: "[ | ]...", + Category: "BLOCKCHAIN COMMANDS", Description: ` The arguments are interpreted as block numbers or hashes. Use "ethereum dump 0" to dump the genesis block. @@ -76,14 +99,66 @@ func importChain(ctx *cli.Context) error { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") } - chain, chainDb := utils.MakeChain(ctx) + stack := makeFullNode(ctx) + chain, chainDb := utils.MakeChain(ctx, stack) + defer chainDb.Close() + + // Start periodically gathering memory profiles + var peakMemAlloc, peakMemSys uint64 + go func() { + stats := new(runtime.MemStats) + for { + runtime.ReadMemStats(stats) + if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { + atomic.StoreUint64(&peakMemAlloc, stats.Alloc) + } + if atomic.LoadUint64(&peakMemSys) < stats.Sys { + atomic.StoreUint64(&peakMemSys, stats.Sys) + } + time.Sleep(5 * time.Second) + } + }() + // Import the chain start := time.Now() - err := utils.ImportChain(chain, ctx.Args().First()) - chainDb.Close() - if err != nil { + if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { utils.Fatalf("Import error: %v", err) } - fmt.Printf("Import done in %v", time.Since(start)) + fmt.Printf("Import done in %v.\n\n", time.Since(start)) + + // Output pre-compaction stats mostly to see the import trashing + db := chainDb.(*ethdb.LDBDatabase) + + stats, err := db.LDB().GetProperty("leveldb.stats") + if err != nil { + utils.Fatalf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) + fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) + + // Print the memory statistics used by the importing + mem := new(runtime.MemStats) + runtime.ReadMemStats(mem) + + fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) + fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) + fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) + fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) + + // Compact the entire database to more accurately measure disk io and print the stats + start = time.Now() + fmt.Println("Compacting entire database...") + if err = db.LDB().CompactRange(util.Range{}); err != nil { + utils.Fatalf("Compaction failed: %v", err) + } + fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) + + stats, err = db.LDB().GetProperty("leveldb.stats") + if err != nil { + utils.Fatalf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + return nil } @@ -91,7 +166,8 @@ func exportChain(ctx *cli.Context) error { if len(ctx.Args()) < 1 { utils.Fatalf("This command requires an argument.") } - chain, _ := utils.MakeChain(ctx) + stack := makeFullNode(ctx) + chain, _ := utils.MakeChain(ctx, stack) start := time.Now() var err error @@ -119,20 +195,25 @@ func exportChain(ctx *cli.Context) error { } func removeDB(ctx *cli.Context) error { - confirm, err := console.Stdin.PromptConfirm("Remove local database?") - if err != nil { - utils.Fatalf("%v", err) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + dbdir := stack.ResolvePath(utils.ChainDbName(ctx)) + if !common.FileExist(dbdir) { + fmt.Println(dbdir, "does not exist") + return nil } - if confirm { - fmt.Println("Removing chaindata...") - start := time.Now() - - os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), utils.ChainDbName(ctx))) - - fmt.Printf("Removed in %v\n", time.Since(start)) - } else { + fmt.Println(dbdir) + confirm, err := console.Stdin.PromptConfirm("Remove this database?") + switch { + case err != nil: + utils.Fatalf("%v", err) + case !confirm: fmt.Println("Operation aborted") + default: + fmt.Println("Removing...") + start := time.Now() + os.RemoveAll(dbdir) + fmt.Printf("Removed in %v\n", time.Since(start)) } return nil } @@ -140,7 +221,8 @@ func removeDB(ctx *cli.Context) error { func upgradeDB(ctx *cli.Context) error { glog.Infoln("Upgrading blockchain database") - chain, chainDb := utils.MakeChain(ctx) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + chain, chainDb := utils.MakeChain(ctx, stack) bcVersion := core.GetBlockChainVersion(chainDb) if bcVersion == 0 { bcVersion = core.BlockChainVersion @@ -153,10 +235,12 @@ func upgradeDB(ctx *cli.Context) error { utils.Fatalf("Unable to export chain for reimport %s", err) } chainDb.Close() - os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), utils.ChainDbName(ctx))) + if dir := dbDirectory(chainDb); dir != "" { + os.RemoveAll(dir) + } // Import the chain file. - chain, chainDb = utils.MakeChain(ctx) + chain, chainDb = utils.MakeChain(ctx, stack) core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) err := utils.ImportChain(chain, exportFile) chainDb.Close() @@ -169,8 +253,17 @@ func upgradeDB(ctx *cli.Context) error { return nil } +func dbDirectory(db ethdb.Database) string { + ldb, ok := db.(*ethdb.LDBDatabase) + if !ok { + return "" + } + return ldb.Path() +} + func dump(ctx *cli.Context) error { - chain, chainDb := utils.MakeChain(ctx) + stack := makeFullNode(ctx) + chain, chainDb := utils.MakeChain(ctx, stack) for _, arg := range ctx.Args() { var block *types.Block if hashish(arg) { diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go index c3becc844..b1c435e00 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go @@ -30,9 +30,11 @@ import ( var ( consoleCommand = cli.Command{ - Action: localConsole, - Name: "console", - Usage: `Geth Console: interactive JavaScript environment`, + Action: localConsole, + Name: "console", + Usage: "Start an interactive JavaScript environment", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. @@ -40,20 +42,24 @@ See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console `, } attachCommand = cli.Command{ - Action: remoteConsole, - Name: "attach", - Usage: `Geth Console: interactive JavaScript environment (connect to node)`, + Action: remoteConsole, + Name: "attach", + Usage: "Start an interactive JavaScript environment (connect to node)", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. This command allows to open a console on a running geth node. - `, +`, } javascriptCommand = cli.Command{ - Action: ephemeralConsole, - Name: "js", - Usage: `executes the given JavaScript files in the Geth JavaScript VM`, + Action: ephemeralConsole, + Name: "js", + Usage: "Execute the specified JavaScript files", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", Description: ` The JavaScript VM exposes a node admin interface as well as the Ðapp JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console @@ -70,13 +76,10 @@ func localConsole(ctx *cli.Context) error { defer node.Stop() // Attach to the newly started node and start the JavaScript console - client := rpc.NewClientRestartWrapper(func() *rpc.Client { - client, err := node.Attach() - if err != nil { - utils.Fatalf("Failed to attach to the inproc geth: %v", err) - } - return client - }) + client, err := node.Attach() + if err != nil { + utils.Fatalf("Failed to attach to the inproc geth: %v", err) + } config := console.Config{ DataDir: node.DataDir(), DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), @@ -105,16 +108,12 @@ func localConsole(ctx *cli.Context) error { // console to it. func remoteConsole(ctx *cli.Context) error { // Attach to a remotely running geth instance and start the JavaScript console - client := rpc.NewClientRestartWrapper(func() *rpc.Client { - client, err := dialRPC(ctx.Args().First()) - if err != nil { - utils.Fatalf("Unable to attach to remote geth: %v", err) - } - return client - }) - + client, err := dialRPC(ctx.Args().First()) + if err != nil { + utils.Fatalf("Unable to attach to remote geth: %v", err) + } config := console.Config{ - DataDir: utils.MustMakeDataDir(ctx), + DataDir: utils.MakeDataDir(ctx), DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), Client: client, Preload: utils.MakeConsolePreloads(ctx), @@ -142,7 +141,7 @@ func remoteConsole(ctx *cli.Context) error { // for "geth attach" and "geth monitor" with no argument. func dialRPC(endpoint string) (*rpc.Client, error) { if endpoint == "" { - endpoint = node.DefaultIPCEndpoint() + endpoint = node.DefaultIPCEndpoint(clientIdentifier) } else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") { // Backwards compatibility with geth < 1.5 which required // these prefixes. @@ -161,14 +160,10 @@ func ephemeralConsole(ctx *cli.Context) error { defer node.Stop() // Attach to the newly started node and start the JavaScript console - client := rpc.NewClientRestartWrapper(func() *rpc.Client { - client, err := node.Attach() - if err != nil { - utils.Fatalf("Failed to attach to the inproc geth: %v", err) - } - return client - }) - + client, err := node.Attach() + if err != nil { + utils.Fatalf("Failed to attach to the inproc geth: %v", err) + } config := console.Config{ DataDir: node.DataDir(), DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/info_test.json b/vendor/github.com/ethereum/go-ethereum/cmd/geth/info_test.json deleted file mode 100644 index 63f2163a9..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/info_test.json +++ /dev/null @@ -1 +0,0 @@ -{"code":"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.23","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go index 3ca79915f..b6928a1c8 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go @@ -32,61 +32,35 @@ import ( "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/ethdb" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/release" - "github.com/ethereum/go-ethereum/rlp" "gopkg.in/urfave/cli.v1" ) const ( - clientIdentifier = "Geth" // Client identifier to advertise over the network - versionMajor = 1 // Major version component of the current release - versionMinor = 5 // Minor version component of the current release - versionPatch = 0 // Patch version component of the current release - versionMeta = "unstable" // Version metadata to append to the version string - - versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle + clientIdentifier = "geth" // Client identifier to advertise over the network ) var ( - gitCommit string // Git SHA1 commit hash of the release (set via linker flags) - verString string // Combined textual representation of all the version components - relConfig release.Config // Structured version information and release oracle config - app *cli.App + // Git SHA1 commit hash of the release (set via linker flags) + gitCommit = "" + // Ethereum address of the Geth release oracle. + relOracle = common.HexToAddress("0xfa7b9770ca4cb04296cac84f37736d4041251cdf") + // The app that holds all commands and flags. + app = utils.NewApp(gitCommit, "the go-ethereum command line interface") ) func init() { - // Construct the textual version string from the individual components - verString = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch) - if versionMeta != "" { - verString += "-" + versionMeta - } - if gitCommit != "" { - verString += "-" + gitCommit[:8] - } - // Construct the version release oracle configuration - relConfig.Oracle = common.HexToAddress(versionOracle) - - relConfig.Major = uint32(versionMajor) - relConfig.Minor = uint32(versionMinor) - relConfig.Patch = uint32(versionPatch) - - commit, _ := hex.DecodeString(gitCommit) - copy(relConfig.Commit[:], commit) - // Initialize the CLI app and start Geth - app = utils.NewApp(verString, "the go-ethereum command line interface") app.Action = geth app.HideVersion = true // we have a command to print the version + app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" app.Commands = []cli.Command{ importCommand, exportCommand, @@ -100,9 +74,11 @@ func init() { attachCommand, javascriptCommand, { - Action: makedag, - Name: "makedag", - Usage: "generate ethash dag (for testing)", + Action: makedag, + Name: "makedag", + Usage: "Generate ethash DAG (for testing)", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", Description: ` The makedag command generates an ethash DAG in /tmp/dag. @@ -111,39 +87,34 @@ Regular users do not need to execute it. `, }, { - Action: gpuinfo, - Name: "gpuinfo", - Usage: "gpuinfo", - Description: ` -Prints OpenCL device info for all found GPUs. -`, - }, - { - Action: gpubench, - Name: "gpubench", - Usage: "benchmark GPU", - Description: ` -Runs quick benchmark on first GPU found. -`, - }, - { - Action: version, - Name: "version", - Usage: "print ethereum version numbers", + 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: "bootstraps and initialises a new genesis block (JSON)", + Action: initGenesis, + Name: "init", + Usage: "Bootstrap and initialize a new genesis block", + ArgsUsage: "", + Category: "BLOCKCHAIN COMMANDS", Description: ` -The init command initialises a new genesis block and definition for the network. +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", + }, } app.Flags = []cli.Flag{ @@ -153,15 +124,14 @@ participating. utils.BootnodesFlag, utils.DataDirFlag, utils.KeyStoreDirFlag, - utils.BlockchainVersionFlag, utils.OlympicFlag, utils.FastSyncFlag, utils.LightModeFlag, - utils.NoDefSrvFlag, utils.LightServFlag, utils.LightPeersFlag, - utils.CacheFlag, utils.LightKDFFlag, + utils.CacheFlag, + utils.TrieCacheGenFlag, utils.JSpathFlag, utils.ListenPortFlag, utils.MaxPeersFlag, @@ -172,12 +142,12 @@ participating. utils.OpposeDAOFork, utils.MinerThreadsFlag, utils.MiningEnabledFlag, - utils.MiningGPUFlag, utils.AutoDAGFlag, utils.TargetGasLimitFlag, utils.NATFlag, utils.NatspecEnabledFlag, utils.NoDiscoverFlag, + utils.DiscoveryV5Flag, utils.NoEthFlag, utils.NodeKeyFileFlag, utils.NodeKeyHexFlag, @@ -267,17 +237,15 @@ func initGenesis(ctx *cli.Context) error { utils.Fatalf("must supply path to genesis JSON file") } - chainDb, err := ethdb.NewLDBDatabase(filepath.Join(utils.MustMakeDataDir(ctx), utils.ChainDbName(ctx)), 0, 0) - if err != nil { - utils.Fatalf("could not open database: %v", err) - } + 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) + block, err := core.WriteGenesisBlock(chaindb, genesisFile) if err != nil { utils.Fatalf("failed to write genesis block: %v", err) } @@ -286,70 +254,41 @@ func initGenesis(ctx *cli.Context) error { } func makeFullNode(ctx *cli.Context) *node.Node { - node := utils.MakeNode(ctx, clientIdentifier, verString) - utils.RegisterEthService(ctx, node, relConfig, makeDefaultExtra()) + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + utils.RegisterEthService(ctx, stack, utils.MakeDefaultExtraData(clientIdentifier)) + // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name) shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name) if shhEnabled || shhAutoEnabled { - utils.RegisterShhService(node) - } - return node -} - -func makeDefaultExtra() []byte { - var clientInfo = struct { - Version uint - Name string - GoVersion string - Os string - }{uint(versionMajor<<16 | versionMinor<<8 | versionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} - extra, err := rlp.EncodeToBytes(clientInfo) - if err != nil { - glog.V(logger.Warn).Infoln("error setting canonical miner information:", err) + utils.RegisterShhService(stack) } - if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() { - glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize) - glog.V(logger.Debug).Infof("extra: %x\n", extra) - return nil + // 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), + } + commit, _ := hex.DecodeString(gitCommit) + copy(config.Commit[:], commit) + return release.NewReleaseService(ctx, config) + }); err != nil { + utils.Fatalf("Failed to register the Geth release oracle service: %v", err) } - return extra + + return stack } // startNode boots up the system node and all registered protocols, after which // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the // miner. func startNode(ctx *cli.Context, stack *node.Node) { - // Report geth version - glog.V(logger.Info).Infof("instance: Geth/%s/%s/%s\n", verString, runtime.Version(), runtime.GOOS) // Start up the node itself utils.StartNode(stack) - if ctx.GlobalBool(utils.LightModeFlag.Name) && !ctx.GlobalBool(utils.NoDefSrvFlag.Name) { - // add default light server; test phase only - addPeer := func(url string) { - node, err := discover.ParseNode(url) - if err == nil { - stack.Server().AddPeer(node) - } - } - - if ctx.GlobalBool(utils.OpposeDAOFork.Name) { - // Classic (Azure) - addPeer("enode://fc3d7b57e5d317946bf421411632ec98d5ffcbf94548cd7bc10088e4fef176670f8ec70280d301a9d0b22fe498203f62b323da15b3acc18b02a1fee2a06b7d3f@40.118.3.223:30305") - } else { - // MainNet (Azure) - addPeer("enode://feaf206a308a669a789be45f4dadcb351246051727f12415ad69e44f8080daf0569c10fe1d9944d245dd1f3e1c89cedda8ce03d7e3d5ed8975a35cad4b4f7ec1@40.118.3.223:30303") - // MainNet (John Gerryts @phonikg) - addPeer("enode://3cbd26f73513af0e789c55ea9efa6d259be2d5f6882bdb52740e21e01379287b652642a87207f1bc07c64aae3ab51ab566dede7588d6064022d40577fe59d5de@50.112.52.169:30300") - } - if ctx.GlobalBool(utils.TestNetFlag.Name) { - // TestNet (John Gerryts @phonikg) - addPeer("enode://7d00e8c27b2328e2008a9fc86e81afba22681fdac675b99805fa62cc29ee8a2a9d83f916f7661da6a6bd78155a430bb2bd7cec733ca9e700e236ec9c71d97e24@50.112.52.169:30301") - } - } - // Unlock any account specifically requested accman := stack.AccountManager() passwords := utils.MakePasswordList(ctx) @@ -365,7 +304,7 @@ func startNode(ctx *cli.Context, stack *node.Node) { if err := stack.Service(ðereum); err != nil { utils.Fatalf("ethereum service not running: %v", err) } - if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil { + if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil { utils.Fatalf("Failed to start mining: %v", err) } } @@ -401,40 +340,34 @@ func makedag(ctx *cli.Context) error { return nil } -func gpuinfo(ctx *cli.Context) error { - eth.PrintOpenCLDevices() - return nil -} - -func gpubench(ctx *cli.Context) error { - args := ctx.Args() - wrongArgs := func() { - utils.Fatalf(`Usage: geth gpubench `) +func version(ctx *cli.Context) error { + fmt.Println(strings.Title(clientIdentifier)) + fmt.Println("Version:", utils.Version) + if gitCommit != "" { + fmt.Println("Git Commit:", gitCommit) } - switch { - case len(args) == 1: - n, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { - wrongArgs() - } - eth.GPUBench(n) - case len(args) == 0: - eth.GPUBench(0) - default: - wrongArgs() - } - return nil -} - -func version(c *cli.Context) error { - fmt.Println(clientIdentifier) - fmt.Println("Version:", verString) fmt.Println("Protocol Versions:", eth.ProtocolVersions) - fmt.Println("Network Id:", c.GlobalInt(utils.NetworkIdFlag.Name)) + fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name)) fmt.Println("Go Version:", runtime.Version()) fmt.Println("OS:", runtime.GOOS) fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) - + return nil +} + +func license(_ *cli.Context) error { + fmt.Println(`Geth is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Geth is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with geth. If not, see . +`) return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go index d1490dce2..03a124494 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go @@ -35,7 +35,7 @@ import ( var ( monitorCommandAttachFlag = cli.StringFlag{ Name: "attach", - Value: node.DefaultIPCEndpoint(), + Value: node.DefaultIPCEndpoint(clientIdentifier), Usage: "API endpoint to attach to", } monitorCommandRowsFlag = cli.IntFlag{ @@ -49,9 +49,11 @@ var ( Usage: "Refresh interval in seconds", } monitorCommand = cli.Command{ - Action: monitor, - Name: "monitor", - Usage: `Geth Monitor: node metrics monitoring and visualization`, + Action: monitor, + Name: "monitor", + Usage: "Monitor and visualize node metrics", + ArgsUsage: " ", + Category: "MONITOR COMMANDS", Description: ` The Geth monitor is a tool to collect and visualize various internal metrics gathered by the node, supporting different chart types as well as the capacity diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go index 9d1b5c19a..5e52040d5 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go @@ -30,6 +30,8 @@ import ( var AppHelpTemplate = `NAME: {{.App.Name}} - {{.App.Usage}} + Copyright 2013-2016 The go-ethereum Authors + USAGE: {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} {{if .App.Version}} @@ -71,12 +73,16 @@ var AppHelpFlagGroups = []flagGroup{ utils.IdentityFlag, utils.FastSyncFlag, utils.LightModeFlag, - utils.NoDefSrvFlag, utils.LightServFlag, utils.LightPeersFlag, utils.LightKDFFlag, + }, + }, + { + Name: "PERFORMANCE TUNING", + Flags: []cli.Flag{ utils.CacheFlag, - utils.BlockchainVersionFlag, + utils.TrieCacheGenFlag, }, }, { @@ -116,6 +122,7 @@ var AppHelpFlagGroups = []flagGroup{ utils.MaxPendingPeersFlag, utils.NATFlag, utils.NoDiscoverFlag, + utils.DiscoveryV5Flag, utils.NodeKeyFileFlag, utils.NodeKeyHexFlag, }, @@ -125,7 +132,6 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ utils.MiningEnabledFlag, utils.MinerThreadsFlag, - utils.MiningGPUFlag, utils.AutoDAGFlag, utils.EtherbaseFlag, utils.TargetGasLimitFlag, diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go index d267dbf58..850bf8eb2 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go @@ -23,8 +23,6 @@ import ( "os" "os/signal" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" @@ -32,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/tests" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" ) const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" @@ -88,12 +86,12 @@ func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) { // Create a networkless protocol stack stack, err := node.New(&node.Config{ UseLightweightKDF: true, - IPCPath: node.DefaultIPCEndpoint(), - HTTPHost: common.DefaultHTTPHost, - HTTPPort: common.DefaultHTTPPort, + IPCPath: node.DefaultIPCEndpoint(""), + HTTPHost: node.DefaultHTTPHost, + HTTPPort: node.DefaultHTTPPort, HTTPModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"}, - WSHost: common.DefaultWSHost, - WSPort: common.DefaultWSPort, + WSHost: node.DefaultWSHost, + WSPort: node.DefaultWSPort, WSModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"}, NoDiscovery: true, }) @@ -123,7 +121,7 @@ func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) { ethConf := ð.Config{ TestGenesisState: db, TestGenesisBlock: test.Genesis, - ChainConfig: &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}, + ChainConfig: ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}, } if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { return nil, err diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/bootnodes.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/bootnodes.go deleted file mode 100644 index fbbaa1f22..000000000 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/bootnodes.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package utils - -import "github.com/ethereum/go-ethereum/p2p/discover" - -// FrontierBootNodes are the enode URLs of the P2P bootstrap nodes running on -// the Frontier network. -var FrontierBootNodes = []*discover.Node{ - // ETH/DEV Go Bootnodes - discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE - discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR - discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG - - // ETH/DEV Cpp Bootnodes - discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"), -} - -// TestNetBootNodes are the enode URLs of the P2P bootstrap nodes running on the -// Morden test network. -var TestNetBootNodes = []*discover.Node{ - // ETH/DEV Go Bootnodes - discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"), - discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"), - - // ETH/DEV Cpp Bootnodes -} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go index 3b521a0e1..584afc804 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go @@ -23,6 +23,7 @@ import ( "os" "os/signal" "regexp" + "runtime" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -52,10 +53,16 @@ func openLogFile(Datadir string, filename string) *os.File { // is redirected to a different file. func Fatalf(format string, args ...interface{}) { 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 + if runtime.GOOS == "windows" { + // The SameFile check below doesn't work on Windows. + // stdout is unlikely to get redirected though, so just print there. + w = os.Stdout + } else { + outf, _ := os.Stdout.Stat() + errf, _ := os.Stderr.Stat() + if outf != nil && errf != nil && os.SameFile(outf, errf) { + w = os.Stderr + } } fmt.Fprintf(w, "Fatal: "+format+"\n", args...) logger.Flush() diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go index 5cbccfe98..11c92d451 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go @@ -137,9 +137,19 @@ func (self *DirectoryFlag) Set(value string) { // Note, it has limitations, e.g. ~someuser/tmp will not be expanded func expandPath(p string) string { if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { - if user, err := user.Current(); err == nil { - p = user.HomeDir + p[1:] + if home := homeDir(); home != "" { + p = home + p[1:] } } return path.Clean(os.ExpandEnv(p)) } + +func homeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go index 9d2a23abf..3b4863e2b 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go @@ -22,13 +22,11 @@ import ( "io/ioutil" "math" "math/big" - "math/rand" "os" "path/filepath" "runtime" "strconv" "strings" - "time" "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" @@ -41,18 +39,17 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/les" - "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" - "github.com/ethereum/go-ethereum/release" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/whisper" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "gopkg.in/urfave/cli.v1" ) @@ -83,13 +80,16 @@ OPTIONS: } // NewApp creates an app with sane defaults. -func NewApp(version, usage string) *cli.App { +func NewApp(gitCommit, usage string) *cli.App { app := cli.NewApp() app.Name = filepath.Base(os.Args[0]) app.Author = "" //app.Authors = nil app.Email = "" - app.Version = version + app.Version = Version + if gitCommit != "" { + app.Version += "-" + gitCommit[:8] + } app.Usage = usage return app } @@ -106,7 +106,7 @@ var ( DataDirFlag = DirectoryFlag{ Name: "datadir", Usage: "Data directory for the databases and keystore", - Value: DirectoryString{common.DefaultDataDir()}, + Value: DirectoryString{node.DefaultDataDir()}, } KeyStoreDirFlag = DirectoryFlag{ Name: "keystore", @@ -140,17 +140,7 @@ var ( DocRootFlag = DirectoryFlag{ Name: "docroot", Usage: "Document Root for HTTPClient file scheme", - Value: DirectoryString{common.HomeDir()}, - } - CacheFlag = cli.IntFlag{ - Name: "cache", - Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", - Value: 128, - } - BlockchainVersionFlag = cli.IntFlag{ - Name: "blockchainversion", - Usage: "Blockchain version (integer)", - Value: core.BlockChainVersion, + Value: DirectoryString{homeDir()}, } FastSyncFlag = cli.BoolFlag{ Name: "fast", @@ -160,24 +150,31 @@ var ( Name: "light", Usage: "Enable light client mode", } - NoDefSrvFlag = cli.BoolFlag{ - Name: "nodefsrv", - Usage: "Don't add default LES server (only for test version)", - } LightServFlag = cli.IntFlag{ Name: "lightserv", Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", - Value: 20, + Value: 0, } LightPeersFlag = cli.IntFlag{ Name: "lightpeers", Usage: "Maximum number of LES client peers", - Value: 10, + Value: 20, } LightKDFFlag = cli.BoolFlag{ Name: "lightkdf", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", } + // Performance tuning settings + CacheFlag = cli.IntFlag{ + Name: "cache", + Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", + Value: 128, + } + TrieCacheGenFlag = cli.IntFlag{ + Name: "trie-cache-gens", + Usage: "Number of trie node generations to keep in memory", + Value: int(state.MaxTrieCacheGen), + } // Fork settings SupportDAOFork = cli.BoolFlag{ Name: "support-dao-fork", @@ -188,7 +185,6 @@ var ( Usage: "Updates the chain rules to oppose the DAO hard-fork", } // Miner settings - // TODO: refactor CPU vs GPU mining flags MiningEnabledFlag = cli.BoolFlag{ Name: "mine", Usage: "Enable mining", @@ -198,10 +194,6 @@ var ( Usage: "Number of CPU threads to use for mining", Value: runtime.NumCPU(), } - MiningGPUFlag = cli.StringFlag{ - Name: "minergpus", - Usage: "List of GPUs to use for mining (e.g. '0,1' will use the first two GPUs found)", - } TargetGasLimitFlag = cli.StringFlag{ Name: "targetgaslimit", Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", @@ -269,12 +261,12 @@ var ( RPCListenAddrFlag = cli.StringFlag{ Name: "rpcaddr", Usage: "HTTP-RPC server listening interface", - Value: common.DefaultHTTPHost, + Value: node.DefaultHTTPHost, } RPCPortFlag = cli.IntFlag{ Name: "rpcport", Usage: "HTTP-RPC server listening port", - Value: common.DefaultHTTPPort, + Value: node.DefaultHTTPPort, } RPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", @@ -292,13 +284,13 @@ var ( } IPCApiFlag = cli.StringFlag{ Name: "ipcapi", - Usage: "API's offered over the IPC-RPC interface", + Usage: "APIs offered over the IPC-RPC interface", Value: rpc.DefaultIPCApis, } IPCPathFlag = DirectoryFlag{ Name: "ipcpath", Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", - Value: DirectoryString{common.DefaultIPCSocket}, + Value: DirectoryString{"geth.ipc"}, } WSEnabledFlag = cli.BoolFlag{ Name: "ws", @@ -307,12 +299,12 @@ var ( WSListenAddrFlag = cli.StringFlag{ Name: "wsaddr", Usage: "WS-RPC server listening interface", - Value: common.DefaultWSHost, + Value: node.DefaultWSHost, } WSPortFlag = cli.IntFlag{ Name: "wsport", Usage: "WS-RPC server listening port", - Value: common.DefaultWSPort, + Value: node.DefaultWSPort, } WSApiFlag = cli.StringFlag{ Name: "wsapi", @@ -371,6 +363,10 @@ var ( Name: "nodiscover", Usage: "Disables the peer discovery mechanism (manual peer addition)", } + DiscoveryV5Flag = cli.BoolFlag{ + Name: "v5disc", + Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", + } NoEthFlag = cli.BoolFlag{ Name: "noeth", Usage: "Disable Ethereum Protocol", @@ -431,13 +427,14 @@ func DebugSetup(ctx *cli.Context) error { return err } -// MustMakeDataDir retrieves the currently requested data directory, terminating +// MakeDataDir retrieves the currently requested data directory, terminating // if none (or the empty string) is specified. If the node is starting a testnet, // the a subdirectory of the specified datadir will be used. -func MustMakeDataDir(ctx *cli.Context) string { +func MakeDataDir(ctx *cli.Context) string { if path := ctx.GlobalString(DataDirFlag.Name); path != "" { + // TODO: choose a different location outside of the regular datadir. if ctx.GlobalBool(TestNetFlag.Name) { - return filepath.Join(path, "/testnet") + return filepath.Join(path, "testnet") } return path } @@ -482,16 +479,16 @@ func MakeNodeKey(ctx *cli.Context) *ecdsa.PrivateKey { return key } -// MakeNodeName creates a node name from a base set and the command line flags. -func MakeNodeName(client, version string, ctx *cli.Context) string { - name := common.MakeName(client, version) +// makeNodeUserIdent creates the user identifier from CLI flags. +func makeNodeUserIdent(ctx *cli.Context) string { + var comps []string if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 { - name += "/" + identity + comps = append(comps, identity) } if ctx.GlobalBool(VMEnableJitFlag.Name) { - name += "/JIT" + comps = append(comps, "JIT") } - return name + return strings.Join(comps, "/") } // MakeBootstrapNodes creates a list of bootstrap nodes from the command line @@ -500,9 +497,9 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node { // Return pre-configured nodes if none were manually requested if !ctx.GlobalIsSet(BootnodesFlag.Name) { if ctx.GlobalBool(TestNetFlag.Name) { - return TestNetBootNodes + return params.TestnetBootnodes } - return FrontierBootNodes + return params.MainnetBootnodes } // Otherwise parse and use the CLI bootstrap nodes bootnodes := []*discover.Node{} @@ -518,12 +515,39 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node { return bootnodes } +// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line +// flags, reverting to pre-configured ones if none have been specified. +func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node { + // Return pre-configured nodes if none were manually requested + if !ctx.GlobalIsSet(BootnodesFlag.Name) { + return params.DiscoveryV5Bootnodes + } + // Otherwise parse and use the CLI bootstrap nodes + bootnodes := []*discv5.Node{} + + for _, url := range strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") { + node, err := discv5.ParseNode(url) + if err != nil { + glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err) + continue + } + bootnodes = append(bootnodes, node) + } + return bootnodes +} + // MakeListenAddress creates a TCP listening address string from set command // line flags. func MakeListenAddress(ctx *cli.Context) string { return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) } +// MakeDiscoveryV5Address creates a UDP listening address string from set command +// line flags for the V5 discovery protocol. +func MakeDiscoveryV5Address(ctx *cli.Context) string { + return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1) +} + // MakeNAT creates a port mapper from set command line flags. func MakeNAT(ctx *cli.Context) nat.Interface { natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name)) @@ -640,15 +664,25 @@ func MakePasswordList(ctx *cli.Context) []string { } // MakeNode configures a node with no services from command line flags. -func MakeNode(ctx *cli.Context, name, version string) *node.Node { +func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { + vsn := Version + if gitCommit != "" { + vsn += "-" + gitCommit[:8] + } + config := &node.Config{ - DataDir: MustMakeDataDir(ctx), + DataDir: MakeDataDir(ctx), KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name), UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name), PrivateKey: MakeNodeKey(ctx), - Name: MakeNodeName(name, version, ctx), - NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name), + Name: name, + Version: vsn, + UserIdent: makeNodeUserIdent(ctx), + NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), + DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0, + DiscoveryV5Addr: MakeDiscoveryV5Address(ctx), BootstrapNodes: MakeBootstrapNodes(ctx), + BootstrapNodesV5: MakeBootstrapNodesV5(ctx), ListenAddr: MakeListenAddress(ctx), NAT: MakeNAT(ctx), MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), @@ -680,7 +714,7 @@ func MakeNode(ctx *cli.Context, name, version 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, relconf release.Config, extra []byte) { +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) @@ -698,25 +732,14 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf Fatalf("The %v flags are mutually exclusive", netFlags) } - // initialise new random number generator - rand := rand.New(rand.NewSource(time.Now().UnixNano())) - // get enabled jit flag - jitEnabled := ctx.GlobalBool(VMEnableJitFlag.Name) - // if the jit is not enabled enable it for 10 pct of the people - if !jitEnabled && rand.Float64() < 0.1 { - jitEnabled = true - glog.V(logger.Info).Infoln("You're one of the lucky few that will try out the JIT VM (random). If you get a consensus failure please be so kind to report this incident with the block hash that failed. You can switch to the regular VM by setting --jitvm=false") - } - ethConf := ð.Config{ Etherbase: MakeEtherbase(stack.AccountManager(), ctx), - ChainConfig: MustMakeChainConfig(ctx), + ChainConfig: MakeChainConfig(ctx, stack), FastSync: ctx.GlobalBool(FastSyncFlag.Name), LightMode: ctx.GlobalBool(LightModeFlag.Name), - NoDefSrv: ctx.GlobalBool(NoDefSrvFlag.Name), LightServ: ctx.GlobalInt(LightServFlag.Name), LightPeers: ctx.GlobalInt(LightPeersFlag.Name), - BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), + MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), DatabaseCache: ctx.GlobalInt(CacheFlag.Name), DatabaseHandles: MakeDatabaseHandles(), NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), @@ -724,8 +747,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf ExtraData: MakeMinerExtra(extra, ctx), NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name), DocRoot: ctx.GlobalString(DocRootFlag.Name), - EnableJit: jitEnabled, - ForceJit: ctx.GlobalBool(VMForceJitFlag.Name), GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)), GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)), @@ -747,11 +768,9 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf case ctx.GlobalBool(TestNetFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { - ethConf.NetworkId = 2 + ethConf.NetworkId = 3 } - ethConf.Genesis = core.TestNetGenesisBlock() - state.StartingNonce = 1048576 // (2**20) - light.StartingNonce = 1048576 // (2**20) + ethConf.Genesis = core.DefaultTestnetGenesisBlock() case ctx.GlobalBool(DevModeFlag.Name): ethConf.Genesis = core.OlympicGenesisBlock() @@ -760,6 +779,10 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf } ethConf.PowTest = true } + // Override any global options pertaining to the Ethereum protocol + if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 { + state.MaxTrieCacheGen = uint16(gen) + } if ethConf.LightMode { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { @@ -770,7 +793,7 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf } else { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { fullNode, err := eth.New(ctx, ethConf) - if fullNode != nil { + if fullNode != nil && ethConf.LightServ > 0 { ls, _ := les.NewLesServer(fullNode, ethConf) fullNode.AddLesServer(ls) } @@ -779,11 +802,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, relconf release.Conf Fatalf("Failed to register the Ethereum full node service: %v", err) } } - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return release.NewReleaseService(ctx, relconf) - }); err != nil { - Fatalf("Failed to register the Geth release oracle service: %v", err) - } } // RegisterShhService configures whisper and adds it to the given node. @@ -808,18 +826,18 @@ func SetupNetwork(ctx *cli.Context) { params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name)) } -// MustMakeChainConfig reads the chain configuration from the database in ctx.Datadir. -func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig { - db := MakeChainDatabase(ctx) +// MakeChainConfig reads the chain configuration from the database in ctx.Datadir. +func MakeChainConfig(ctx *cli.Context, stack *node.Node) *params.ChainConfig { + db := MakeChainDatabase(ctx, stack) defer db.Close() - return MustMakeChainConfigFromDb(ctx, db) + return MakeChainConfigFromDb(ctx, db) } -// MustMakeChainConfigFromDb reads the chain configuration from the given database. -func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig { +// MakeChainConfigFromDb reads the chain configuration from the given database. +func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig { // If the chain is already initialized, use any existing chain configs - config := new(core.ChainConfig) + config := new(params.ChainConfig) genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0) if genesis != nil { @@ -833,21 +851,36 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC Fatalf("Could not make chain configuration: %v", err) } } - // Set any missing fields due to them being unset or system upgrade - if config.HomesteadBlock == nil { - if ctx.GlobalBool(TestNetFlag.Name) { - config.HomesteadBlock = params.TestNetHomesteadBlock - } else { - config.HomesteadBlock = params.MainNetHomesteadBlock - } + // set chain id in case it's zero. + if config.ChainId == nil { + config.ChainId = new(big.Int) } - if config.DAOForkBlock == nil { + // Check whether we are allowed to set default config params or not: + // - If no genesis is set, we're running either mainnet or testnet (private nets use `geth init`) + // - If a genesis is already set, ensure we have a configuration for it (mainnet or testnet) + defaults := genesis == nil || + (genesis.Hash() == params.MainNetGenesisHash && !ctx.GlobalBool(TestNetFlag.Name)) || + (genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name)) + + if defaults { if ctx.GlobalBool(TestNetFlag.Name) { - config.DAOForkBlock = params.TestNetDAOForkBlock + 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 } - config.DAOForkSupport = true } // Force override any existing configs if explicitly requested switch { @@ -856,23 +889,6 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC case ctx.GlobalBool(OpposeDAOFork.Name): config.DAOForkSupport = false } - // Temporarilly display a proper message so the user knows which fork its on - if !ctx.GlobalBool(TestNetFlag.Name) && (genesis == nil || genesis.Hash() == common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")) { - choice := "SUPPORT" - if !config.DAOForkSupport { - choice = "OPPOSE" - } - current := fmt.Sprintf("Geth is currently configured to %s the DAO hard-fork!", choice) - howtoswap := fmt.Sprintf("You can change your choice prior to block #%v with --support-dao-fork or --oppose-dao-fork.", config.DAOForkBlock) - howtosync := fmt.Sprintf("After the hard-fork block #%v passed, changing chains requires a resync from scratch!", config.DAOForkBlock) - separator := strings.Repeat("-", len(howtoswap)) - - glog.V(logger.Warn).Info(separator) - glog.V(logger.Warn).Info(current) - glog.V(logger.Warn).Info(howtoswap) - glog.V(logger.Warn).Info(howtosync) - glog.V(logger.Warn).Info(separator) - } return config } @@ -885,15 +901,14 @@ func ChainDbName(ctx *cli.Context) string { } // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. -func MakeChainDatabase(ctx *cli.Context) ethdb.Database { +func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { var ( - datadir = MustMakeDataDir(ctx) cache = ctx.GlobalInt(CacheFlag.Name) handles = MakeDatabaseHandles() name = ChainDbName(ctx) ) - chainDb, err := ethdb.NewLDBDatabase(filepath.Join(datadir, name), cache, handles) + chainDb, err := stack.OpenDatabase(name, cache, handles) if err != nil { Fatalf("Could not open database: %v", err) } @@ -901,9 +916,9 @@ func MakeChainDatabase(ctx *cli.Context) ethdb.Database { } // MakeChain creates a chain manager from set command line flags. -func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) { +func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { var err error - chainDb = MakeChainDatabase(ctx) + chainDb = MakeChainDatabase(ctx, stack) if ctx.GlobalBool(OlympicFlag.Name) { _, err := core.WriteTestNetGenesisBlock(chainDb) @@ -911,7 +926,7 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database glog.Fatalln(err) } } - chainConfig := MustMakeChainConfigFromDb(ctx, chainDb) + chainConfig := MakeChainConfigFromDb(ctx, chainDb) pow := pow.PoW(core.FakePow{}) if !ctx.GlobalBool(FakePoWFlag.Name) { diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go new file mode 100644 index 000000000..3070d10b5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/version.go @@ -0,0 +1,64 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Package utils contains internal helper functions for go-ethereum commands. +package utils + +import ( + "fmt" + "runtime" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + VersionMajor = 1 // Major version component of the current release + VersionMinor = 5 // Minor version component of the current release + VersionPatch = 4 // Patch version component of the current release + VersionMeta = "unstable" // Version metadata to append to the version string +) + +// Version holds the textual version string. +var Version = func() string { + v := fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) + if VersionMeta != "" { + v += "-" + VersionMeta + } + return v +}() + +// MakeDefaultExtraData returns the default Ethereum block extra data blob. +func MakeDefaultExtraData(clientIdentifier string) []byte { + var clientInfo = struct { + Version uint + Name string + GoVersion string + Os string + }{uint(VersionMajor<<16 | VersionMinor<<8 | VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} + extra, err := rlp.EncodeToBytes(clientInfo) + if err != nil { + glog.V(logger.Warn).Infoln("error setting canonical miner information:", err) + } + if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() { + glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize) + glog.V(logger.Debug).Infof("extra: %x\n", extra) + return nil + } + return extra +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/bytes.go b/vendor/github.com/ethereum/go-ethereum/common/bytes.go index 4fb016a97..b9fb3b2da 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/bytes.go +++ b/vendor/github.com/ethereum/go-ethereum/common/bytes.go @@ -37,7 +37,7 @@ func ToHex(b []byte) string { func FromHex(s string) []byte { if len(s) > 1 { - if s[0:2] == "0x" { + if s[0:2] == "0x" || s[0:2] == "0X" { s = s[2:] } if len(s)%2 == 1 { diff --git a/vendor/github.com/ethereum/go-ethereum/common/format.go b/vendor/github.com/ethereum/go-ethereum/common/format.go new file mode 100644 index 000000000..119637d2e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/format.go @@ -0,0 +1,40 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "fmt" + "regexp" + "strings" + "time" +) + +// PrettyDuration is a pretty printed version of a time.Duration value that cuts +// the unnecessary precision off from the formatted textual representation. +type PrettyDuration time.Duration + +var prettyDurationRe = regexp.MustCompile("\\.[0-9]+") + +// String implements the Stringer interface, allowing pretty printing of duration +// values rounded to three decimals. +func (d PrettyDuration) String() string { + label := fmt.Sprintf("%v", time.Duration(d)) + if match := prettyDurationRe.FindString(label); len(match) > 4 { + label = strings.Replace(label, match, match[:4], 1) + } + return label +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/math/exp.go b/vendor/github.com/ethereum/go-ethereum/common/math/exp.go new file mode 100644 index 000000000..6f6c040e0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/math/exp.go @@ -0,0 +1,44 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// wordSize is the size number of bits in a big.Int Word. +const wordSize = 32 << (uint64(^big.Word(0)) >> 63) + +// Exp implement exponentiation by squaring algorithm. +// +// Courtesy @karalabe and @chfast +func Exp(base, exponent *big.Int) *big.Int { + result := big.NewInt(1) + + for _, word := range exponent.Bits() { + for i := 0; i < wordSize; i++ { + if word&1 == 1 { + common.U256(result.Mul(result, base)) + } + common.U256(base.Mul(base, base)) + word >>= 1 + } + } + return result +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go index 760429377..92005252e 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go +++ b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go @@ -20,11 +20,11 @@ package mclock import ( "time" - "github.com/aristanetworks/goarista/atime" + "github.com/aristanetworks/goarista/monotime" ) -type AbsTime time.Duration // absolute monotonic time +type AbsTime time.Duration // absolute monotonic time func Now() AbsTime { - return AbsTime(atime.NanoTime()) + return AbsTime(monotime.Now()) } diff --git a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec.go b/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec.go deleted file mode 100644 index 4521ff7a1..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec.go +++ /dev/null @@ -1,262 +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 . - -// +build ignore - -package natspec - -import ( - "bytes" - "encoding/json" - "fmt" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/httpclient" - "github.com/ethereum/go-ethereum/common/registrar" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/xeth" - "github.com/robertkrimen/otto" -) - -type abi2method map[[8]byte]*method - -type NatSpec struct { - jsvm *otto.Otto - abiDocJson []byte - userDoc userDoc - tx, data string -} - -// main entry point for to get natspec notice for a transaction -// the implementation is frontend friendly in that it always gives back -// a notice that is safe to display -// :FIXME: the second return value is an error, which can be used to fine-tune bahaviour -func GetNotice(xeth *xeth.XEth, tx string, http *httpclient.HTTPClient) (notice string) { - ns, err := New(xeth, tx, http) - if err != nil { - if ns == nil { - return getFallbackNotice(fmt.Sprintf("no NatSpec info found for contract: %v", err), tx) - } else { - return getFallbackNotice(fmt.Sprintf("invalid NatSpec info: %v", err), tx) - } - } - - notice, err = ns.Notice() - if err != nil { - return getFallbackNotice(fmt.Sprintf("NatSpec notice error: %v", err), tx) - } - - return -} - -func getFallbackNotice(comment, tx string) string { - return fmt.Sprintf("About to submit transaction (%s): %s", comment, tx) -} - -type transaction struct { - To string `json:"to"` - Data string `json:"data"` -} - -type jsonTx struct { - Params []transaction `json:"params"` -} - -type contractInfo struct { - Source string `json:"source"` - Language string `json:"language"` - Version string `json:"compilerVersion"` - AbiDefinition json.RawMessage `json:"abiDefinition"` - UserDoc userDoc `json:"userDoc"` - DeveloperDoc json.RawMessage `json:"developerDoc"` -} - -func New(xeth *xeth.XEth, jsontx string, http *httpclient.HTTPClient) (self *NatSpec, err error) { - - // extract contract address from tx - var tx jsonTx - err = json.Unmarshal([]byte(jsontx), &tx) - if err != nil { - return - } - t := tx.Params[0] - contractAddress := t.To - - content, err := FetchDocsForContract(contractAddress, xeth, http) - if err != nil { - return - } - - self, err = NewWithDocs(content, jsontx, t.Data) - return -} - -// also called by admin.contractInfo.get -func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, client *httpclient.HTTPClient) (content []byte, err error) { - // retrieve contract hash from state - codehex := xeth.CodeAt(contractAddress) - codeb := xeth.CodeAtBytes(contractAddress) - - if codehex == "0x" { - err = fmt.Errorf("contract (%v) not found", contractAddress) - return - } - codehash := common.BytesToHash(crypto.Keccak256(codeb)) - // set up nameresolver with natspecreg + urlhint contract addresses - reg := registrar.New(xeth) - - // resolve host via HashReg/UrlHint Resolver - hash, err := reg.HashToHash(codehash) - if err != nil { - return - } - if client.HasScheme("bzz") { - content, err = client.Get("bzz://"+hash.Hex()[2:], "") - if err == nil { // non-fatal - return - } - err = nil - //falling back to urlhint - } - - uri, err := reg.HashToUrl(hash) - if err != nil { - return - } - - // get content via http client and authenticate content using hash - content, err = client.GetAuthContent(uri, hash) - if err != nil { - return - } - return -} - -func NewWithDocs(infoDoc []byte, tx string, data string) (self *NatSpec, err error) { - - var contract contractInfo - err = json.Unmarshal(infoDoc, &contract) - if err != nil { - return - } - - self = &NatSpec{ - jsvm: otto.New(), - abiDocJson: []byte(contract.AbiDefinition), - userDoc: contract.UserDoc, - tx: tx, - data: data, - } - - // load and require natspec js (but it is meant to be protected environment) - _, err = self.jsvm.Run(natspecJS) - if err != nil { - return - } - _, err = self.jsvm.Run("var natspec = require('natspec');") - return -} - -// type abiDoc []method - -// type method struct { -// Name string `json:name` -// Inputs []input `json:inputs` -// abiKey [8]byte -// } - -// type input struct { -// Name string `json:name` -// Type string `json:type` -// } - -// json skeleton for abi doc (contract method definitions) -type method struct { - Notice string `json:notice` - name string -} - -type userDoc struct { - Methods map[string]*method `json:methods` -} - -func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) { - for signature, m := range self.userDoc.Methods { - name := strings.Split(signature, "(")[0] - hash := []byte(common.Bytes2Hex(crypto.Keccak256([]byte(signature)))) - var key [8]byte - copy(key[:], hash[:8]) - if bytes.Equal(key[:], abiKey[:]) { - meth = m - meth.name = name - return - } - } - return -} - -func (self *NatSpec) Notice() (notice string, err error) { - var abiKey [8]byte - if len(self.data) < 10 { - err = fmt.Errorf("Invalid transaction data") - return - } - copy(abiKey[:], self.data[2:10]) - meth := self.makeAbi2method(abiKey) - - if meth == nil { - err = fmt.Errorf("abi key does not match any method") - return - } - notice, err = self.noticeForMethod(self.tx, meth.name, meth.Notice) - return -} - -func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) { - - if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil { - return "", fmt.Errorf("natspec.js error setting transaction: %v", err) - } - - if _, err = self.jsvm.Run("var abi = " + string(self.abiDocJson) + ";"); err != nil { - return "", fmt.Errorf("natspec.js error setting abi: %v", err) - } - - if _, err = self.jsvm.Run("var method = '" + name + "';"); err != nil { - return "", fmt.Errorf("natspec.js error setting method: %v", err) - } - - if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil { - return "", fmt.Errorf("natspec.js error setting expression: %v", err) - } - - self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};") - value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);") - if err != nil { - return "", fmt.Errorf("natspec.js error evaluating expression: %v", err) - } - evalError := "Natspec evaluation failed, wrong input params" - if value.String() == evalError { - return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression) - } - if len(value.String()) == 0 { - return "", fmt.Errorf("natspec.js error evaluating expression") - } - - return value.String(), nil - -} diff --git a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go b/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go deleted file mode 100644 index 2b30d31d3..000000000 --- a/vendor/github.com/ethereum/go-ethereum/common/natspec/natspec_js.go +++ /dev/null @@ -1,4060 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package natspec - -const natspecJS = //`require=function t(e,n,r){function i(f,u){if(!n[f]){if(!e[f]){var s="function"==typeof require&&require;if(!u&&s)return s(f,!0);if(o)return o(f,!0);var c=new Error("Cannot find module '"+f+"'");throw c.code="MODULE_NOT_FOUND",c}var a=n[f]={exports:{}};e[f][0].call(a.exports,function(t){var n=e[f][1][t];return i(n?n:t)},a,a.exports,t,e,n,r)}return n[f].exports}for(var o="function"==typeof require&&require,f=0;fv;v++)d.push(g(e.slice(0,s))),e=e.slice(s);n.push(d)}else r.prefixedType("string")(t[c].type)?(a=a.slice(s),n.push(g(e.slice(0,s))),e=e.slice(s)):(n.push(g(e.slice(0,s))),e=e.slice(s))}),n},g=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return a(t.inputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},m=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e};e.exports={inputParser:g,outputParser:m,formatInput:a,formatOutput:h}},{"./const":4,"./formatters":5,"./types":6,"./utils":7}],4:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:i,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3}}).call(this,t("_process"))},{_process:2,"bignumber.js":8}],5:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=t("./utils"),o=t("./const"),f=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},u=function(t){var e=2*o.ETH_PADDING;return t instanceof r||"number"==typeof t?("number"==typeof t&&(t=new r(t)),r.config(o.ETH_BIGNUMBER_ROUNDING_MODE),t=t.round(),t.lessThan(0)&&(t=new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16).plus(t).plus(1)),t=t.toString(16)):t=0===t.indexOf("0x")?t.substr(2):"string"==typeof t?u(new r(t)):(+t).toString(16),f(t,e)},s=function(t){return i.fromAscii(t,o.ETH_PADDING).substr(2)},c=function(t){return"000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0")},a=function(t){return u(new r(t).times(new r(2).pow(128)))},l=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},p=function(t){return t=t||"0",l(t)?new r(t,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(t,16)},h=function(t){return t=t||"0",new r(t,16)},g=function(t){return p(t).dividedBy(new r(2).pow(128))},m=function(t){return h(t).dividedBy(new r(2).pow(128))},d=function(t){return"0x"+t},v=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t?!0:!1},w=function(t){return i.toAscii(t)},y=function(t){return"0x"+t.slice(t.length-40,t.length)};e.exports={formatInputInt:u,formatInputString:s,formatInputBool:c,formatInputReal:a,formatOutputInt:p,formatOutputUInt:h,formatOutputReal:g,formatOutputUReal:m,formatOutputHash:d,formatOutputBool:v,formatOutputString:w,formatOutputAddress:y}}).call(this,t("_process"))},{"./const":4,"./utils":7,_process:2,"bignumber.js":8}],6:[function(t,e){var n=t("./formatters"),r=function(t){return function(e){return 0===e.indexOf(t)}},i=function(t){return function(e){return t===e}},o=function(){return[{type:r("uint"),format:n.formatInputInt},{type:r("int"),format:n.formatInputInt},{type:r("hash"),format:n.formatInputInt},{type:r("string"),format:n.formatInputString},{type:r("real"),format:n.formatInputReal},{type:r("ureal"),format:n.formatInputReal},{type:i("address"),format:n.formatInputInt},{type:i("bool"),format:n.formatInputBool}]},f=function(){return[{type:r("uint"),format:n.formatOutputUInt},{type:r("int"),format:n.formatOutputInt},{type:r("hash"),format:n.formatOutputHash},{type:r("string"),format:n.formatOutputString},{type:r("real"),format:n.formatOutputReal},{type:r("ureal"),format:n.formatOutputUReal},{type:i("address"),format:n.formatOutputAddress},{type:i("bool"),format:n.formatOutputBool}]};e.exports={prefixedType:r,namedType:i,inputTypes:o,outputTypes:f}},{"./formatters":5}],7:[function(t,e){var n=t("./const"),r=function(t,e){for(var n=!1,r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&rr?"i":"").test(c))return m(a,c,u,r);u?(a.s=0>1/t?(c=c.slice(1),-1):1,$&&c.replace(/^0\.0*|\./,"").length>15&&U(L,O,t),u=!1):a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1,c=n(c,10,r,a.s)}else{if(t instanceof e)return a.s=t.s,a.e=t.e,a.c=(t=t.c)?t.slice():t,void(L=0);if((u="number"==typeof t)&&0*t==0){if(a.s=0>1/t?(t=-t,-1):1,t===~~t){for(o=0,f=t;f>=10;f/=10,o++);return a.e=o,a.c=[t],void(L=0)}c=t+""}else{if(!d.test(c=t+""))return m(a,c,u);a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1}}for((o=c.indexOf("."))>-1&&(c=c.replace(".","")),(f=c.search(/e/i))>0?(0>o&&(o=f),o+=+c.slice(f+1),c=c.substring(0,f)):0>o&&(o=c.length),f=0;48===c.charCodeAt(f);f++);for(s=c.length;48===c.charCodeAt(--s););if(c=c.slice(f,s+1))if(s=c.length,u&&$&&s>15&&U(L,O,a.s*t),o=o-f-1,o>q)a.c=a.e=null;else if(k>o)a.c=[a.e=0];else{if(a.e=o,a.c=[],f=(o+1)%I,0>o&&(f+=I),s>f){for(f&&a.c.push(+c.slice(0,f)),s-=I;s>f;)a.c.push(+c.slice(f,f+=I));c=c.slice(f),f=I-c.length}else f-=s;for(;f--;c+="0");a.c.push(+c)}else a.c=[a.e=0];L=0}function n(t,n,r,i){var f,u,s,a,p,h,g,m=t.indexOf("."),d=B,v=H;for(37>r&&(t=t.toLowerCase()),m>=0&&(s=Y,Y=0,t=t.replace(".",""),g=new e(r),p=g.pow(t.length-m),Y=s,g.c=c(l(o(p.c),p.e),10,n),g.e=g.c.length),h=c(t,r,n),u=s=h.length;0==h[--s];h.pop());if(!h[0])return"0";if(0>m?--u:(p.c=h,p.e=u,p.s=i,p=G(p,g,d,v,n),h=p.c,a=p.r,u=p.e),f=u+d+1,m=h[f],s=n/2,a=a||0>f||null!=h[f+1],a=4>v?(null!=m||a)&&(0==v||v==(p.s<0?3:2)):m>s||m==s&&(4==v||a||6==v&&1&h[f-1]||v==(p.s<0?8:7)),1>f||!h[0])t=a?l("1",-d):"0";else{if(h.length=f,a)for(--n;++h[--f]>n;)h[f]=0,f||(++u,h.unshift(1));for(s=h.length;!h[--s];);for(m=0,t="";s>=m;t+=N.charAt(h[m++]));t=l(t,u)}return t}function h(t,n,r,i){var f,u,s,c,p;if(r=null!=r&&z(r,0,8,i,b)?0|r:H,!t.c)return t.toString();if(f=t.c[0],s=t.e,null==n)p=o(t.c),p=19==i||24==i&&C>=s?a(p,s):l(p,s);else if(t=F(new e(t),n,r),u=t.e,p=o(t.c),c=p.length,19==i||24==i&&(u>=n||C>=u)){for(;n>c;p+="0",c++);p=a(p,u)}else if(n-=s,p=l(p,u),u+1>c){if(--n>0)for(p+=".";n--;p+="0");}else if(n+=u-c,n>0)for(u+1==c&&(p+=".");n--;p+="0");return t.s<0&&f?"-"+p:p}function S(t,n){var r,i,o=0;for(s(t[0])&&(t=t[0]),r=new e(t[0]);++ot||t>n||t!=p(t))&&U(r,(i||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function R(t,e,n){for(var r=1,i=e.length;!e[--i];e.pop());for(i=e[0];i>=10;i/=10,r++);return(n=r+n*I-1)>q?t.c=t.e=null:k>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function U(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",L=0,r}function F(t,e,n,r){var i,o,f,u,s,c,a,l=t.c,p=_;if(l){t:{for(i=1,u=l[0];u>=10;u/=10,i++);if(o=e-i,0>o)o+=I,f=e,s=l[c=0],a=s/p[i-f-1]%10|0;else if(c=v((o+1)/I),c>=l.length){if(!r)break t;for(;l.length<=c;l.push(0));s=a=0,i=1,o%=I,f=o-I+1}else{for(s=u=l[c],i=1;u>=10;u/=10,i++);o%=I,f=o-I+i,a=0>f?0:s/p[i-f-1]%10|0}if(r=r||0>e||null!=l[c+1]||(0>f?s:s%p[i-f-1]),r=4>n?(a||r)&&(0==n||n==(t.s<0?3:2)):a>5||5==a&&(4==n||r||6==n&&(o>0?f>0?s/p[i-f]:0:l[c-1])%10&1||n==(t.s<0?8:7)),1>e||!l[0])return l.length=0,r?(e-=t.e+1,l[0]=p[e%I],t.e=-e||0):l[0]=t.e=0,t;if(0==o?(l.length=c,u=1,c--):(l.length=c+1,u=p[I-o],l[c]=f>0?w(s/p[i-f]%p[f])*u:0),r)for(;;){if(0==c){for(o=1,f=l[0];f>=10;f/=10,o++);for(f=l[0]+=u,u=1;f>=10;f/=10,u++);o!=u&&(t.e++,l[0]==E&&(l[0]=1));break}if(l[c]+=u,l[c]!=E)break;l[c--]=0,u=1}for(o=l.length;0===l[--o];l.pop());}t.e>q?t.c=t.e=null:t.en?null!=(t=i[n++]):void 0};return f(e="DECIMAL_PLACES")&&z(t,0,D,2,e)&&(B=0|t),r[e]=B,f(e="ROUNDING_MODE")&&z(t,0,8,2,e)&&(H=0|t),r[e]=H,f(e="EXPONENTIAL_AT")&&(s(t)?z(t[0],-D,0,2,e)&&z(t[1],0,D,2,e)&&(C=0|t[0],j=0|t[1]):z(t,-D,D,2,e)&&(C=-(j=0|(0>t?-t:t)))),r[e]=[C,j],f(e="RANGE")&&(s(t)?z(t[0],-D,-1,2,e)&&z(t[1],1,D,2,e)&&(k=0|t[0],q=0|t[1]):z(t,-D,D,2,e)&&(0|t?k=-(q=0|(0>t?-t:t)):$&&U(2,e+" cannot be zero",t))),r[e]=[k,q],f(e="ERRORS")&&(t===!!t||1===t||0===t?(L=0,z=($=!!t)?A:u):$&&U(2,e+y,t)),r[e]=$,f(e="CRYPTO")&&(t===!!t||1===t||0===t?(V=!(!t||!g||"object"!=typeof g),t&&!V&&$&&U(2,"crypto unavailable",g)):$&&U(2,e+y,t)),r[e]=V,f(e="MODULO_MODE")&&z(t,0,9,2,e)&&(W=0|t),r[e]=W,f(e="POW_PRECISION")&&z(t,0,D,2,e)&&(Y=0|t),r[e]=Y,f(e="FORMAT")&&("object"==typeof t?Z=t:$&&U(2,e+" not an object",t)),r[e]=Z,r},e.max=function(){return S(arguments,M.lt)},e.min=function(){return S(arguments,M.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return w(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,i,o,f,u,s=0,c=[],a=new e(P);if(t=null!=t&&z(t,0,D,14)?0|t:B,f=v(t/I),V)if(g&&g.getRandomValues){for(r=g.getRandomValues(new Uint32Array(f*=2));f>s;)u=131072*r[s]+(r[s+1]>>>11),u>=9e15?(i=g.getRandomValues(new Uint32Array(2)),r[s]=i[0],r[s+1]=i[1]):(c.push(u%1e14),s+=2);s=f/2}else if(g&&g.randomBytes){for(r=g.randomBytes(f*=7);f>s;)u=281474976710656*(31&r[s])+1099511627776*r[s+1]+4294967296*r[s+2]+16777216*r[s+3]+(r[s+4]<<16)+(r[s+5]<<8)+r[s+6],u>=9e15?g.randomBytes(7).copy(r,s):(c.push(u%1e14),s+=7);s=f/7}else $&&U(14,"crypto unavailable",g);if(!s)for(;f>s;)u=n(),9e15>u&&(c[s++]=u%1e14);for(f=c[--s],t%=I,f&&t&&(u=_[I-t],c[s]=w(f/u)*u);0===c[s];c.pop(),s--);if(0>s)c=[o=0];else{for(o=-1;0===c[0];c.shift(),o-=I);for(s=1,u=c[0];u>=10;u/=10,s++);I>s&&(o-=I-s)}return a.e=o,a.c=c,a}}(),G=function(){function t(t,e,n){var r,i,o,f,u=0,s=t.length,c=e%T,a=e/T|0;for(t=t.slice();s--;)o=t[s]%T,f=t[s]/T|0,r=a*o+f*c,i=c*o+r%T*T+u,u=(i/n|0)+(r/T|0)+a*f,t[s]=i%n;return u&&t.unshift(u),t}function n(t,e,n,r){var i,o;if(n!=r)o=n>r?1:-1;else for(i=o=0;n>i;i++)if(t[i]!=e[i]){o=t[i]>e[i]?1:-1;break}return o}function r(t,e,n,r){for(var i=0;n--;)t[n]-=i,i=t[n]1;t.shift());}return function(o,f,u,s,c){var a,l,p,h,g,m,d,v,y,b,O,N,x,_,T,D,S,A=o.s==f.s?1:-1,R=o.c,U=f.c;if(!(R&&R[0]&&U&&U[0]))return new e(o.s&&f.s&&(R?!U||R[0]!=U[0]:U)?R&&0==R[0]||!U?0*A:A/0:0/0);for(v=new e(A),y=v.c=[],l=o.e-f.e,A=u+l+1,c||(c=E,l=i(o.e/I)-i(f.e/I),A=A/I|0),p=0;U[p]==(R[p]||0);p++);if(U[p]>(R[p]||0)&&l--,0>A)y.push(1),h=!0;else{for(_=R.length,D=U.length,p=0,A+=2,g=w(c/(U[0]+1)),g>1&&(U=t(U,g,c),R=t(R,g,c),D=U.length,_=R.length),x=D,b=R.slice(0,D),O=b.length;D>O;b[O++]=0);S=U.slice(),S.unshift(0),T=U[0],U[1]>=c/2&&T++;do g=0,a=n(U,b,D,O),0>a?(N=b[0],D!=O&&(N=N*c+(b[1]||0)),g=w(N/T),g>1?(g>=c&&(g=c-1),m=t(U,g,c),d=m.length,O=b.length,a=n(m,b,d,O),1==a&&(g--,r(m,d>D?S:U,d,c))):(0==g&&(a=g=1),m=U.slice()),d=m.length,O>d&&m.unshift(0),r(b,m,O,c),-1==a&&(O=b.length,a=n(U,b,D,O),1>a&&(g++,r(b,O>D?S:U,O,c))),O=b.length):0===a&&(g++,b=[0]),y[p++]=g,a&&b[0]?b[O++]=R[x]||0:(b=[R[x]],O=1);while((x++<_||null!=b[0])&&A--);h=null!=b[0],y[0]||y.shift()}if(c==E){for(p=1,A=y[0];A>=10;A/=10,p++);F(v,u+(v.e=p+l*I-1)+1,s,h)}else v.e=l,v.r=+h;return v}}(),m==function(){var t=/^(-?)0([xbo])(\w[\w.]*$)/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,i=/^-?(Infinity|NaN)$/,o=/^\s*\+([\w.])|^\s+|\s+$/g;return function(f,u,s,c){var a,l=s?u:u.replace(o,"$1");if(i.test(l))f.s=isNaN(l)?null:0>l?-1:1;else{if(!s&&(l=l.replace(t,function(t,e,n){return a="x"==(n=n.toLowerCase())?16:"b"==n?2:8,c&&c!=a?t:e}),c&&(a=c,l=l.replace(n,"$1").replace(r,"0.$1")),u!=l))return new e(l,a);$&&U(L,"not a"+(c?" base "+c:"")+" number",u),f.s=null}f.c=f.e=null,L=0}}(),M.absoluteValue=M.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},M.ceil=function(){return F(new e(this),this.e+1,2)},M.comparedTo=M.cmp=function(t,n){return L=1,f(this,new e(t,n))},M.decimalPlaces=M.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-i(this.e/I))*I,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},M.dividedBy=M.div=function(t,n){return L=3,G(this,new e(t,n),B,H)},M.dividedToIntegerBy=M.divToInt=function(t,n){return L=4,G(this,new e(t,n),0,1)},M.equals=M.eq=function(t,n){return L=5,0===f(this,new e(t,n))},M.floor=function(){return F(new e(this),this.e+1,3)},M.greaterThan=M.gt=function(t,n){return L=6,f(this,new e(t,n))>0},M.greaterThanOrEqualTo=M.gte=function(t,n){return L=7,1===(n=f(this,new e(t,n)))||0===n},M.isFinite=function(){return!!this.c},M.isInteger=M.isInt=function(){return!!this.c&&i(this.e/I)>this.c.length-2},M.isNaN=function(){return!this.s},M.isNegative=M.isNeg=function(){return this.s<0},M.isZero=function(){return!!this.c&&0==this.c[0]},M.lessThan=M.lt=function(t,n){return L=8,f(this,new e(t,n))<0},M.lessThanOrEqualTo=M.lte=function(t,n){return L=9,-1===(n=f(this,new e(t,n)))||0===n},M.minus=M.sub=function(t,n){var r,o,f,u,s=this,c=s.s;if(L=10,t=new e(t,n),n=t.s,!c||!n)return new e(0/0);if(c!=n)return t.s=-n,s.plus(t);var a=s.e/I,l=t.e/I,p=s.c,h=t.c;if(!a||!l){if(!p||!h)return p?(t.s=-n,t):new e(h?s:0/0);if(!p[0]||!h[0])return h[0]?(t.s=-n,t):new e(p[0]?s:3==H?-0:0)}if(a=i(a),l=i(l),p=p.slice(),c=a-l){for((u=0>c)?(c=-c,f=p):(l=a,f=h),f.reverse(),n=c;n--;f.push(0));f.reverse()}else for(o=(u=(c=p.length)<(n=h.length))?c:n,c=n=0;o>n;n++)if(p[n]!=h[n]){u=p[n]0)for(;n--;p[r++]=0);for(n=E-1;o>c;){if(p[--o]0?(s=u,r=a):(f=-f,r=c),r.reverse();f--;r.push(0));r.reverse()}for(f=c.length,n=a.length,0>f-n&&(r=a,a=c,c=r,n=f),f=0;n;)f=(c[--n]=c[n]+a[n]+f)/E|0,c[n]%=E;return f&&(c.unshift(f),++s),R(t,c,s)},M.precision=M.sd=function(t){var e,n,r=this,i=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&($&&U(13,"argument"+y,t),t!=!!t&&(t=null)),!i)return null;if(n=i.length-1,e=n*I+1,n=i[n]){for(;n%10==0;n/=10,e--);for(n=i[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},M.round=function(t,n){var r=new e(this);return(null==t||z(t,0,D,15))&&F(r,~~t+this.e+1,null!=n&&z(n,0,8,15,b)?0|n:H),r},M.shift=function(t){var n=this;return z(t,-x,x,16,"argument")?n.times("1e"+p(t)):new e(n.c&&n.c[0]&&(-x>t||t>x)?n.s*(0>t?0:1/0):n)},M.squareRoot=M.sqrt=function(){var t,n,r,f,u,s=this,c=s.c,a=s.s,l=s.e,p=B+4,h=new e("0.5");if(1!==a||!c||!c[0])return new e(!a||0>a&&(!c||c[0])?0/0:c?s:1/0);if(a=Math.sqrt(+s),0==a||a==1/0?(n=o(c),(n.length+l)%2==0&&(n+="0"),a=Math.sqrt(n),l=i((l+1)/2)-(0>l||l%2),a==1/0?n="1e"+l:(n=a.toExponential(),n=n.slice(0,n.indexOf("e")+1)+l),r=new e(n)):r=new e(a+""),r.c[0])for(l=r.e,a=l+p,3>a&&(a=0);;)if(u=r,r=h.times(u.plus(G(s,u,p,1))),o(u.c).slice(0,a)===(n=o(r.c)).slice(0,a)){if(r.ea&&(d=b,b=O,O=d,f=a,a=h,h=f),f=a+h,d=[];f--;d.push(0));for(v=E,w=T,f=h;--f>=0;){for(r=0,g=O[f]%w,m=O[f]/w|0,s=a,u=f+s;u>f;)l=b[--s]%w,p=b[s]/w|0,c=m*l+p*g,l=g*l+c%w*w+d[u]+r,r=(l/v|0)+(c/w|0)+m*p,d[u--]=l%v;d[u]=r}return r?++o:d.shift(),R(t,d,o)},M.toDigits=function(t,n){var r=new e(this);return t=null!=t&&z(t,1,D,18,"precision")?0|t:null,n=null!=n&&z(n,0,8,18,b)?0|n:H,t?F(r,t,n):r},M.toExponential=function(t,e){return h(this,null!=t&&z(t,0,D,19)?~~t+1:null,e,19)},M.toFixed=function(t,e){return h(this,null!=t&&z(t,0,D,20)?~~t+this.e+1:null,e,20)},M.toFormat=function(t,e){var n=h(this,null!=t&&z(t,0,D,21)?~~t+this.e+1:null,e,21);if(this.c){var r,i=n.split("."),o=+Z.groupSize,f=+Z.secondaryGroupSize,u=Z.groupSeparator,s=i[0],c=i[1],a=this.s<0,l=a?s.slice(1):s,p=l.length;if(f&&(r=o,o=f,f=r,p-=r),o>0&&p>0){for(r=p%o||o,s=l.substr(0,r);p>r;r+=o)s+=u+l.substr(r,o);f>0&&(s+=u+l.slice(r)),a&&(s="-"+s)}n=c?s+Z.decimalSeparator+((f=+Z.fractionGroupSize)?c.replace(new RegExp("\\d{"+f+"}\\B","g"),"$&"+Z.fractionGroupSeparator):c):s}return n},M.toFraction=function(t){var n,r,i,f,u,s,c,a,l,p=$,h=this,g=h.c,m=new e(P),d=r=new e(P),v=c=new e(P);if(null!=t&&($=!1,s=new e(t),$=p,(!(p=s.isInt())||s.lt(P))&&($&&U(22,"max denominator "+(p?"out of range":"not an integer"),t),t=!p&&s.c&&F(s,s.e+1,1).gte(P)?s:null)),!g)return h.toString();for(l=o(g),f=m.e=l.length-h.e-1,m.c[0]=_[(u=f%I)<0?I+u:u],t=!t||s.cmp(m)>0?f>0?m:d:s,u=q,q=1/0,s=new e(l),c.c[0]=0;a=G(s,m,0,1),i=r.plus(a.times(v)),1!=i.cmp(t);)r=v,v=i,d=c.plus(a.times(i=d)),c=i,m=s.minus(a.times(i=m)),s=i;return i=G(t.minus(r),v,0,1),c=c.plus(i.times(d)),r=r.plus(i.times(v)),c.s=d.s=h.s,f*=2,n=G(d,v,f,H).minus(h).abs().cmp(G(c,r,f,H).minus(h).abs())<1?[d.toString(),v.toString()]:[c.toString(),r.toString()],q=u,n},M.toNumber=function(){var t=this;return+t||(t.s?0*t.s:0/0)},M.toPower=M.pow=function(t){var n,r,i=w(0>t?-t:+t),o=this;if(!z(t,-x,x,23,"exponent")&&(!isFinite(t)||i>x&&(t/=0)||parseFloat(t)!=t&&!(t=0/0)))return new e(Math.pow(+o,t));for(n=Y?v(Y/I+2):0,r=new e(P);;){if(i%2){if(r=r.times(o),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(i=w(i/2),!i)break;o=o.times(o),n&&o.c&&o.c.length>n&&(o.c.length=n)}return 0>t&&(r=P.div(r)),n?F(r,Y,H):r},M.toPrecision=function(t,e){return h(this,null!=t&&z(t,1,D,24,"precision")?0|t:null,e,24)},M.toString=function(t){var e,r=this,i=r.s,f=r.e;return null===f?i?(e="Infinity",0>i&&(e="-"+e)):e="NaN":(e=o(r.c),e=null!=t&&z(t,2,64,25,"base")?n(l(e,f),0|t,10,i):C>=f||f>=j?a(e,f):l(e,f),0>i&&r.c[0]&&(e="-"+e)),e},M.truncated=M.trunc=function(){return F(new e(this),this.e+1,1)},M.valueOf=M.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function i(t){var e=0|t;return t>0||t===e?e:e-1}function o(t){for(var e,n,r=1,i=t.length,o=t[0]+"";i>r;){for(e=t[r++]+"",n=I-e.length;n--;e="0"+e);o+=e}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function f(t,e){var n,r,i=t.c,o=e.c,f=t.s,u=e.s,s=t.e,c=e.e;if(!f||!u)return null;if(n=i&&!i[0],r=o&&!o[0],n||r)return n?r?0:-u:f;if(f!=u)return f;if(n=0>f,r=s==c,!i||!o)return r?0:!i^n?1:-1;if(!r)return s>c^n?1:-1;for(u=(s=i.length)<(c=o.length)?s:c,f=0;u>f;f++)if(i[f]!=o[f])return i[f]>o[f]^n?1:-1;return s==c?0:s>c^n?1:-1}function u(t,e,n){return(t=p(t))>=e&&n>=t}function s(t){return"[object Array]"==Object.prototype.toString.call(t)}function c(t,e,n){for(var r,i,o=[0],f=0,u=t.length;u>f;){for(i=o.length;i--;o[i]*=e);for(o[r=0]+=N.indexOf(t.charAt(f++));rn-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/n|0,o[r]%=n)}return o.reverse()}function a(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function l(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function p(t){return t=parseFloat(t),0>t?v(t):w(t)}var h,g,m,d=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,v=Math.ceil,w=Math.floor,y=" not a boolean or binary digit",b="rounding mode",O="number type has more than 15 significant digits",N="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",E=1e14,I=14,x=9007199254740991,_=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],T=1e7,D=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!g)try{g=t("crypto")}catch(S){}}else n.BigNumber=h}(this)},{crypto:1}],natspec:[function(t,e){var n=t("./node_modules/ethereum.js/lib/abi.js"),r=function(){var t=function(t,e){Object.keys(t).forEach(function(n){e[n]=t[n]})},e=function(t){return Object.keys(t).reduce(function(t,e){return t+"var "+e+" = context['"+e+"'];\n"},"")},r=function(t,e){return t.filter(function(t){return t.name===e})[0]},i=function(t,e){var r=n.formatOutput(t.inputs,"0x"+e.params[0].data.slice(10));return t.inputs.reduce(function(t,e,n){return t[e.name]=r[n],t},{})},o=function(t,e){var n,r="",i=/\` + "`" + `(?:\\.|[^` + "`" + `\\])*\` + "`" + `/gim,o=0;try{for(;null!==(n=i.exec(t));){var f=i.lastIndex-n[0].length,u=n[0].slice(1,n[0].length-1);r+=t.slice(o,f);var s=e(u);r+=s,o=i.lastIndex}r+=t.slice(o)}catch(c){throw new Error("Natspec evaluation failed, wrong input params")}return r},f=function(n,f){var u={};if(f)try{var s=r(f.abi,f.method),c=i(s,f.transaction);t(c,u)}catch(a){throw new Error("Natspec evaluation failed, method does not exist")}var l=e(u),p=o(n,function(t){var e=new Function("context",l+"return "+t+";");return e(u).toString()});return p},u=function(t,e){try{return f(t,e)}catch(n){return n.message}};return{evaluateExpression:f,evaluateExpressionSafe:u}}();e.exports=r},{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); -` -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. -*/ -/** - * @file abi.js - * @author Marek Kotewicz - * @author Gav Wood - * @date 2014 - */ - -var utils = require('../utils/utils'); -var c = require('../utils/config'); -var types = require('./types'); -var f = require('./formatters'); -var solUtils = require('./utils'); - -/** - * throw incorrect type error - * - * @method throwTypeError - * @param {String} type - * @throws incorrect type error - */ -var throwTypeError = function (type) { - throw new Error('parser does not support type: ' + type); -}; - -/** This method should be called if we want to check if givent type is an array type - * - * @method isArrayType - * @param {String} type name - * @returns {Boolean} true if it is, otherwise false - */ -var isArrayType = function (type) { - return type.slice(-2) === '[]'; -}; - -/** - * This method should be called to return dynamic type length in hex - * - * @method dynamicTypeBytes - * @param {String} type - * @param {String|Array} dynamic type - * @return {String} length of dynamic type in hex or empty string if type is not dynamic - */ -var dynamicTypeBytes = function (type, value) { - // TODO: decide what to do with array of strings - if (isArrayType(type) || type === 'bytes') - return f.formatInputInt(value.length); - return ""; -}; - -var inputTypes = types.inputTypes(); - -/** - * Formats input params to bytes - * - * @method formatInput - * @param {Array} abi inputs of method - * @param {Array} params that will be formatted to bytes - * @returns bytes representation of input params - */ -var formatInput = function (inputs, params) { - var bytes = ""; - var toAppendConstant = ""; - var toAppendArrayContent = ""; - - /// first we iterate in search for dynamic - inputs.forEach(function (input, index) { - bytes += dynamicTypeBytes(input.type, params[index]); - }); - - inputs.forEach(function (input, i) { - /*jshint maxcomplexity:5 */ - var typeMatch = false; - for (var j = 0; j < inputTypes.length && !typeMatch; j++) { - typeMatch = inputTypes[j].type(inputs[i].type, params[i]); - } - if (!typeMatch) { - throwTypeError(inputs[i].type); - } - - var formatter = inputTypes[j - 1].format; - - if (isArrayType(inputs[i].type)) - toAppendArrayContent += params[i].reduce(function (acc, curr) { - return acc + formatter(curr); - }, ""); - else if (inputs[i].type === 'bytes') - toAppendArrayContent += formatter(params[i]); - else - toAppendConstant += formatter(params[i]); - }); - - bytes += toAppendConstant + toAppendArrayContent; - - return bytes; -}; - -/** - * This method should be called to predict the length of dynamic type - * - * @method dynamicBytesLength - * @param {String} type - * @returns {Number} length of dynamic type, 0 or multiplication of ETH_PADDING (32) - */ -var dynamicBytesLength = function (type) { - if (isArrayType(type) || type === 'bytes') - return c.ETH_PADDING * 2; - return 0; -}; - -var outputTypes = types.outputTypes(); - -/** - * Formats output bytes back to param list - * - * @method formatOutput - * @param {Array} abi outputs of method - * @param {String} bytes represention of output - * @returns {Array} output params - */ -var formatOutput = function (outs, output) { - - output = output.slice(2); - var result = []; - var padding = c.ETH_PADDING * 2; - - var dynamicPartLength = outs.reduce(function (acc, curr) { - return acc + dynamicBytesLength(curr.type); - }, 0); - - var dynamicPart = output.slice(0, dynamicPartLength); - output = output.slice(dynamicPartLength); - - outs.forEach(function (out, i) { - /*jshint maxcomplexity:6 */ - var typeMatch = false; - for (var j = 0; j < outputTypes.length && !typeMatch; j++) { - typeMatch = outputTypes[j].type(outs[i].type); - } - - if (!typeMatch) { - throwTypeError(outs[i].type); - } - - var formatter = outputTypes[j - 1].format; - if (isArrayType(outs[i].type)) { - var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); - dynamicPart = dynamicPart.slice(padding); - var array = []; - for (var k = 0; k < size; k++) { - array.push(formatter(output.slice(0, padding))); - output = output.slice(padding); - } - result.push(array); - } - else if (types.prefixedType('bytes')(outs[i].type)) { - dynamicPart = dynamicPart.slice(padding); - result.push(formatter(output.slice(0, padding))); - output = output.slice(padding); - } else { - result.push(formatter(output.slice(0, padding))); - output = output.slice(padding); - } - }); - - return result; -}; - -/** - * Should be called to create input parser for contract with given abi - * - * @method inputParser - * @param {Array} contract abi - * @returns {Object} input parser object for given json abi - * TODO: refactor creating the parser, do not double logic from contract - */ -var inputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function () { - var params = Array.prototype.slice.call(arguments); - return formatInput(method.inputs, params); - }; - - if (parser[displayName] === undefined) { - parser[displayName] = impl; - } - - parser[displayName][typeName] = impl; - }); - - return parser; -}; - -/** - * Should be called to create output parser for contract with given abi - * - * @method outputParser - * @param {Array} contract abi - * @returns {Object} output parser for given json abi - */ -var outputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function (output) { - return formatOutput(method.outputs, output); - }; - - if (parser[displayName] === undefined) { - parser[displayName] = impl; - } - - parser[displayName][typeName] = impl; - }); - - return parser; -}; - -var formatConstructorParams = function (abi, params) { - var constructor = solUtils.getConstructor(abi, params.length); - if (!constructor) { - if (params.length > 0) { - console.warn("didn't found matching constructor, using default one"); - } - return ''; - } - return formatInput(constructor.inputs, params); -}; - -module.exports = { - inputParser: inputParser, - outputParser: outputParser, - formatInput: formatInput, - formatOutput: formatOutput, - formatConstructorParams: formatConstructorParams -}; - -},{"../utils/config":6,"../utils/utils":7,"./formatters":3,"./types":4,"./utils":5}],3:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file formatters.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var BigNumber = require('bignumber.js'); -var utils = require('../utils/utils'); -var c = require('../utils/config'); - -/** - * Formats input value to byte representation of int - * If value is negative, return it's two's complement - * If the value is floating point, round it down - * - * @method formatInputInt - * @param {String|Number|BigNumber} value that needs to be formatted - * @returns {String} right-aligned byte representation of int - */ -var formatInputInt = function (value) { - var padding = c.ETH_PADDING * 2; - BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); - return utils.padLeft(utils.toTwosComplement(value).round().toString(16), padding); -}; - -/** - * Formats input value to byte representation of string - * - * @method formatInputString - * @param {String} - * @returns {String} left-algined byte representation of string - */ -var formatInputString = function (value) { - return utils.fromAscii(value, c.ETH_PADDING).substr(2); -}; - -/** - * Formats input value to byte representation of bool - * - * @method formatInputBool - * @param {Boolean} - * @returns {String} right-aligned byte representation bool - */ -var formatInputBool = function (value) { - return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); -}; - -/** - * Formats input value to byte representation of real - * Values are multiplied by 2^m and encoded as integers - * - * @method formatInputReal - * @param {String|Number|BigNumber} - * @returns {String} byte representation of real - */ -var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); -}; - -/** - * Check if input value is negative - * - * @method signedIsNegative - * @param {String} value is hex format - * @returns {Boolean} true if it is negative, otherwise false - */ -var signedIsNegative = function (value) { - return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; -}; - -/** - * Formats right-aligned output bytes to int - * - * @method formatOutputInt - * @param {String} bytes - * @returns {BigNumber} right-aligned output bytes formatted to big number - */ -var formatOutputInt = function (value) { - - value = value || "0"; - - // check if it's negative number - // it it is, return two's complement - if (signedIsNegative(value)) { - return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); - } - return new BigNumber(value, 16); -}; - -/** - * Formats right-aligned output bytes to uint - * - * @method formatOutputUInt - * @param {String} bytes - * @returns {BigNumeber} right-aligned output bytes formatted to uint - */ -var formatOutputUInt = function (value) { - value = value || "0"; - return new BigNumber(value, 16); -}; - -/** - * Formats right-aligned output bytes to real - * - * @method formatOutputReal - * @param {String} - * @returns {BigNumber} input bytes formatted to real - */ -var formatOutputReal = function (value) { - return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/** - * Formats right-aligned output bytes to ureal - * - * @method formatOutputUReal - * @param {String} - * @returns {BigNumber} input bytes formatted to ureal - */ -var formatOutputUReal = function (value) { - return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/** - * Should be used to format output hash - * - * @method formatOutputHash - * @param {String} - * @returns {String} right-aligned output bytes formatted to hex - */ -var formatOutputHash = function (value) { - return "0x" + value; -}; - -/** - * Should be used to format output bool - * - * @method formatOutputBool - * @param {String} - * @returns {Boolean} right-aligned input bytes formatted to bool - */ -var formatOutputBool = function (value) { - return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; -}; - -/** - * Should be used to format output string - * - * @method formatOutputString - * @param {Sttring} left-aligned hex representation of string - * @returns {String} ascii string - */ -var formatOutputString = function (value) { - return utils.toAscii(value); -}; - -/** - * Should be used to format output address - * - * @method formatOutputAddress - * @param {String} right-aligned input bytes - * @returns {String} address - */ -var formatOutputAddress = function (value) { - return "0x" + value.slice(value.length - 40, value.length); -}; - -module.exports = { - formatInputInt: formatInputInt, - formatInputString: formatInputString, - formatInputBool: formatInputBool, - formatInputReal: formatInputReal, - formatOutputInt: formatOutputInt, - formatOutputUInt: formatOutputUInt, - formatOutputReal: formatOutputReal, - formatOutputUReal: formatOutputUReal, - formatOutputHash: formatOutputHash, - formatOutputBool: formatOutputBool, - formatOutputString: formatOutputString, - formatOutputAddress: formatOutputAddress -}; - - -},{"../utils/config":6,"../utils/utils":7,"bignumber.js":8}],4:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file types.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var f = require('./formatters'); - -/// @param expected type prefix (string) -/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false -var prefixedType = function (prefix) { - return function (type) { - return type.indexOf(prefix) === 0; - }; -}; - -/// @param expected type name (string) -/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false -var namedType = function (name) { - return function (type) { - return name === type; - }; -}; - -/// Setups input formatters for solidity types -/// @returns an array of input formatters -var inputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatInputInt }, - { type: prefixedType('int'), format: f.formatInputInt }, - { type: prefixedType('bytes'), format: f.formatInputString }, - { type: prefixedType('real'), format: f.formatInputReal }, - { type: prefixedType('ureal'), format: f.formatInputReal }, - { type: namedType('address'), format: f.formatInputInt }, - { type: namedType('bool'), format: f.formatInputBool } - ]; -}; - -/// Setups output formaters for solidity types -/// @returns an array of output formatters -var outputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatOutputUInt }, - { type: prefixedType('int'), format: f.formatOutputInt }, - { type: prefixedType('bytes'), format: f.formatOutputString }, - { type: prefixedType('real'), format: f.formatOutputReal }, - { type: prefixedType('ureal'), format: f.formatOutputUReal }, - { type: namedType('address'), format: f.formatOutputAddress }, - { type: namedType('bool'), format: f.formatOutputBool } - ]; -}; - -module.exports = { - prefixedType: prefixedType, - namedType: namedType, - inputTypes: inputTypes, - outputTypes: outputTypes -}; - - -},{"./formatters":3}],5:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** - * @file utils.js - * @author Marek Kotewicz - * @date 2015 - */ - -/** - * Returns the contstructor with matching number of arguments - * - * @method getConstructor - * @param {Array} abi - * @param {Number} numberOfArgs - * @returns {Object} constructor function abi - */ -var getConstructor = function (abi, numberOfArgs) { - return abi.filter(function (f) { - return f.type === 'constructor' && f.inputs.length === numberOfArgs; - })[0]; -}; - -/** - * Filters all functions from input abi - * - * @method filterFunctions - * @param {Array} abi - * @returns {Array} abi array with filtered objects of type 'function' - */ -var filterFunctions = function (json) { - return json.filter(function (current) { - return current.type === 'function'; - }); -}; - -/** - * Filters all events from input abi - * - * @method filterEvents - * @param {Array} abi - * @returns {Array} abi array with filtered objects of type 'event' - */ -var filterEvents = function (json) { - return json.filter(function (current) { - return current.type === 'event'; - }); -}; - -module.exports = { - getConstructor: getConstructor, - filterFunctions: filterFunctions, - filterEvents: filterEvents -}; - - -},{}],6:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file config.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -/** - * Utils - * - * @module utils - */ - -/** - * Utility functions - * - * @class [utils] config - * @constructor - */ - -/// required to define ETH_BIGNUMBER_ROUNDING_MODE -var BigNumber = require('bignumber.js'); - -var ETH_UNITS = [ - 'wei', - 'Kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' -]; - -module.exports = { - ETH_PADDING: 32, - ETH_SIGNATURE_LENGTH: 4, - ETH_UNITS: ETH_UNITS, - ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, - ETH_POLLING_TIMEOUT: 1000, - ETH_DEFAULTBLOCK: 'latest' -}; - - -},{"bignumber.js":8}],7:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js 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. - - ethereum.js 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 ethereum.js. If not, see . -*/ -/** @file utils.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -/** - * Utils - * - * @module utils - */ - -/** - * Utility functions - * - * @class [utils] utils - * @constructor - */ - -var BigNumber = require('bignumber.js'); - -var unitMap = { - 'wei': '1', - 'kwei': '1000', - 'ada': '1000', - 'mwei': '1000000', - 'babbage': '1000000', - 'gwei': '1000000000', - 'shannon': '1000000000', - 'szabo': '1000000000000', - 'finney': '1000000000000000', - 'ether': '1000000000000000000', - 'kether': '1000000000000000000000', - 'grand': '1000000000000000000000', - 'einstein': '1000000000000000000000', - 'mether': '1000000000000000000000000', - 'gether': '1000000000000000000000000000', - 'tether': '1000000000000000000000000000000' -}; - -/** - * Should be called to pad string to expected length - * - * @method padLeft - * @param {String} string to be padded - * @param {Number} characters that result string should have - * @param {String} sign, by default 0 - * @returns {String} right aligned string - */ -var padLeft = function (string, chars, sign) { - return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; -}; - -/** Finds first index of array element matching pattern - * - * @method findIndex - * @param {Array} - * @param {Function} pattern - * @returns {Number} index of element - */ -var findIndex = function (array, callback) { - var end = false; - var i = 0; - for (; i < array.length && !end; i++) { - end = callback(array[i]); - } - return end ? i - 1 : -1; -}; - -/** - * Should be called to get sting from it's hex representation - * - * @method toAscii - * @param {String} string in hex - * @returns {String} ascii string representation of hex value - */ -var toAscii = function(hex) { -// Find termination - var str = ""; - var i = 0, l = hex.length; - if (hex.substring(0, 2) === '0x') { - i = 2; - } - for (; i < l; i+=2) { - var code = parseInt(hex.substr(i, 2), 16); - if (code === 0) { - break; - } - - str += String.fromCharCode(code); - } - - return str; -}; - -/** - * Shold be called to get hex representation (prefixed by 0x) of ascii string - * - * @method fromAscii - * @param {String} string - * @returns {String} hex representation of input string - */ -var toHexNative = function(str) { - var hex = ""; - for(var i = 0; i < str.length; i++) { - var n = str.charCodeAt(i).toString(16); - hex += n.length < 2 ? '0' + n : n; - } - - return hex; -}; - -/** - * Shold be called to get hex representation (prefixed by 0x) of ascii string - * - * @method fromAscii - * @param {String} string - * @param {Number} optional padding - * @returns {String} hex representation of input string - */ -var fromAscii = function(str, pad) { - pad = pad === undefined ? 0 : pad; - var hex = toHexNative(str); - while (hex.length < pad*2) - hex += "00"; - return "0x" + hex; -}; - -/** - * Should be called to get display name of contract function - * - * @method extractDisplayName - * @param {String} name of function/event - * @returns {String} display name for function/event eg. multiply(uint256) -> multiply - */ -var extractDisplayName = function (name) { - var length = name.indexOf('('); - return length !== -1 ? name.substr(0, length) : name; -}; - -/// @returns overloaded part of function/event name -var extractTypeName = function (name) { - /// TODO: make it invulnerable - var length = name.indexOf('('); - return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; -}; - -/** - * Converts value to it's decimal representation in string - * - * @method toDecimal - * @param {String|Number|BigNumber} - * @return {String} - */ -var toDecimal = function (value) { - return toBigNumber(value).toNumber(); -}; - -/** - * Converts value to it's hex representation - * - * @method fromDecimal - * @param {String|Number|BigNumber} - * @return {String} - */ -var fromDecimal = function (value) { - var number = toBigNumber(value); - var result = number.toString(16); - - return number.lessThan(0) ? '-0x' + result.substr(1) : '0x' + result; -}; - -/** - * Auto converts any given value into it's hex representation. - * - * And even stringifys objects before. - * - * @method toHex - * @param {String|Number|BigNumber|Object} - * @return {String} - */ -var toHex = function (val) { - /*jshint maxcomplexity:7 */ - - if (isBoolean(val)) - return fromDecimal(+val); - - if (isBigNumber(val)) - return fromDecimal(val); - - if (isObject(val)) - return fromAscii(JSON.stringify(val)); - - // if its a negative number, pass it through fromDecimal - if (isString(val)) { - if (val.indexOf('-0x') === 0) - return fromDecimal(val); - else if (!isFinite(val)) - return fromAscii(val); - } - - return fromDecimal(val); -}; - -/** - * Returns value of unit in Wei - * - * @method getValueOfUnit - * @param {String} unit the unit to convert to, default ether - * @returns {BigNumber} value of the unit (in Wei) - * @throws error if the unit is not correct:w - */ -var getValueOfUnit = function (unit) { - unit = unit ? unit.toLowerCase() : 'ether'; - var unitValue = unitMap[unit]; - if (unitValue === undefined) { - throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2)); - } - return new BigNumber(unitValue, 10); -}; - -/** - * Takes a number of wei and converts it to any other ether unit. - * - * Possible units are: - * - kwei/ada - * - mwei/babbage - * - gwei/shannon - * - szabo - * - finney - * - ether - * - kether/grand/einstein - * - mether - * - gether - * - tether - * - * @method fromWei - * @param {Number|String} number can be a number, number string or a HEX of a decimal - * @param {String} unit the unit to convert to, default ether - * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var fromWei = function(number, unit) { - var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); - - return isBigNumber(number) ? returnValue : returnValue.toString(10); -}; - -/** - * Takes a number of a unit and converts it to wei. - * - * Possible units are: - * - kwei/ada - * - mwei/babbage - * - gwei/shannon - * - szabo - * - finney - * - ether - * - kether/grand/einstein - * - mether - * - gether - * - tether - * - * @method toWei - * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal - * @param {String} unit the unit to convert from, default ether - * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var toWei = function(number, unit) { - var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); - - return isBigNumber(number) ? returnValue : returnValue.toString(10); -}; - -/** - * Takes an input and transforms it into an bignumber - * - * @method toBigNumber - * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber - * @return {BigNumber} BigNumber -*/ -var toBigNumber = function(number) { - /*jshint maxcomplexity:5 */ - number = number || 0; - if (isBigNumber(number)) - return number; - - if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) { - return new BigNumber(number.replace('0x',''), 16); - } - - return new BigNumber(number.toString(10), 10); -}; - -/** - * Takes and input transforms it into bignumber and if it is negative value, into two's complement - * - * @method toTwosComplement - * @param {Number|String|BigNumber} - * @return {BigNumber} - */ -var toTwosComplement = function (number) { - var bigNumber = toBigNumber(number); - if (bigNumber.lessThan(0)) { - return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1); - } - return bigNumber; -}; - -/** - * Checks if the given string is strictly an address - * - * @method isStrictAddress - * @param {String} address the given HEX adress - * @return {Boolean} -*/ -var isStrictAddress = function (address) { - return /^0x[0-9a-f]{40}$/.test(address); -}; - -/** - * Checks if the given string is an address - * - * @method isAddress - * @param {String} address the given HEX adress - * @return {Boolean} -*/ -var isAddress = function (address) { - return /^(0x)?[0-9a-f]{40}$/.test(address); -}; - -/** - * Transforms given string to valid 20 bytes-length addres with 0x prefix - * - * @method toAddress - * @param {String} address - * @return {String} formatted address - */ -var toAddress = function (address) { - if (isStrictAddress(address)) { - return address; - } - - if (/^[0-9a-f]{40}$/.test(address)) { - return '0x' + address; - } - - return '0x' + padLeft(toHex(address).substr(2), 40); -}; - -/** - * Returns true if object is BigNumber, otherwise false - * - * @method isBigNumber - * @param {Object} - * @return {Boolean} - */ -var isBigNumber = function (object) { - return object instanceof BigNumber || - (object && object.constructor && object.constructor.name === 'BigNumber'); -}; - -/** - * Returns true if object is string, otherwise false - * - * @method isString - * @param {Object} - * @return {Boolean} - */ -var isString = function (object) { - return typeof object === 'string' || - (object && object.constructor && object.constructor.name === 'String'); -}; - -/** - * Returns true if object is function, otherwise false - * - * @method isFunction - * @param {Object} - * @return {Boolean} - */ -var isFunction = function (object) { - return typeof object === 'function'; -}; - -/** - * Returns true if object is Objet, otherwise false - * - * @method isObject - * @param {Object} - * @return {Boolean} - */ -var isObject = function (object) { - return typeof object === 'object'; -}; - -/** - * Returns true if object is boolean, otherwise false - * - * @method isBoolean - * @param {Object} - * @return {Boolean} - */ -var isBoolean = function (object) { - return typeof object === 'boolean'; -}; - -/** - * Returns true if object is array, otherwise false - * - * @method isArray - * @param {Object} - * @return {Boolean} - */ -var isArray = function (object) { - return object instanceof Array; -}; - -/** - * Returns true if given string is valid json object - * - * @method isJson - * @param {String} - * @return {Boolean} - */ -var isJson = function (str) { - try { - return !!JSON.parse(str); - } catch (e) { - return false; - } -}; - -module.exports = { - padLeft: padLeft, - findIndex: findIndex, - toHex: toHex, - toDecimal: toDecimal, - fromDecimal: fromDecimal, - toAscii: toAscii, - fromAscii: fromAscii, - extractDisplayName: extractDisplayName, - extractTypeName: extractTypeName, - toWei: toWei, - fromWei: fromWei, - toBigNumber: toBigNumber, - toTwosComplement: toTwosComplement, - toAddress: toAddress, - isBigNumber: isBigNumber, - isStrictAddress: isStrictAddress, - isAddress: isAddress, - isFunction: isFunction, - isString: isString, - isObject: isObject, - isBoolean: isBoolean, - isArray: isArray, - isJson: isJson -}; - - -},{"bignumber.js":8}],8:[function(require,module,exports){ -/*! bignumber.js v2.0.7 https://github.com/MikeMcl/bignumber.js/LICENCE */ - -;(function (global) { - 'use strict'; - - /* - bignumber.js v2.0.7 - A JavaScript library for arbitrary-precision arithmetic. - https://github.com/MikeMcl/bignumber.js - Copyright (c) 2015 Michael Mclaughlin - MIT Expat Licence - */ - - - var BigNumber, crypto, parseNumeric, - isNumeric = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, - mathceil = Math.ceil, - mathfloor = Math.floor, - notBool = ' not a boolean or binary digit', - roundingMode = 'rounding mode', - tooManyDigits = 'number type has more than 15 significant digits', - ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_', - BASE = 1e14, - LOG_BASE = 14, - MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 - // MAX_INT32 = 0x7fffffff, // 2^31 - 1 - POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], - SQRT_BASE = 1e7, - - /* - * The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and - * the arguments to toExponential, toFixed, toFormat, and toPrecision, beyond which an - * exception is thrown (if ERRORS is true). - */ - MAX = 1E9; // 0 to MAX_INT32 - - - /* - * Create and return a BigNumber constructor. - */ - function another(configObj) { - var div, - - // id tracks the caller function, so its name can be included in error messages. - id = 0, - P = BigNumber.prototype, - ONE = new BigNumber(1), - - - /********************************* EDITABLE DEFAULTS **********************************/ - - - /* - * The default values below must be integers within the inclusive ranges stated. - * The values can also be changed at run-time using BigNumber.config. - */ - - // The maximum number of decimal places for operations involving division. - DECIMAL_PLACES = 20, // 0 to MAX - - /* - * The rounding mode used when rounding to the above decimal places, and when using - * toExponential, toFixed, toFormat and toPrecision, and round (default value). - * UP 0 Away from zero. - * DOWN 1 Towards zero. - * CEIL 2 Towards +Infinity. - * FLOOR 3 Towards -Infinity. - * HALF_UP 4 Towards nearest neighbour. If equidistant, up. - * HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. - * HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. - * HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. - * HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. - */ - ROUNDING_MODE = 4, // 0 to 8 - - // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] - - // The exponent value at and beneath which toString returns exponential notation. - // Number type: -7 - TO_EXP_NEG = -7, // 0 to -MAX - - // The exponent value at and above which toString returns exponential notation. - // Number type: 21 - TO_EXP_POS = 21, // 0 to MAX - - // RANGE : [MIN_EXP, MAX_EXP] - - // The minimum exponent value, beneath which underflow to zero occurs. - // Number type: -324 (5e-324) - MIN_EXP = -1e7, // -1 to -MAX - - // The maximum exponent value, above which overflow to Infinity occurs. - // Number type: 308 (1.7976931348623157e+308) - // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. - MAX_EXP = 1e7, // 1 to MAX - - // Whether BigNumber Errors are ever thrown. - ERRORS = true, // true or false - - // Change to intValidatorNoErrors if ERRORS is false. - isValidInt = intValidatorWithErrors, // intValidatorWithErrors/intValidatorNoErrors - - // Whether to use cryptographically-secure random number generation, if available. - CRYPTO = false, // true or false - - /* - * The modulo mode used when calculating the modulus: a mod n. - * The quotient (q = a / n) is calculated according to the corresponding rounding mode. - * The remainder (r) is calculated as: r = a - n * q. - * - * UP 0 The remainder is positive if the dividend is negative, else is negative. - * DOWN 1 The remainder has the same sign as the dividend. - * This modulo mode is commonly known as 'truncated division' and is - * equivalent to (a % n) in JavaScript. - * FLOOR 3 The remainder has the same sign as the divisor (Python %). - * HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. - * EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). - * The remainder is always positive. - * - * The truncated division, floored division, Euclidian division and IEEE 754 remainder - * modes are commonly used for the modulus operation. - * Although the other rounding modes can also be used, they may not give useful results. - */ - MODULO_MODE = 1, // 0 to 9 - - // The maximum number of significant digits of the result of the toPower operation. - // If POW_PRECISION is 0, there will be unlimited significant digits. - POW_PRECISION = 100, // 0 to MAX - - // The format specification used by the BigNumber.prototype.toFormat method. - FORMAT = { - decimalSeparator: '.', - groupSeparator: ',', - groupSize: 3, - secondaryGroupSize: 0, - fractionGroupSeparator: '\xA0', // non-breaking space - fractionGroupSize: 0 - }; - - - /******************************************************************************************/ - - - // CONSTRUCTOR - - - /* - * The BigNumber constructor and exported function. - * Create and return a new instance of a BigNumber object. - * - * n {number|string|BigNumber} A numeric value. - * [b] {number} The base of n. Integer, 2 to 64 inclusive. - */ - function BigNumber( n, b ) { - var c, e, i, num, len, str, - x = this; - - // Enable constructor usage without new. - if ( !( x instanceof BigNumber ) ) { - - // 'BigNumber() constructor call without new: {n}' - if (ERRORS) raise( 26, 'constructor call without new', n ); - return new BigNumber( n, b ); - } - - // 'new BigNumber() base not an integer: {b}' - // 'new BigNumber() base out of range: {b}' - if ( b == null || !isValidInt( b, 2, 64, id, 'base' ) ) { - - // Duplicate. - if ( n instanceof BigNumber ) { - x.s = n.s; - x.e = n.e; - x.c = ( n = n.c ) ? n.slice() : n; - id = 0; - return; - } - - if ( ( num = typeof n == 'number' ) && n * 0 == 0 ) { - x.s = 1 / n < 0 ? ( n = -n, -1 ) : 1; - - // Fast path for integers. - if ( n === ~~n ) { - for ( e = 0, i = n; i >= 10; i /= 10, e++ ); - x.e = e; - x.c = [n]; - id = 0; - return; - } - - str = n + ''; - } else { - if ( !isNumeric.test( str = n + '' ) ) return parseNumeric( x, str, num ); - x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; - } - } else { - b = b | 0; - str = n + ''; - - // Ensure return value is rounded to DECIMAL_PLACES as with other bases. - // Allow exponential notation to be used with base 10 argument. - if ( b == 10 ) { - x = new BigNumber( n instanceof BigNumber ? n : str ); - return round( x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE ); - } - - // Avoid potential interpretation of Infinity and NaN as base 44+ values. - // Any number in exponential form will fail due to the [Ee][+-]. - if ( ( num = typeof n == 'number' ) && n * 0 != 0 || - !( new RegExp( '^-?' + ( c = '[' + ALPHABET.slice( 0, b ) + ']+' ) + - '(?:\\.' + c + ')?$',b < 37 ? 'i' : '' ) ).test(str) ) { - return parseNumeric( x, str, num, b ); - } - - if (num) { - x.s = 1 / n < 0 ? ( str = str.slice(1), -1 ) : 1; - - if ( ERRORS && str.replace( /^0\.0*|\./, '' ).length > 15 ) { - - // 'new BigNumber() number type has more than 15 significant digits: {n}' - raise( id, tooManyDigits, n ); - } - - // Prevent later check for length on converted number. - num = false; - } else { - x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; - } - - str = convertBase( str, 10, b, x.s ); - } - - // Decimal point? - if ( ( e = str.indexOf('.') ) > -1 ) str = str.replace( '.', '' ); - - // Exponential form? - if ( ( i = str.search( /e/i ) ) > 0 ) { - - // Determine exponent. - if ( e < 0 ) e = i; - e += +str.slice( i + 1 ); - str = str.substring( 0, i ); - } else if ( e < 0 ) { - - // Integer. - e = str.length; - } - - // Determine leading zeros. - for ( i = 0; str.charCodeAt(i) === 48; i++ ); - - // Determine trailing zeros. - for ( len = str.length; str.charCodeAt(--len) === 48; ); - str = str.slice( i, len + 1 ); - - if (str) { - len = str.length; - - // Disallow numbers with over 15 significant digits if number type. - // 'new BigNumber() number type has more than 15 significant digits: {n}' - if ( num && ERRORS && len > 15 ) raise( id, tooManyDigits, x.s * n ); - - e = e - i - 1; - - // Overflow? - if ( e > MAX_EXP ) { - - // Infinity. - x.c = x.e = null; - - // Underflow? - } else if ( e < MIN_EXP ) { - - // Zero. - x.c = [ x.e = 0 ]; - } else { - x.e = e; - x.c = []; - - // Transform base - - // e is the base 10 exponent. - // i is where to slice str to get the first element of the coefficient array. - i = ( e + 1 ) % LOG_BASE; - if ( e < 0 ) i += LOG_BASE; - - if ( i < len ) { - if (i) x.c.push( +str.slice( 0, i ) ); - - for ( len -= LOG_BASE; i < len; ) { - x.c.push( +str.slice( i, i += LOG_BASE ) ); - } - - str = str.slice(i); - i = LOG_BASE - str.length; - } else { - i -= len; - } - - for ( ; i--; str += '0' ); - x.c.push( +str ); - } - } else { - - // Zero. - x.c = [ x.e = 0 ]; - } - - id = 0; - } - - - // CONSTRUCTOR PROPERTIES - - - BigNumber.another = another; - - BigNumber.ROUND_UP = 0; - BigNumber.ROUND_DOWN = 1; - BigNumber.ROUND_CEIL = 2; - BigNumber.ROUND_FLOOR = 3; - BigNumber.ROUND_HALF_UP = 4; - BigNumber.ROUND_HALF_DOWN = 5; - BigNumber.ROUND_HALF_EVEN = 6; - BigNumber.ROUND_HALF_CEIL = 7; - BigNumber.ROUND_HALF_FLOOR = 8; - BigNumber.EUCLID = 9; - - - /* - * Configure infrequently-changing library-wide settings. - * - * Accept an object or an argument list, with one or many of the following properties or - * parameters respectively: - * - * DECIMAL_PLACES {number} Integer, 0 to MAX inclusive - * ROUNDING_MODE {number} Integer, 0 to 8 inclusive - * EXPONENTIAL_AT {number|number[]} Integer, -MAX to MAX inclusive or - * [integer -MAX to 0 incl., 0 to MAX incl.] - * RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or - * [integer -MAX to -1 incl., integer 1 to MAX incl.] - * ERRORS {boolean|number} true, false, 1 or 0 - * CRYPTO {boolean|number} true, false, 1 or 0 - * MODULO_MODE {number} 0 to 9 inclusive - * POW_PRECISION {number} 0 to MAX inclusive - * FORMAT {object} See BigNumber.prototype.toFormat - * decimalSeparator {string} - * groupSeparator {string} - * groupSize {number} - * secondaryGroupSize {number} - * fractionGroupSeparator {string} - * fractionGroupSize {number} - * - * (The values assigned to the above FORMAT object properties are not checked for validity.) - * - * E.g. - * BigNumber.config(20, 4) is equivalent to - * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) - * - * Ignore properties/parameters set to null or undefined. - * Return an object with the properties current values. - */ - BigNumber.config = function () { - var v, p, - i = 0, - r = {}, - a = arguments, - o = a[0], - has = o && typeof o == 'object' - ? function () { if ( o.hasOwnProperty(p) ) return ( v = o[p] ) != null; } - : function () { if ( a.length > i ) return ( v = a[i++] ) != null; }; - - // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. - // 'config() DECIMAL_PLACES not an integer: {v}' - // 'config() DECIMAL_PLACES out of range: {v}' - if ( has( p = 'DECIMAL_PLACES' ) && isValidInt( v, 0, MAX, 2, p ) ) { - DECIMAL_PLACES = v | 0; - } - r[p] = DECIMAL_PLACES; - - // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. - // 'config() ROUNDING_MODE not an integer: {v}' - // 'config() ROUNDING_MODE out of range: {v}' - if ( has( p = 'ROUNDING_MODE' ) && isValidInt( v, 0, 8, 2, p ) ) { - ROUNDING_MODE = v | 0; - } - r[p] = ROUNDING_MODE; - - // EXPONENTIAL_AT {number|number[]} - // Integer, -MAX to MAX inclusive or [integer -MAX to 0 inclusive, 0 to MAX inclusive]. - // 'config() EXPONENTIAL_AT not an integer: {v}' - // 'config() EXPONENTIAL_AT out of range: {v}' - if ( has( p = 'EXPONENTIAL_AT' ) ) { - - if ( isArray(v) ) { - if ( isValidInt( v[0], -MAX, 0, 2, p ) && isValidInt( v[1], 0, MAX, 2, p ) ) { - TO_EXP_NEG = v[0] | 0; - TO_EXP_POS = v[1] | 0; - } - } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { - TO_EXP_NEG = -( TO_EXP_POS = ( v < 0 ? -v : v ) | 0 ); - } - } - r[p] = [ TO_EXP_NEG, TO_EXP_POS ]; - - // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or - // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. - // 'config() RANGE not an integer: {v}' - // 'config() RANGE cannot be zero: {v}' - // 'config() RANGE out of range: {v}' - if ( has( p = 'RANGE' ) ) { - - if ( isArray(v) ) { - if ( isValidInt( v[0], -MAX, -1, 2, p ) && isValidInt( v[1], 1, MAX, 2, p ) ) { - MIN_EXP = v[0] | 0; - MAX_EXP = v[1] | 0; - } - } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { - if ( v | 0 ) MIN_EXP = -( MAX_EXP = ( v < 0 ? -v : v ) | 0 ); - else if (ERRORS) raise( 2, p + ' cannot be zero', v ); - } - } - r[p] = [ MIN_EXP, MAX_EXP ]; - - // ERRORS {boolean|number} true, false, 1 or 0. - // 'config() ERRORS not a boolean or binary digit: {v}' - if ( has( p = 'ERRORS' ) ) { - - if ( v === !!v || v === 1 || v === 0 ) { - id = 0; - isValidInt = ( ERRORS = !!v ) ? intValidatorWithErrors : intValidatorNoErrors; - } else if (ERRORS) { - raise( 2, p + notBool, v ); - } - } - r[p] = ERRORS; - - // CRYPTO {boolean|number} true, false, 1 or 0. - // 'config() CRYPTO not a boolean or binary digit: {v}' - // 'config() crypto unavailable: {crypto}' - if ( has( p = 'CRYPTO' ) ) { - - if ( v === !!v || v === 1 || v === 0 ) { - CRYPTO = !!( v && crypto && typeof crypto == 'object' ); - if ( v && !CRYPTO && ERRORS ) raise( 2, 'crypto unavailable', crypto ); - } else if (ERRORS) { - raise( 2, p + notBool, v ); - } - } - r[p] = CRYPTO; - - // MODULO_MODE {number} Integer, 0 to 9 inclusive. - // 'config() MODULO_MODE not an integer: {v}' - // 'config() MODULO_MODE out of range: {v}' - if ( has( p = 'MODULO_MODE' ) && isValidInt( v, 0, 9, 2, p ) ) { - MODULO_MODE = v | 0; - } - r[p] = MODULO_MODE; - - // POW_PRECISION {number} Integer, 0 to MAX inclusive. - // 'config() POW_PRECISION not an integer: {v}' - // 'config() POW_PRECISION out of range: {v}' - if ( has( p = 'POW_PRECISION' ) && isValidInt( v, 0, MAX, 2, p ) ) { - POW_PRECISION = v | 0; - } - r[p] = POW_PRECISION; - - // FORMAT {object} - // 'config() FORMAT not an object: {v}' - if ( has( p = 'FORMAT' ) ) { - - if ( typeof v == 'object' ) { - FORMAT = v; - } else if (ERRORS) { - raise( 2, p + ' not an object', v ); - } - } - r[p] = FORMAT; - - return r; - }; - - - /* - * Return a new BigNumber whose value is the maximum of the arguments. - * - * arguments {number|string|BigNumber} - */ - BigNumber.max = function () { return maxOrMin( arguments, P.lt ); }; - - - /* - * Return a new BigNumber whose value is the minimum of the arguments. - * - * arguments {number|string|BigNumber} - */ - BigNumber.min = function () { return maxOrMin( arguments, P.gt ); }; - - - /* - * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, - * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing - * zeros are produced). - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * - * 'random() decimal places not an integer: {dp}' - * 'random() decimal places out of range: {dp}' - * 'random() crypto unavailable: {crypto}' - */ - BigNumber.random = (function () { - var pow2_53 = 0x20000000000000; - - // Return a 53 bit integer n, where 0 <= n < 9007199254740992. - // Check if Math.random() produces more than 32 bits of randomness. - // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. - // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. - var random53bitInt = (Math.random() * pow2_53) & 0x1fffff - ? function () { return mathfloor( Math.random() * pow2_53 ); } - : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + - (Math.random() * 0x800000 | 0); }; - - return function (dp) { - var a, b, e, k, v, - i = 0, - c = [], - rand = new BigNumber(ONE); - - dp = dp == null || !isValidInt( dp, 0, MAX, 14 ) ? DECIMAL_PLACES : dp | 0; - k = mathceil( dp / LOG_BASE ); - - if (CRYPTO) { - - // Browsers supporting crypto.getRandomValues. - if ( crypto && crypto.getRandomValues ) { - - a = crypto.getRandomValues( new Uint32Array( k *= 2 ) ); - - for ( ; i < k; ) { - - // 53 bits: - // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) - // 11111 11111111 11111111 11111111 11100000 00000000 00000000 - // ((Math.pow(2, 32) - 1) >>> 11).toString(2) - // 11111 11111111 11111111 - // 0x20000 is 2^21. - v = a[i] * 0x20000 + (a[i + 1] >>> 11); - - // Rejection sampling: - // 0 <= v < 9007199254740992 - // Probability that v >= 9e15, is - // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 - if ( v >= 9e15 ) { - b = crypto.getRandomValues( new Uint32Array(2) ); - a[i] = b[0]; - a[i + 1] = b[1]; - } else { - - // 0 <= v <= 8999999999999999 - // 0 <= (v % 1e14) <= 99999999999999 - c.push( v % 1e14 ); - i += 2; - } - } - i = k / 2; - - // Node.js supporting crypto.randomBytes. - } else if ( crypto && crypto.randomBytes ) { - - // buffer - a = crypto.randomBytes( k *= 7 ); - - for ( ; i < k; ) { - - // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 - // 0x100000000 is 2^32, 0x1000000 is 2^24 - // 11111 11111111 11111111 11111111 11111111 11111111 11111111 - // 0 <= v < 9007199254740992 - v = ( ( a[i] & 31 ) * 0x1000000000000 ) + ( a[i + 1] * 0x10000000000 ) + - ( a[i + 2] * 0x100000000 ) + ( a[i + 3] * 0x1000000 ) + - ( a[i + 4] << 16 ) + ( a[i + 5] << 8 ) + a[i + 6]; - - if ( v >= 9e15 ) { - crypto.randomBytes(7).copy( a, i ); - } else { - - // 0 <= (v % 1e14) <= 99999999999999 - c.push( v % 1e14 ); - i += 7; - } - } - i = k / 7; - } else if (ERRORS) { - raise( 14, 'crypto unavailable', crypto ); - } - } - - // Use Math.random: CRYPTO is false or crypto is unavailable and ERRORS is false. - if (!i) { - - for ( ; i < k; ) { - v = random53bitInt(); - if ( v < 9e15 ) c[i++] = v % 1e14; - } - } - - k = c[--i]; - dp %= LOG_BASE; - - // Convert trailing digits to zeros according to dp. - if ( k && dp ) { - v = POWS_TEN[LOG_BASE - dp]; - c[i] = mathfloor( k / v ) * v; - } - - // Remove trailing elements which are zero. - for ( ; c[i] === 0; c.pop(), i-- ); - - // Zero? - if ( i < 0 ) { - c = [ e = 0 ]; - } else { - - // Remove leading elements which are zero and adjust exponent accordingly. - for ( e = -1 ; c[0] === 0; c.shift(), e -= LOG_BASE); - - // Count the digits of the first element of c to determine leading zeros, and... - for ( i = 1, v = c[0]; v >= 10; v /= 10, i++); - - // adjust the exponent accordingly. - if ( i < LOG_BASE ) e -= LOG_BASE - i; - } - - rand.e = e; - rand.c = c; - return rand; - }; - })(); - - - // PRIVATE FUNCTIONS - - - // Convert a numeric string of baseIn to a numeric string of baseOut. - function convertBase( str, baseOut, baseIn, sign ) { - var d, e, k, r, x, xc, y, - i = str.indexOf( '.' ), - dp = DECIMAL_PLACES, - rm = ROUNDING_MODE; - - if ( baseIn < 37 ) str = str.toLowerCase(); - - // Non-integer. - if ( i >= 0 ) { - k = POW_PRECISION; - - // Unlimited precision. - POW_PRECISION = 0; - str = str.replace( '.', '' ); - y = new BigNumber(baseIn); - x = y.pow( str.length - i ); - POW_PRECISION = k; - - // Convert str as if an integer, then restore the fraction part by dividing the - // result by its base raised to a power. - y.c = toBaseOut( toFixedPoint( coeffToString( x.c ), x.e ), 10, baseOut ); - y.e = y.c.length; - } - - // Convert the number as integer. - xc = toBaseOut( str, baseIn, baseOut ); - e = k = xc.length; - - // Remove trailing zeros. - for ( ; xc[--k] == 0; xc.pop() ); - if ( !xc[0] ) return '0'; - - if ( i < 0 ) { - --e; - } else { - x.c = xc; - x.e = e; - - // sign is needed for correct rounding. - x.s = sign; - x = div( x, y, dp, rm, baseOut ); - xc = x.c; - r = x.r; - e = x.e; - } - - d = e + dp + 1; - - // The rounding digit, i.e. the digit to the right of the digit that may be rounded up. - i = xc[d]; - k = baseOut / 2; - r = r || d < 0 || xc[d + 1] != null; - - r = rm < 4 ? ( i != null || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) - : i > k || i == k &&( rm == 4 || r || rm == 6 && xc[d - 1] & 1 || - rm == ( x.s < 0 ? 8 : 7 ) ); - - if ( d < 1 || !xc[0] ) { - - // 1^-dp or 0. - str = r ? toFixedPoint( '1', -dp ) : '0'; - } else { - xc.length = d; - - if (r) { - - // Rounding up may mean the previous digit has to be rounded up and so on. - for ( --baseOut; ++xc[--d] > baseOut; ) { - xc[d] = 0; - - if ( !d ) { - ++e; - xc.unshift(1); - } - } - } - - // Determine trailing zeros. - for ( k = xc.length; !xc[--k]; ); - - // E.g. [4, 11, 15] becomes 4bf. - for ( i = 0, str = ''; i <= k; str += ALPHABET.charAt( xc[i++] ) ); - str = toFixedPoint( str, e ); - } - - // The caller will add the sign. - return str; - } - - - // Perform division in the specified base. Called by div and convertBase. - div = (function () { - - // Assume non-zero x and k. - function multiply( x, k, base ) { - var m, temp, xlo, xhi, - carry = 0, - i = x.length, - klo = k % SQRT_BASE, - khi = k / SQRT_BASE | 0; - - for ( x = x.slice(); i--; ) { - xlo = x[i] % SQRT_BASE; - xhi = x[i] / SQRT_BASE | 0; - m = khi * xlo + xhi * klo; - temp = klo * xlo + ( ( m % SQRT_BASE ) * SQRT_BASE ) + carry; - carry = ( temp / base | 0 ) + ( m / SQRT_BASE | 0 ) + khi * xhi; - x[i] = temp % base; - } - - if (carry) x.unshift(carry); - - return x; - } - - function compare( a, b, aL, bL ) { - var i, cmp; - - if ( aL != bL ) { - cmp = aL > bL ? 1 : -1; - } else { - - for ( i = cmp = 0; i < aL; i++ ) { - - if ( a[i] != b[i] ) { - cmp = a[i] > b[i] ? 1 : -1; - break; - } - } - } - return cmp; - } - - function subtract( a, b, aL, base ) { - var i = 0; - - // Subtract b from a. - for ( ; aL--; ) { - a[aL] -= i; - i = a[aL] < b[aL] ? 1 : 0; - a[aL] = i * base + a[aL] - b[aL]; - } - - // Remove leading zeros. - for ( ; !a[0] && a.length > 1; a.shift() ); - } - - // x: dividend, y: divisor. - return function ( x, y, dp, rm, base ) { - var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, - yL, yz, - s = x.s == y.s ? 1 : -1, - xc = x.c, - yc = y.c; - - // Either NaN, Infinity or 0? - if ( !xc || !xc[0] || !yc || !yc[0] ) { - - return new BigNumber( - - // Return NaN if either NaN, or both Infinity or 0. - !x.s || !y.s || ( xc ? yc && xc[0] == yc[0] : !yc ) ? NaN : - - // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. - xc && xc[0] == 0 || !yc ? s * 0 : s / 0 - ); - } - - q = new BigNumber(s); - qc = q.c = []; - e = x.e - y.e; - s = dp + e + 1; - - if ( !base ) { - base = BASE; - e = bitFloor( x.e / LOG_BASE ) - bitFloor( y.e / LOG_BASE ); - s = s / LOG_BASE | 0; - } - - // Result exponent may be one less then the current value of e. - // The coefficients of the BigNumbers from convertBase may have trailing zeros. - for ( i = 0; yc[i] == ( xc[i] || 0 ); i++ ); - if ( yc[i] > ( xc[i] || 0 ) ) e--; - - if ( s < 0 ) { - qc.push(1); - more = true; - } else { - xL = xc.length; - yL = yc.length; - i = 0; - s += 2; - - // Normalise xc and yc so highest order digit of yc is >= base / 2. - - n = mathfloor( base / ( yc[0] + 1 ) ); - - // Not necessary, but to handle odd bases where yc[0] == ( base / 2 ) - 1. - // if ( n > 1 || n++ == 1 && yc[0] < base / 2 ) { - if ( n > 1 ) { - yc = multiply( yc, n, base ); - xc = multiply( xc, n, base ); - yL = yc.length; - xL = xc.length; - } - - xi = yL; - rem = xc.slice( 0, yL ); - remL = rem.length; - - // Add zeros to make remainder as long as divisor. - for ( ; remL < yL; rem[remL++] = 0 ); - yz = yc.slice(); - yz.unshift(0); - yc0 = yc[0]; - if ( yc[1] >= base / 2 ) yc0++; - // Not necessary, but to prevent trial digit n > base, when using base 3. - // else if ( base == 3 && yc0 == 1 ) yc0 = 1 + 1e-15; - - do { - n = 0; - - // Compare divisor and remainder. - cmp = compare( yc, rem, yL, remL ); - - // If divisor < remainder. - if ( cmp < 0 ) { - - // Calculate trial digit, n. - - rem0 = rem[0]; - if ( yL != remL ) rem0 = rem0 * base + ( rem[1] || 0 ); - - // n is how many times the divisor goes into the current remainder. - n = mathfloor( rem0 / yc0 ); - - // Algorithm: - // 1. product = divisor * trial digit (n) - // 2. if product > remainder: product -= divisor, n-- - // 3. remainder -= product - // 4. if product was < remainder at 2: - // 5. compare new remainder and divisor - // 6. If remainder > divisor: remainder -= divisor, n++ - - if ( n > 1 ) { - - // n may be > base only when base is 3. - if (n >= base) n = base - 1; - - // product = divisor * trial digit. - prod = multiply( yc, n, base ); - prodL = prod.length; - remL = rem.length; - - // Compare product and remainder. - // If product > remainder. - // Trial digit n too high. - // n is 1 too high about 5% of the time, and is not known to have - // ever been more than 1 too high. - while ( compare( prod, rem, prodL, remL ) == 1 ) { - n--; - - // Subtract divisor from product. - subtract( prod, yL < prodL ? yz : yc, prodL, base ); - prodL = prod.length; - cmp = 1; - } - } else { - - // n is 0 or 1, cmp is -1. - // If n is 0, there is no need to compare yc and rem again below, - // so change cmp to 1 to avoid it. - // If n is 1, leave cmp as -1, so yc and rem are compared again. - if ( n == 0 ) { - - // divisor < remainder, so n must be at least 1. - cmp = n = 1; - } - - // product = divisor - prod = yc.slice(); - prodL = prod.length; - } - - if ( prodL < remL ) prod.unshift(0); - - // Subtract product from remainder. - subtract( rem, prod, remL, base ); - remL = rem.length; - - // If product was < remainder. - if ( cmp == -1 ) { - - // Compare divisor and new remainder. - // If divisor < new remainder, subtract divisor from remainder. - // Trial digit n too low. - // n is 1 too low about 5% of the time, and very rarely 2 too low. - while ( compare( yc, rem, yL, remL ) < 1 ) { - n++; - - // Subtract divisor from remainder. - subtract( rem, yL < remL ? yz : yc, remL, base ); - remL = rem.length; - } - } - } else if ( cmp === 0 ) { - n++; - rem = [0]; - } // else cmp === 1 and n will be 0 - - // Add the next digit, n, to the result array. - qc[i++] = n; - - // Update the remainder. - if ( rem[0] ) { - rem[remL++] = xc[xi] || 0; - } else { - rem = [ xc[xi] ]; - remL = 1; - } - } while ( ( xi++ < xL || rem[0] != null ) && s-- ); - - more = rem[0] != null; - - // Leading zero? - if ( !qc[0] ) qc.shift(); - } - - if ( base == BASE ) { - - // To calculate q.e, first get the number of digits of qc[0]. - for ( i = 1, s = qc[0]; s >= 10; s /= 10, i++ ); - round( q, dp + ( q.e = i + e * LOG_BASE - 1 ) + 1, rm, more ); - - // Caller is convertBase. - } else { - q.e = e; - q.r = +more; - } - - return q; - }; - })(); - - - /* - * Return a string representing the value of BigNumber n in fixed-point or exponential - * notation rounded to the specified decimal places or significant digits. - * - * n is a BigNumber. - * i is the index of the last digit required (i.e. the digit that may be rounded up). - * rm is the rounding mode. - * caller is caller id: toExponential 19, toFixed 20, toFormat 21, toPrecision 24. - */ - function format( n, i, rm, caller ) { - var c0, e, ne, len, str; - - rm = rm != null && isValidInt( rm, 0, 8, caller, roundingMode ) - ? rm | 0 : ROUNDING_MODE; - - if ( !n.c ) return n.toString(); - c0 = n.c[0]; - ne = n.e; - - if ( i == null ) { - str = coeffToString( n.c ); - str = caller == 19 || caller == 24 && ne <= TO_EXP_NEG - ? toExponential( str, ne ) - : toFixedPoint( str, ne ); - } else { - n = round( new BigNumber(n), i, rm ); - - // n.e may have changed if the value was rounded up. - e = n.e; - - str = coeffToString( n.c ); - len = str.length; - - // toPrecision returns exponential notation if the number of significant digits - // specified is less than the number of digits necessary to represent the integer - // part of the value in fixed-point notation. - - // Exponential notation. - if ( caller == 19 || caller == 24 && ( i <= e || e <= TO_EXP_NEG ) ) { - - // Append zeros? - for ( ; len < i; str += '0', len++ ); - str = toExponential( str, e ); - - // Fixed-point notation. - } else { - i -= ne; - str = toFixedPoint( str, e ); - - // Append zeros? - if ( e + 1 > len ) { - if ( --i > 0 ) for ( str += '.'; i--; str += '0' ); - } else { - i += e - len; - if ( i > 0 ) { - if ( e + 1 == len ) str += '.'; - for ( ; i--; str += '0' ); - } - } - } - } - - return n.s < 0 && c0 ? '-' + str : str; - } - - - // Handle BigNumber.max and BigNumber.min. - function maxOrMin( args, method ) { - var m, n, - i = 0; - - if ( isArray( args[0] ) ) args = args[0]; - m = new BigNumber( args[0] ); - - for ( ; ++i < args.length; ) { - n = new BigNumber( args[i] ); - - // If any number is NaN, return NaN. - if ( !n.s ) { - m = n; - break; - } else if ( method.call( m, n ) ) { - m = n; - } - } - - return m; - } - - - /* - * Return true if n is an integer in range, otherwise throw. - * Use for argument validation when ERRORS is true. - */ - function intValidatorWithErrors( n, min, max, caller, name ) { - if ( n < min || n > max || n != truncate(n) ) { - raise( caller, ( name || 'decimal places' ) + - ( n < min || n > max ? ' out of range' : ' not an integer' ), n ); - } - - return true; - } - - - /* - * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. - * Called by minus, plus and times. - */ - function normalise( n, c, e ) { - var i = 1, - j = c.length; - - // Remove trailing zeros. - for ( ; !c[--j]; c.pop() ); - - // Calculate the base 10 exponent. First get the number of digits of c[0]. - for ( j = c[0]; j >= 10; j /= 10, i++ ); - - // Overflow? - if ( ( e = i + e * LOG_BASE - 1 ) > MAX_EXP ) { - - // Infinity. - n.c = n.e = null; - - // Underflow? - } else if ( e < MIN_EXP ) { - - // Zero. - n.c = [ n.e = 0 ]; - } else { - n.e = e; - n.c = c; - } - - return n; - } - - - // Handle values that fail the validity test in BigNumber. - parseNumeric = (function () { - var basePrefix=/^(-?)0([xbo])/i, - dotAfter=/^([^.]+)\.$/, - dotBefore=/^\.([^.]+)$/, - isInfinityOrNaN=/^-?(Infinity|NaN)$/, - whitespaceOrPlus=/^\s*\+|^\s+|\s+$/g; - - return function ( x, str, num, b ) { - var base, - s = num ? str : str.replace( whitespaceOrPlus, '' ); - - // No exception on ±Infinity or NaN. - if ( isInfinityOrNaN.test(s) ) { - x.s = isNaN(s) ? null : s < 0 ? -1 : 1; - } else { - if ( !num ) { - - // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i - s = s.replace( basePrefix, function ( m, p1, p2 ) { - base = ( p2 = p2.toLowerCase() ) == 'x' ? 16 : p2 == 'b' ? 2 : 8; - return !b || b == base ? p1 : m; - }); - - if (b) { - base = b; - - // E.g. '1.' to '1', '.1' to '0.1' - s = s.replace( dotAfter, '$1' ).replace( dotBefore, '0.$1' ); - } - - if ( str != s ) return new BigNumber( s, base ); - } - - // 'new BigNumber() not a number: {n}' - // 'new BigNumber() not a base {b} number: {n}' - if (ERRORS) raise( id, 'not a' + ( b ? ' base ' + b : '' ) + ' number', str ); - x.s = null; - } - - x.c = x.e = null; - id = 0; - } - })(); - - - // Throw a BigNumber Error. - function raise( caller, msg, val ) { - var error = new Error( [ - 'new BigNumber', // 0 - 'cmp', // 1 - 'config', // 2 - 'div', // 3 - 'divToInt', // 4 - 'eq', // 5 - 'gt', // 6 - 'gte', // 7 - 'lt', // 8 - 'lte', // 9 - 'minus', // 10 - 'mod', // 11 - 'plus', // 12 - 'precision', // 13 - 'random', // 14 - 'round', // 15 - 'shift', // 16 - 'times', // 17 - 'toDigits', // 18 - 'toExponential', // 19 - 'toFixed', // 20 - 'toFormat', // 21 - 'toFraction', // 22 - 'pow', // 23 - 'toPrecision', // 24 - 'toString', // 25 - 'BigNumber' // 26 - ][caller] + '() ' + msg + ': ' + val ); - - error.name = 'BigNumber Error'; - id = 0; - throw error; - } - - - /* - * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. - * If r is truthy, it is known that there are more digits after the rounding digit. - */ - function round( x, sd, rm, r ) { - var d, i, j, k, n, ni, rd, - xc = x.c, - pows10 = POWS_TEN; - - // if x is not Infinity or NaN... - if (xc) { - - // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. - // n is a base 1e14 number, the value of the element of array x.c containing rd. - // ni is the index of n within x.c. - // d is the number of digits of n. - // i is the index of rd within n including leading zeros. - // j is the actual index of rd within n (if < 0, rd is a leading zero). - out: { - - // Get the number of digits of the first element of xc. - for ( d = 1, k = xc[0]; k >= 10; k /= 10, d++ ); - i = sd - d; - - // If the rounding digit is in the first element of xc... - if ( i < 0 ) { - i += LOG_BASE; - j = sd; - n = xc[ ni = 0 ]; - - // Get the rounding digit at index j of n. - rd = n / pows10[ d - j - 1 ] % 10 | 0; - } else { - ni = mathceil( ( i + 1 ) / LOG_BASE ); - - if ( ni >= xc.length ) { - - if (r) { - - // Needed by sqrt. - for ( ; xc.length <= ni; xc.push(0) ); - n = rd = 0; - d = 1; - i %= LOG_BASE; - j = i - LOG_BASE + 1; - } else { - break out; - } - } else { - n = k = xc[ni]; - - // Get the number of digits of n. - for ( d = 1; k >= 10; k /= 10, d++ ); - - // Get the index of rd within n. - i %= LOG_BASE; - - // Get the index of rd within n, adjusted for leading zeros. - // The number of leading zeros of n is given by LOG_BASE - d. - j = i - LOG_BASE + d; - - // Get the rounding digit at index j of n. - rd = j < 0 ? 0 : n / pows10[ d - j - 1 ] % 10 | 0; - } - } - - r = r || sd < 0 || - - // Are there any non-zero digits after the rounding digit? - // The expression n % pows10[ d - j - 1 ] returns all digits of n to the right - // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. - xc[ni + 1] != null || ( j < 0 ? n : n % pows10[ d - j - 1 ] ); - - r = rm < 4 - ? ( rd || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) - : rd > 5 || rd == 5 && ( rm == 4 || r || rm == 6 && - - // Check whether the digit to the left of the rounding digit is odd. - ( ( i > 0 ? j > 0 ? n / pows10[ d - j ] : 0 : xc[ni - 1] ) % 10 ) & 1 || - rm == ( x.s < 0 ? 8 : 7 ) ); - - if ( sd < 1 || !xc[0] ) { - xc.length = 0; - - if (r) { - - // Convert sd to decimal places. - sd -= x.e + 1; - - // 1, 0.1, 0.01, 0.001, 0.0001 etc. - xc[0] = pows10[ sd % LOG_BASE ]; - x.e = -sd || 0; - } else { - - // Zero. - xc[0] = x.e = 0; - } - - return x; - } - - // Remove excess digits. - if ( i == 0 ) { - xc.length = ni; - k = 1; - ni--; - } else { - xc.length = ni + 1; - k = pows10[ LOG_BASE - i ]; - - // E.g. 56700 becomes 56000 if 7 is the rounding digit. - // j > 0 means i > number of leading zeros of n. - xc[ni] = j > 0 ? mathfloor( n / pows10[ d - j ] % pows10[j] ) * k : 0; - } - - // Round up? - if (r) { - - for ( ; ; ) { - - // If the digit to be rounded up is in the first element of xc... - if ( ni == 0 ) { - - // i will be the length of xc[0] before k is added. - for ( i = 1, j = xc[0]; j >= 10; j /= 10, i++ ); - j = xc[0] += k; - for ( k = 1; j >= 10; j /= 10, k++ ); - - // if i != k the length has increased. - if ( i != k ) { - x.e++; - if ( xc[0] == BASE ) xc[0] = 1; - } - - break; - } else { - xc[ni] += k; - if ( xc[ni] != BASE ) break; - xc[ni--] = 0; - k = 1; - } - } - } - - // Remove trailing zeros. - for ( i = xc.length; xc[--i] === 0; xc.pop() ); - } - - // Overflow? Infinity. - if ( x.e > MAX_EXP ) { - x.c = x.e = null; - - // Underflow? Zero. - } else if ( x.e < MIN_EXP ) { - x.c = [ x.e = 0 ]; - } - } - - return x; - } - - - // PROTOTYPE/INSTANCE METHODS - - - /* - * Return a new BigNumber whose value is the absolute value of this BigNumber. - */ - P.absoluteValue = P.abs = function () { - var x = new BigNumber(this); - if ( x.s < 0 ) x.s = 1; - return x; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole - * number in the direction of Infinity. - */ - P.ceil = function () { - return round( new BigNumber(this), this.e + 1, 2 ); - }; - - - /* - * Return - * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), - * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), - * 0 if they have the same value, - * or null if the value of either is NaN. - */ - P.comparedTo = P.cmp = function ( y, b ) { - id = 1; - return compare( this, new BigNumber( y, b ) ); - }; - - - /* - * Return the number of decimal places of the value of this BigNumber, or null if the value - * of this BigNumber is ±Infinity or NaN. - */ - P.decimalPlaces = P.dp = function () { - var n, v, - c = this.c; - - if ( !c ) return null; - n = ( ( v = c.length - 1 ) - bitFloor( this.e / LOG_BASE ) ) * LOG_BASE; - - // Subtract the number of trailing zeros of the last number. - if ( v = c[v] ) for ( ; v % 10 == 0; v /= 10, n-- ); - if ( n < 0 ) n = 0; - - return n; - }; - - - /* - * n / 0 = I - * n / N = N - * n / I = 0 - * 0 / n = 0 - * 0 / 0 = N - * 0 / N = N - * 0 / I = 0 - * N / n = N - * N / 0 = N - * N / N = N - * N / I = N - * I / n = I - * I / 0 = I - * I / N = N - * I / I = N - * - * Return a new BigNumber whose value is the value of this BigNumber divided by the value of - * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. - */ - P.dividedBy = P.div = function ( y, b ) { - id = 3; - return div( this, new BigNumber( y, b ), DECIMAL_PLACES, ROUNDING_MODE ); - }; - - - /* - * Return a new BigNumber whose value is the integer part of dividing the value of this - * BigNumber by the value of BigNumber(y, b). - */ - P.dividedToIntegerBy = P.divToInt = function ( y, b ) { - id = 4; - return div( this, new BigNumber( y, b ), 0, 1 ); - }; - - - /* - * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), - * otherwise returns false. - */ - P.equals = P.eq = function ( y, b ) { - id = 5; - return compare( this, new BigNumber( y, b ) ) === 0; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole - * number in the direction of -Infinity. - */ - P.floor = function () { - return round( new BigNumber(this), this.e + 1, 3 ); - }; - - - /* - * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), - * otherwise returns false. - */ - P.greaterThan = P.gt = function ( y, b ) { - id = 6; - return compare( this, new BigNumber( y, b ) ) > 0; - }; - - - /* - * Return true if the value of this BigNumber is greater than or equal to the value of - * BigNumber(y, b), otherwise returns false. - */ - P.greaterThanOrEqualTo = P.gte = function ( y, b ) { - id = 7; - return ( b = compare( this, new BigNumber( y, b ) ) ) === 1 || b === 0; - - }; - - - /* - * Return true if the value of this BigNumber is a finite number, otherwise returns false. - */ - P.isFinite = function () { - return !!this.c; - }; - - - /* - * Return true if the value of this BigNumber is an integer, otherwise return false. - */ - P.isInteger = P.isInt = function () { - return !!this.c && bitFloor( this.e / LOG_BASE ) > this.c.length - 2; - }; - - - /* - * Return true if the value of this BigNumber is NaN, otherwise returns false. - */ - P.isNaN = function () { - return !this.s; - }; - - - /* - * Return true if the value of this BigNumber is negative, otherwise returns false. - */ - P.isNegative = P.isNeg = function () { - return this.s < 0; - }; - - - /* - * Return true if the value of this BigNumber is 0 or -0, otherwise returns false. - */ - P.isZero = function () { - return !!this.c && this.c[0] == 0; - }; - - - /* - * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), - * otherwise returns false. - */ - P.lessThan = P.lt = function ( y, b ) { - id = 8; - return compare( this, new BigNumber( y, b ) ) < 0; - }; - - - /* - * Return true if the value of this BigNumber is less than or equal to the value of - * BigNumber(y, b), otherwise returns false. - */ - P.lessThanOrEqualTo = P.lte = function ( y, b ) { - id = 9; - return ( b = compare( this, new BigNumber( y, b ) ) ) === -1 || b === 0; - }; - - - /* - * n - 0 = n - * n - N = N - * n - I = -I - * 0 - n = -n - * 0 - 0 = 0 - * 0 - N = N - * 0 - I = -I - * N - n = N - * N - 0 = N - * N - N = N - * N - I = N - * I - n = I - * I - 0 = I - * I - N = N - * I - I = N - * - * Return a new BigNumber whose value is the value of this BigNumber minus the value of - * BigNumber(y, b). - */ - P.minus = P.sub = function ( y, b ) { - var i, j, t, xLTy, - x = this, - a = x.s; - - id = 10; - y = new BigNumber( y, b ); - b = y.s; - - // Either NaN? - if ( !a || !b ) return new BigNumber(NaN); - - // Signs differ? - if ( a != b ) { - y.s = -b; - return x.plus(y); - } - - var xe = x.e / LOG_BASE, - ye = y.e / LOG_BASE, - xc = x.c, - yc = y.c; - - if ( !xe || !ye ) { - - // Either Infinity? - if ( !xc || !yc ) return xc ? ( y.s = -b, y ) : new BigNumber( yc ? x : NaN ); - - // Either zero? - if ( !xc[0] || !yc[0] ) { - - // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. - return yc[0] ? ( y.s = -b, y ) : new BigNumber( xc[0] ? x : - - // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity - ROUNDING_MODE == 3 ? -0 : 0 ); - } - } - - xe = bitFloor(xe); - ye = bitFloor(ye); - xc = xc.slice(); - - // Determine which is the bigger number. - if ( a = xe - ye ) { - - if ( xLTy = a < 0 ) { - a = -a; - t = xc; - } else { - ye = xe; - t = yc; - } - - t.reverse(); - - // Prepend zeros to equalise exponents. - for ( b = a; b--; t.push(0) ); - t.reverse(); - } else { - - // Exponents equal. Check digit by digit. - j = ( xLTy = ( a = xc.length ) < ( b = yc.length ) ) ? a : b; - - for ( a = b = 0; b < j; b++ ) { - - if ( xc[b] != yc[b] ) { - xLTy = xc[b] < yc[b]; - break; - } - } - } - - // x < y? Point xc to the array of the bigger number. - if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s; - - b = ( j = yc.length ) - ( i = xc.length ); - - // Append zeros to xc if shorter. - // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. - if ( b > 0 ) for ( ; b--; xc[i++] = 0 ); - b = BASE - 1; - - // Subtract yc from xc. - for ( ; j > a; ) { - - if ( xc[--j] < yc[j] ) { - for ( i = j; i && !xc[--i]; xc[i] = b ); - --xc[i]; - xc[j] += BASE; - } - - xc[j] -= yc[j]; - } - - // Remove leading zeros and adjust exponent accordingly. - for ( ; xc[0] == 0; xc.shift(), --ye ); - - // Zero? - if ( !xc[0] ) { - - // Following IEEE 754 (2008) 6.3, - // n - n = +0 but n - n = -0 when rounding towards -Infinity. - y.s = ROUNDING_MODE == 3 ? -1 : 1; - y.c = [ y.e = 0 ]; - return y; - } - - // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity - // for finite x and y. - return normalise( y, xc, ye ); - }; - - - /* - * n % 0 = N - * n % N = N - * n % I = n - * 0 % n = 0 - * -0 % n = -0 - * 0 % 0 = N - * 0 % N = N - * 0 % I = 0 - * N % n = N - * N % 0 = N - * N % N = N - * N % I = N - * I % n = N - * I % 0 = N - * I % N = N - * I % I = N - * - * Return a new BigNumber whose value is the value of this BigNumber modulo the value of - * BigNumber(y, b). The result depends on the value of MODULO_MODE. - */ - P.modulo = P.mod = function ( y, b ) { - var q, s, - x = this; - - id = 11; - y = new BigNumber( y, b ); - - // Return NaN if x is Infinity or NaN, or y is NaN or zero. - if ( !x.c || !y.s || y.c && !y.c[0] ) { - return new BigNumber(NaN); - - // Return x if y is Infinity or x is zero. - } else if ( !y.c || x.c && !x.c[0] ) { - return new BigNumber(x); - } - - if ( MODULO_MODE == 9 ) { - - // Euclidian division: q = sign(y) * floor(x / abs(y)) - // r = x - qy where 0 <= r < abs(y) - s = y.s; - y.s = 1; - q = div( x, y, 0, 3 ); - y.s = s; - q.s *= s; - } else { - q = div( x, y, 0, MODULO_MODE ); - } - - return x.minus( q.times(y) ); - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber negated, - * i.e. multiplied by -1. - */ - P.negated = P.neg = function () { - var x = new BigNumber(this); - x.s = -x.s || null; - return x; - }; - - - /* - * n + 0 = n - * n + N = N - * n + I = I - * 0 + n = n - * 0 + 0 = 0 - * 0 + N = N - * 0 + I = I - * N + n = N - * N + 0 = N - * N + N = N - * N + I = N - * I + n = I - * I + 0 = I - * I + N = N - * I + I = I - * - * Return a new BigNumber whose value is the value of this BigNumber plus the value of - * BigNumber(y, b). - */ - P.plus = P.add = function ( y, b ) { - var t, - x = this, - a = x.s; - - id = 12; - y = new BigNumber( y, b ); - b = y.s; - - // Either NaN? - if ( !a || !b ) return new BigNumber(NaN); - - // Signs differ? - if ( a != b ) { - y.s = -b; - return x.minus(y); - } - - var xe = x.e / LOG_BASE, - ye = y.e / LOG_BASE, - xc = x.c, - yc = y.c; - - if ( !xe || !ye ) { - - // Return ±Infinity if either ±Infinity. - if ( !xc || !yc ) return new BigNumber( a / 0 ); - - // Either zero? - // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. - if ( !xc[0] || !yc[0] ) return yc[0] ? y : new BigNumber( xc[0] ? x : a * 0 ); - } - - xe = bitFloor(xe); - ye = bitFloor(ye); - xc = xc.slice(); - - // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. - if ( a = xe - ye ) { - if ( a > 0 ) { - ye = xe; - t = yc; - } else { - a = -a; - t = xc; - } - - t.reverse(); - for ( ; a--; t.push(0) ); - t.reverse(); - } - - a = xc.length; - b = yc.length; - - // Point xc to the longer array, and b to the shorter length. - if ( a - b < 0 ) t = yc, yc = xc, xc = t, b = a; - - // Only start adding at yc.length - 1 as the further digits of xc can be ignored. - for ( a = 0; b; ) { - a = ( xc[--b] = xc[b] + yc[b] + a ) / BASE | 0; - xc[b] %= BASE; - } - - if (a) { - xc.unshift(a); - ++ye; - } - - // No need to check for zero, as +x + +y != 0 && -x + -y != 0 - // ye = MAX_EXP + 1 possible - return normalise( y, xc, ye ); - }; - - - /* - * Return the number of significant digits of the value of this BigNumber. - * - * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0. - */ - P.precision = P.sd = function (z) { - var n, v, - x = this, - c = x.c; - - // 'precision() argument not a boolean or binary digit: {z}' - if ( z != null && z !== !!z && z !== 1 && z !== 0 ) { - if (ERRORS) raise( 13, 'argument' + notBool, z ); - if ( z != !!z ) z = null; - } - - if ( !c ) return null; - v = c.length - 1; - n = v * LOG_BASE + 1; - - if ( v = c[v] ) { - - // Subtract the number of trailing zeros of the last element. - for ( ; v % 10 == 0; v /= 10, n-- ); - - // Add the number of digits of the first element. - for ( v = c[0]; v >= 10; v /= 10, n++ ); - } - - if ( z && x.e + 1 > n ) n = x.e + 1; - - return n; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of - * dp decimal places using rounding mode rm, or to 0 and ROUNDING_MODE respectively if - * omitted. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'round() decimal places out of range: {dp}' - * 'round() decimal places not an integer: {dp}' - * 'round() rounding mode not an integer: {rm}' - * 'round() rounding mode out of range: {rm}' - */ - P.round = function ( dp, rm ) { - var n = new BigNumber(this); - - if ( dp == null || isValidInt( dp, 0, MAX, 15 ) ) { - round( n, ~~dp + this.e + 1, rm == null || - !isValidInt( rm, 0, 8, 15, roundingMode ) ? ROUNDING_MODE : rm | 0 ); - } - - return n; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber shifted by k places - * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. - * - * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. - * - * If k is out of range and ERRORS is false, the result will be ±0 if k < 0, or ±Infinity - * otherwise. - * - * 'shift() argument not an integer: {k}' - * 'shift() argument out of range: {k}' - */ - P.shift = function (k) { - var n = this; - return isValidInt( k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 16, 'argument' ) - - // k < 1e+21, or truncate(k) will produce exponential notation. - ? n.times( '1e' + truncate(k) ) - : new BigNumber( n.c && n.c[0] && ( k < -MAX_SAFE_INTEGER || k > MAX_SAFE_INTEGER ) - ? n.s * ( k < 0 ? 0 : 1 / 0 ) - : n ); - }; - - - /* - * sqrt(-n) = N - * sqrt( N) = N - * sqrt(-I) = N - * sqrt( I) = I - * sqrt( 0) = 0 - * sqrt(-0) = -0 - * - * Return a new BigNumber whose value is the square root of the value of this BigNumber, - * rounded according to DECIMAL_PLACES and ROUNDING_MODE. - */ - P.squareRoot = P.sqrt = function () { - var m, n, r, rep, t, - x = this, - c = x.c, - s = x.s, - e = x.e, - dp = DECIMAL_PLACES + 4, - half = new BigNumber('0.5'); - - // Negative/NaN/Infinity/zero? - if ( s !== 1 || !c || !c[0] ) { - return new BigNumber( !s || s < 0 && ( !c || c[0] ) ? NaN : c ? x : 1 / 0 ); - } - - // Initial estimate. - s = Math.sqrt( +x ); - - // Math.sqrt underflow/overflow? - // Pass x to Math.sqrt as integer, then adjust the exponent of the result. - if ( s == 0 || s == 1 / 0 ) { - n = coeffToString(c); - if ( ( n.length + e ) % 2 == 0 ) n += '0'; - s = Math.sqrt(n); - e = bitFloor( ( e + 1 ) / 2 ) - ( e < 0 || e % 2 ); - - if ( s == 1 / 0 ) { - n = '1e' + e; - } else { - n = s.toExponential(); - n = n.slice( 0, n.indexOf('e') + 1 ) + e; - } - - r = new BigNumber(n); - } else { - r = new BigNumber( s + '' ); - } - - // Check for zero. - // r could be zero if MIN_EXP is changed after the this value was created. - // This would cause a division by zero (x/t) and hence Infinity below, which would cause - // coeffToString to throw. - if ( r.c[0] ) { - e = r.e; - s = e + dp; - if ( s < 3 ) s = 0; - - // Newton-Raphson iteration. - for ( ; ; ) { - t = r; - r = half.times( t.plus( div( x, t, dp, 1 ) ) ); - - if ( coeffToString( t.c ).slice( 0, s ) === ( n = - coeffToString( r.c ) ).slice( 0, s ) ) { - - // The exponent of r may here be one less than the final result exponent, - // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits - // are indexed correctly. - if ( r.e < e ) --s; - n = n.slice( s - 3, s + 1 ); - - // The 4th rounding digit may be in error by -1 so if the 4 rounding digits - // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the - // iteration. - if ( n == '9999' || !rep && n == '4999' ) { - - // On the first iteration only, check to see if rounding up gives the - // exact result as the nines may infinitely repeat. - if ( !rep ) { - round( t, t.e + DECIMAL_PLACES + 2, 0 ); - - if ( t.times(t).eq(x) ) { - r = t; - break; - } - } - - dp += 4; - s += 4; - rep = 1; - } else { - - // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact - // result. If not, then there are further digits and m will be truthy. - if ( !+n || !+n.slice(1) && n.charAt(0) == '5' ) { - - // Truncate to the first rounding digit. - round( r, r.e + DECIMAL_PLACES + 2, 1 ); - m = !r.times(r).eq(x); - } - - break; - } - } - } - } - - return round( r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m ); - }; - - - /* - * n * 0 = 0 - * n * N = N - * n * I = I - * 0 * n = 0 - * 0 * 0 = 0 - * 0 * N = N - * 0 * I = N - * N * n = N - * N * 0 = N - * N * N = N - * N * I = N - * I * n = I - * I * 0 = N - * I * N = N - * I * I = I - * - * Return a new BigNumber whose value is the value of this BigNumber times the value of - * BigNumber(y, b). - */ - P.times = P.mul = function ( y, b ) { - var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, - base, sqrtBase, - x = this, - xc = x.c, - yc = ( id = 17, y = new BigNumber( y, b ) ).c; - - // Either NaN, ±Infinity or ±0? - if ( !xc || !yc || !xc[0] || !yc[0] ) { - - // Return NaN if either is NaN, or one is 0 and the other is Infinity. - if ( !x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc ) { - y.c = y.e = y.s = null; - } else { - y.s *= x.s; - - // Return ±Infinity if either is ±Infinity. - if ( !xc || !yc ) { - y.c = y.e = null; - - // Return ±0 if either is ±0. - } else { - y.c = [0]; - y.e = 0; - } - } - - return y; - } - - e = bitFloor( x.e / LOG_BASE ) + bitFloor( y.e / LOG_BASE ); - y.s *= x.s; - xcL = xc.length; - ycL = yc.length; - - // Ensure xc points to longer array and xcL to its length. - if ( xcL < ycL ) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i; - - // Initialise the result array with zeros. - for ( i = xcL + ycL, zc = []; i--; zc.push(0) ); - - base = BASE; - sqrtBase = SQRT_BASE; - - for ( i = ycL; --i >= 0; ) { - c = 0; - ylo = yc[i] % sqrtBase; - yhi = yc[i] / sqrtBase | 0; - - for ( k = xcL, j = i + k; j > i; ) { - xlo = xc[--k] % sqrtBase; - xhi = xc[k] / sqrtBase | 0; - m = yhi * xlo + xhi * ylo; - xlo = ylo * xlo + ( ( m % sqrtBase ) * sqrtBase ) + zc[j] + c; - c = ( xlo / base | 0 ) + ( m / sqrtBase | 0 ) + yhi * xhi; - zc[j--] = xlo % base; - } - - zc[j] = c; - } - - if (c) { - ++e; - } else { - zc.shift(); - } - - return normalise( y, zc, e ); - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of - * sd significant digits using rounding mode rm, or ROUNDING_MODE if rm is omitted. - * - * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toDigits() precision out of range: {sd}' - * 'toDigits() precision not an integer: {sd}' - * 'toDigits() rounding mode not an integer: {rm}' - * 'toDigits() rounding mode out of range: {rm}' - */ - P.toDigits = function ( sd, rm ) { - var n = new BigNumber(this); - sd = sd == null || !isValidInt( sd, 1, MAX, 18, 'precision' ) ? null : sd | 0; - rm = rm == null || !isValidInt( rm, 0, 8, 18, roundingMode ) ? ROUNDING_MODE : rm | 0; - return sd ? round( n, sd, rm ) : n; - }; - - - /* - * Return a string representing the value of this BigNumber in exponential notation and - * rounded using ROUNDING_MODE to dp fixed decimal places. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toExponential() decimal places not an integer: {dp}' - * 'toExponential() decimal places out of range: {dp}' - * 'toExponential() rounding mode not an integer: {rm}' - * 'toExponential() rounding mode out of range: {rm}' - */ - P.toExponential = function ( dp, rm ) { - return format( this, - dp != null && isValidInt( dp, 0, MAX, 19 ) ? ~~dp + 1 : null, rm, 19 ); - }; - - - /* - * Return a string representing the value of this BigNumber in fixed-point notation rounding - * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. - * - * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', - * but e.g. (-0.00001).toFixed(0) is '-0'. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toFixed() decimal places not an integer: {dp}' - * 'toFixed() decimal places out of range: {dp}' - * 'toFixed() rounding mode not an integer: {rm}' - * 'toFixed() rounding mode out of range: {rm}' - */ - P.toFixed = function ( dp, rm ) { - return format( this, dp != null && isValidInt( dp, 0, MAX, 20 ) - ? ~~dp + this.e + 1 : null, rm, 20 ); - }; - - - /* - * Return a string representing the value of this BigNumber in fixed-point notation rounded - * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties - * of the FORMAT object (see BigNumber.config). - * - * FORMAT = { - * decimalSeparator : '.', - * groupSeparator : ',', - * groupSize : 3, - * secondaryGroupSize : 0, - * fractionGroupSeparator : '\xA0', // non-breaking space - * fractionGroupSize : 0 - * }; - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toFormat() decimal places not an integer: {dp}' - * 'toFormat() decimal places out of range: {dp}' - * 'toFormat() rounding mode not an integer: {rm}' - * 'toFormat() rounding mode out of range: {rm}' - */ - P.toFormat = function ( dp, rm ) { - var str = format( this, dp != null && isValidInt( dp, 0, MAX, 21 ) - ? ~~dp + this.e + 1 : null, rm, 21 ); - - if ( this.c ) { - var i, - arr = str.split('.'), - g1 = +FORMAT.groupSize, - g2 = +FORMAT.secondaryGroupSize, - groupSeparator = FORMAT.groupSeparator, - intPart = arr[0], - fractionPart = arr[1], - isNeg = this.s < 0, - intDigits = isNeg ? intPart.slice(1) : intPart, - len = intDigits.length; - - if (g2) i = g1, g1 = g2, g2 = i, len -= i; - - if ( g1 > 0 && len > 0 ) { - i = len % g1 || g1; - intPart = intDigits.substr( 0, i ); - - for ( ; i < len; i += g1 ) { - intPart += groupSeparator + intDigits.substr( i, g1 ); - } - - if ( g2 > 0 ) intPart += groupSeparator + intDigits.slice(i); - if (isNeg) intPart = '-' + intPart; - } - - str = fractionPart - ? intPart + FORMAT.decimalSeparator + ( ( g2 = +FORMAT.fractionGroupSize ) - ? fractionPart.replace( new RegExp( '\\d{' + g2 + '}\\B', 'g' ), - '$&' + FORMAT.fractionGroupSeparator ) - : fractionPart ) - : intPart; - } - - return str; - }; - - - /* - * Return a string array representing the value of this BigNumber as a simple fraction with - * an integer numerator and an integer denominator. The denominator will be a positive - * non-zero value less than or equal to the specified maximum denominator. If a maximum - * denominator is not specified, the denominator will be the lowest value necessary to - * represent the number exactly. - * - * [md] {number|string|BigNumber} Integer >= 1 and < Infinity. The maximum denominator. - * - * 'toFraction() max denominator not an integer: {md}' - * 'toFraction() max denominator out of range: {md}' - */ - P.toFraction = function (md) { - var arr, d0, d2, e, exp, n, n0, q, s, - k = ERRORS, - x = this, - xc = x.c, - d = new BigNumber(ONE), - n1 = d0 = new BigNumber(ONE), - d1 = n0 = new BigNumber(ONE); - - if ( md != null ) { - ERRORS = false; - n = new BigNumber(md); - ERRORS = k; - - if ( !( k = n.isInt() ) || n.lt(ONE) ) { - - if (ERRORS) { - raise( 22, - 'max denominator ' + ( k ? 'out of range' : 'not an integer' ), md ); - } - - // ERRORS is false: - // If md is a finite non-integer >= 1, round it to an integer and use it. - md = !k && n.c && round( n, n.e + 1, 1 ).gte(ONE) ? n : null; - } - } - - if ( !xc ) return x.toString(); - s = coeffToString(xc); - - // Determine initial denominator. - // d is a power of 10 and the minimum max denominator that specifies the value exactly. - e = d.e = s.length - x.e - 1; - d.c[0] = POWS_TEN[ ( exp = e % LOG_BASE ) < 0 ? LOG_BASE + exp : exp ]; - md = !md || n.cmp(d) > 0 ? ( e > 0 ? d : n1 ) : n; - - exp = MAX_EXP; - MAX_EXP = 1 / 0; - n = new BigNumber(s); - - // n0 = d1 = 0 - n0.c[0] = 0; - - for ( ; ; ) { - q = div( n, d, 0, 1 ); - d2 = d0.plus( q.times(d1) ); - if ( d2.cmp(md) == 1 ) break; - d0 = d1; - d1 = d2; - n1 = n0.plus( q.times( d2 = n1 ) ); - n0 = d2; - d = n.minus( q.times( d2 = d ) ); - n = d2; - } - - d2 = div( md.minus(d0), d1, 0, 1 ); - n0 = n0.plus( d2.times(n1) ); - d0 = d0.plus( d2.times(d1) ); - n0.s = n1.s = x.s; - e *= 2; - - // Determine which fraction is closer to x, n0/d0 or n1/d1 - arr = div( n1, d1, e, ROUNDING_MODE ).minus(x).abs().cmp( - div( n0, d0, e, ROUNDING_MODE ).minus(x).abs() ) < 1 - ? [ n1.toString(), d1.toString() ] - : [ n0.toString(), d0.toString() ]; - - MAX_EXP = exp; - return arr; - }; - - - /* - * Return the value of this BigNumber converted to a number primitive. - */ - P.toNumber = function () { - var x = this; - - // Ensure zero has correct sign. - return +x || ( x.s ? x.s * 0 : NaN ); - }; - - - /* - * Return a BigNumber whose value is the value of this BigNumber raised to the power n. - * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. - * If POW_PRECISION is not 0, round to POW_PRECISION using ROUNDING_MODE. - * - * n {number} Integer, -9007199254740992 to 9007199254740992 inclusive. - * (Performs 54 loop iterations for n of 9007199254740992.) - * - * 'pow() exponent not an integer: {n}' - * 'pow() exponent out of range: {n}' - */ - P.toPower = P.pow = function (n) { - var k, y, - i = mathfloor( n < 0 ? -n : +n ), - x = this; - - // Pass ±Infinity to Math.pow if exponent is out of range. - if ( !isValidInt( n, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 23, 'exponent' ) && - ( !isFinite(n) || i > MAX_SAFE_INTEGER && ( n /= 0 ) || - parseFloat(n) != n && !( n = NaN ) ) ) { - return new BigNumber( Math.pow( +x, n ) ); - } - - // Truncating each coefficient array to a length of k after each multiplication equates - // to truncating significant digits to POW_PRECISION + [28, 41], i.e. there will be a - // minimum of 28 guard digits retained. (Using + 1.5 would give [9, 21] guard digits.) - k = POW_PRECISION ? mathceil( POW_PRECISION / LOG_BASE + 2 ) : 0; - y = new BigNumber(ONE); - - for ( ; ; ) { - - if ( i % 2 ) { - y = y.times(x); - if ( !y.c ) break; - if ( k && y.c.length > k ) y.c.length = k; - } - - i = mathfloor( i / 2 ); - if ( !i ) break; - - x = x.times(x); - if ( k && x.c && x.c.length > k ) x.c.length = k; - } - - if ( n < 0 ) y = ONE.div(y); - return k ? round( y, POW_PRECISION, ROUNDING_MODE ) : y; - }; - - - /* - * Return a string representing the value of this BigNumber rounded to sd significant digits - * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits - * necessary to represent the integer part of the value in fixed-point notation, then use - * exponential notation. - * - * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toPrecision() precision not an integer: {sd}' - * 'toPrecision() precision out of range: {sd}' - * 'toPrecision() rounding mode not an integer: {rm}' - * 'toPrecision() rounding mode out of range: {rm}' - */ - P.toPrecision = function ( sd, rm ) { - return format( this, sd != null && isValidInt( sd, 1, MAX, 24, 'precision' ) - ? sd | 0 : null, rm, 24 ); - }; - - - /* - * Return a string representing the value of this BigNumber in base b, or base 10 if b is - * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and - * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent - * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than - * TO_EXP_NEG, return exponential notation. - * - * [b] {number} Integer, 2 to 64 inclusive. - * - * 'toString() base not an integer: {b}' - * 'toString() base out of range: {b}' - */ - P.toString = function (b) { - var str, - n = this, - s = n.s, - e = n.e; - - // Infinity or NaN? - if ( e === null ) { - - if (s) { - str = 'Infinity'; - if ( s < 0 ) str = '-' + str; - } else { - str = 'NaN'; - } - } else { - str = coeffToString( n.c ); - - if ( b == null || !isValidInt( b, 2, 64, 25, 'base' ) ) { - str = e <= TO_EXP_NEG || e >= TO_EXP_POS - ? toExponential( str, e ) - : toFixedPoint( str, e ); - } else { - str = convertBase( toFixedPoint( str, e ), b | 0, 10, s ); - } - - if ( s < 0 && n.c[0] ) str = '-' + str; - } - - return str; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber truncated to a whole - * number. - */ - P.truncated = P.trunc = function () { - return round( new BigNumber(this), this.e + 1, 1 ); - }; - - - - /* - * Return as toString, but do not accept a base argument. - */ - P.valueOf = P.toJSON = function () { - return this.toString(); - }; - - - // Aliases for BigDecimal methods. - //P.add = P.plus; // P.add included above - //P.subtract = P.minus; // P.sub included above - //P.multiply = P.times; // P.mul included above - //P.divide = P.div; - //P.remainder = P.mod; - //P.compareTo = P.cmp; - //P.negate = P.neg; - - - if ( configObj != null ) BigNumber.config(configObj); - - return BigNumber; - } - - - // PRIVATE HELPER FUNCTIONS - - - function bitFloor(n) { - var i = n | 0; - return n > 0 || n === i ? i : i - 1; - } - - - // Return a coefficient array as a string of base 10 digits. - function coeffToString(a) { - var s, z, - i = 1, - j = a.length, - r = a[0] + ''; - - for ( ; i < j; ) { - s = a[i++] + ''; - z = LOG_BASE - s.length; - for ( ; z--; s = '0' + s ); - r += s; - } - - // Determine trailing zeros. - for ( j = r.length; r.charCodeAt(--j) === 48; ); - return r.slice( 0, j + 1 || 1 ); - } - - - // Compare the value of BigNumbers x and y. - function compare( x, y ) { - var a, b, - xc = x.c, - yc = y.c, - i = x.s, - j = y.s, - k = x.e, - l = y.e; - - // Either NaN? - if ( !i || !j ) return null; - - a = xc && !xc[0]; - b = yc && !yc[0]; - - // Either zero? - if ( a || b ) return a ? b ? 0 : -j : i; - - // Signs differ? - if ( i != j ) return i; - - a = i < 0; - b = k == l; - - // Either Infinity? - if ( !xc || !yc ) return b ? 0 : !xc ^ a ? 1 : -1; - - // Compare exponents. - if ( !b ) return k > l ^ a ? 1 : -1; - - j = ( k = xc.length ) < ( l = yc.length ) ? k : l; - - // Compare digit by digit. - for ( i = 0; i < j; i++ ) if ( xc[i] != yc[i] ) return xc[i] > yc[i] ^ a ? 1 : -1; - - // Compare lengths. - return k == l ? 0 : k > l ^ a ? 1 : -1; - } - - - /* - * Return true if n is a valid number in range, otherwise false. - * Use for argument validation when ERRORS is false. - * Note: parseInt('1e+1') == 1 but parseFloat('1e+1') == 10. - */ - function intValidatorNoErrors( n, min, max ) { - return ( n = truncate(n) ) >= min && n <= max; - } - - - function isArray(obj) { - return Object.prototype.toString.call(obj) == '[object Array]'; - } - - - /* - * Convert string of baseIn to an array of numbers of baseOut. - * Eg. convertBase('255', 10, 16) returns [15, 15]. - * Eg. convertBase('ff', 16, 10) returns [2, 5, 5]. - */ - function toBaseOut( str, baseIn, baseOut ) { - var j, - arr = [0], - arrL, - i = 0, - len = str.length; - - for ( ; i < len; ) { - for ( arrL = arr.length; arrL--; arr[arrL] *= baseIn ); - arr[ j = 0 ] += ALPHABET.indexOf( str.charAt( i++ ) ); - - for ( ; j < arr.length; j++ ) { - - if ( arr[j] > baseOut - 1 ) { - if ( arr[j + 1] == null ) arr[j + 1] = 0; - arr[j + 1] += arr[j] / baseOut | 0; - arr[j] %= baseOut; - } - } - } - - return arr.reverse(); - } - - - function toExponential( str, e ) { - return ( str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str ) + - ( e < 0 ? 'e' : 'e+' ) + e; - } - - - function toFixedPoint( str, e ) { - var len, z; - - // Negative exponent? - if ( e < 0 ) { - - // Prepend zeros. - for ( z = '0.'; ++e; z += '0' ); - str = z + str; - - // Positive exponent - } else { - len = str.length; - - // Append zeros. - if ( ++e > len ) { - for ( z = '0', e -= len; --e; z += '0' ); - str += z; - } else if ( e < len ) { - str = str.slice( 0, e ) + '.' + str.slice(e); - } - } - - return str; - } - - - function truncate(n) { - n = parseFloat(n); - return n < 0 ? mathceil(n) : mathfloor(n); - } - - - // EXPORT - - - BigNumber = another(); - - // AMD. - if ( typeof define == 'function' && define.amd ) { - define( function () { return BigNumber; } ); - - // Node and other environments that support module.exports. - } else if ( typeof module != 'undefined' && module.exports ) { - module.exports = BigNumber; - if ( !crypto ) try { crypto = require('crypto'); } catch (e) {} - - // Browser. - } else { - global.BigNumber = BigNumber; - } -})(this); - -},{"crypto":1}],"natspec":[function(require,module,exports){ -/* - This file is part of natspec.js. - - natspec.js 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. - - natspec.js 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 natspec.js. If not, see . -*/ -/** @file natspec.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var abi = require('./node_modules/web3/lib/solidity/abi.js'); - -/** - * This object should be used to evaluate natspec expression - * It has one method evaluateExpression which shoul be used - */ -var natspec = (function () { - /** - * Helper method - * Should be called to copy values from object to global context - * - * @method copyToContext - * @param {Object} object from which we want to copy properties - * @param {Object} object to which we copy - */ - var copyToContext = function (obj, context) { - Object.keys(obj).forEach(function (key) { - context[key] = obj[key]; - }); - } - - /** - * Should be used to generate codes, which will be evaluated - * - * @method generateCode - * @param {Object} object from which code will be generated - * @return {String} javascript code which is used to initalized variables - */ - var generateCode = function (obj) { - return Object.keys(obj).reduce(function (acc, key) { - return acc + "var " + key + " = context['" + key + "'];\n"; - }, ""); - }; - - /** - * Helper method - * Should be called to get method with given name from the abi - * - * @method getMethodWithName - * @param {Array} contract's abi - * @param {String} name of the method that we are looking for - * @return {Object} abi for method with name - */ - var getMethodWithName = function(abi, name) { - return abi.filter(function (method) { - return method.name === name; - })[0]; - }; - - /** - * Should be used to get all contract method input variables - * - * @method getMethodInputParams - * @param {Object} abi for certain method - * @param {Object} transaction object - * @return {Object} object with all contract's method input variables - */ - var getMethodInputParams = function (method, transaction) { - // do it with output formatter (cause we have to decode) - var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10)); - - return method.inputs.reduce(function (acc, current, index) { - acc[current.name] = params[index]; - return acc; - }, {}); - }; - - /** - * Should be called when we want to evaluate natspec expression - * Replaces all natspec 'subexpressions' with evaluated value - * - * @method mapExpressionToEvaluate - * @param {String} expression to evaluate - * @param {Function} callback which is called to evaluate te expression - * @return {String} evaluated expression - */ - var mapExpressionsToEvaluate = function (expression, cb) { - var evaluatedExpression = ""; - - // match everything in quotes - var pattern = /\` + "`" + `(?:\\.|[^` + "`" + `\\])*\` + "`" + `/gim - var match; - var lastIndex = 0; - try { - while ((match = pattern.exec(expression)) !== null) { - var startIndex = pattern.lastIndex - match[0].length; - var toEval = match[0].slice(1, match[0].length - 1); - evaluatedExpression += expression.slice(lastIndex, startIndex); - var evaluatedPart = cb(toEval); - evaluatedExpression += evaluatedPart; - lastIndex = pattern.lastIndex; - } - - evaluatedExpression += expression.slice(lastIndex); - } - catch (err) { - throw new Error("Natspec evaluation failed, wrong input params"); - } - - return evaluatedExpression; - }; - - /** - * Should be called to evaluate single expression - * Is internally using javascript's 'eval' method - * - * @method evaluateExpression - * @param {String} expression which should be evaluated - * @param {Object} [call] object containing contract abi, transaction, called method - * @return {String} evaluated expression - * @throws exception if method is not found or we are trying to evaluate input params that does not exists - */ - - var utils = require('../utils/utils'); - - var evaluateExpression = function (expression, call) { - //var self = this; - var context = {}; - - if (!!call) { - try { - var method = getMethodWithName(call.abi, call.method); - var params = getMethodInputParams(method, call.transaction); - copyToContext(params, context); - } - catch (err) { - throw new Error("Natspec evaluation failed, method does not exist"); - } - } - - var code = generateCode(context); - - var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) { - //var fn = new Function("context", "toHex", code + "return " + toEval + ";"); - //return fn(context, toHex).toString(); - var fn = new Function("context", "utils", code + "return " + toEval + ";"); - return fn(context, utils).toString(); - }); - - return evaluatedExpression; - }; - - /** - * Safe version of evaluateExpression - * Instead of throwing an exception it returns it as a string - * - * @method evaluateExpressionSafe - * @param {String} expression which should be evaluated - * @param {Object} [call] object containing contract abi, transaction, called method - * @return {String} evaluated expression - */ - var evaluateExpressionSafe = function (expression, call) { - try { - return evaluateExpression(expression, call); - } - catch (err) { - return err.message; - } - }; - - return { - evaluateExpression: evaluateExpression, - evaluateExpressionSafe: evaluateExpressionSafe - }; - -})(); - -module.exports = natspec; - - -},{"./node_modules/web3/lib/solidity/abi.js":2,"../utils/utils":7}]},{},[]); -` - -//# sourceMappingURL=natspec.js.map` diff --git a/vendor/github.com/ethereum/go-ethereum/common/path.go b/vendor/github.com/ethereum/go-ethereum/common/path.go index cbcd13c4f..bd8da86e7 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/path.go +++ b/vendor/github.com/ethereum/go-ethereum/common/path.go @@ -19,10 +19,8 @@ package common import ( "fmt" "os" - "os/user" "path/filepath" "runtime" - "strings" ) // MakeName creates a node name that follows the ethereum convention @@ -32,21 +30,6 @@ func MakeName(name, version string) string { return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) } -func ExpandHomePath(p string) (path string) { - path = p - sep := string(os.PathSeparator) - - // Check in case of paths like "/something/~/something/" - if len(p) > 1 && p[:1+len(sep)] == "~"+sep { - usr, _ := user.Current() - dir := usr.HomeDir - - path = strings.Replace(p, "~", dir, 1) - } - - return -} - func FileExist(filePath string) bool { _, err := os.Stat(filePath) if err != nil && os.IsNotExist(err) { @@ -62,13 +45,3 @@ func AbsolutePath(Datadir string, filename string) string { } return filepath.Join(Datadir, filename) } - -func HomeDir() string { - if home := os.Getenv("HOME"); home != "" { - return home - } - if usr, err := user.Current(); err == nil { - return usr.HomeDir - } - return "" -} diff --git a/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go b/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go index 6dd0ef46f..a32653554 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go +++ b/vendor/github.com/ethereum/go-ethereum/common/registrar/ethreg/api.go @@ -32,11 +32,12 @@ import ( "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 *core.ChainConfig + config *params.ChainConfig bc *core.BlockChain chainDb ethdb.Database txPool *core.TxPool @@ -45,12 +46,12 @@ type registryAPIBackend struct { // PrivateRegistarAPI offers various functions to access the Ethereum registry. type PrivateRegistarAPI struct { - config *core.ChainConfig + config *params.ChainConfig be *registryAPIBackend } // NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. -func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { +func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { return &PrivateRegistarAPI{ config: config, be: ®istryAPIBackend{ @@ -173,25 +174,20 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr from.SetBalance(common.MaxBig) - msg := callmsg{ - from: from, - gas: common.Big(gasStr), - gasPrice: common.Big(gasPriceStr), - value: common.Big(valueStr), - data: common.FromHex(dataStr), - } + var to *common.Address if len(toStr) > 0 { addr := common.HexToAddress(toStr) - msg.to = &addr + to = &addr } - - if msg.gas.Cmp(big.NewInt(0)) == 0 { - msg.gas = big.NewInt(50000000) + gas := common.Big(gasStr) + if gas.BitLen() == 0 { + gas = big.NewInt(50000000) } - - if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + 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{}) @@ -257,11 +253,12 @@ func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasSt tx = types.NewTransaction(nonce, to, value, gas, price, data) } - signature, err := be.am.Sign(from, tx.SigHash().Bytes()) + sigHash := (types.HomesteadSigner{}).Hash(tx) + signature, err := be.am.SignEthereum(from, sigHash.Bytes()) if err != nil { return "", err } - signedTx, err := tx.WithSignature(signature) + signedTx, err := tx.WithSignature(types.HomesteadSigner{}, signature) if err != nil { return "", err } diff --git a/vendor/github.com/ethereum/go-ethereum/common/types.go b/vendor/github.com/ethereum/go-ethereum/common/types.go index d00884484..70b7e7aae 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/types.go +++ b/vendor/github.com/ethereum/go-ethereum/common/types.go @@ -35,7 +35,10 @@ const ( var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes") type ( - Hash [HashLength]byte + // Hash represents the 32 byte Keccak256 hash of arbitrary data. + Hash [HashLength]byte + + // Address represents the 20 byte address of an Ethereum account. Address [AddressLength]byte ) diff --git a/vendor/github.com/ethereum/go-ethereum/console/bridge.go b/vendor/github.com/ethereum/go-ethereum/console/bridge.go index 3009eabc4..7f7e6feb1 100644 --- a/vendor/github.com/ethereum/go-ethereum/console/bridge.go +++ b/vendor/github.com/ethereum/go-ethereum/console/bridge.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -26,20 +26,18 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rpc" "github.com/robertkrimen/otto" - "golang.org/x/net/context" ) // bridge is a collection of JavaScript utility methods to bride the .js runtime // environment and the Go RPC connection backing the remote method calls. type bridge struct { - client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through + client *rpc.Client // RPC client to execute Ethereum requests through prompter UserPrompter // Input prompter to allow interactive user feedback printer io.Writer // Output writer to serialize any display strings to - ctx context.Context } // newBridge creates a new JavaScript wrapper around an RPC client. -func newBridge(client *rpc.ClientRestartWrapper, prompter UserPrompter, printer io.Writer) *bridge { +func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *bridge { return &bridge{ client: client, prompter: prompter, @@ -47,10 +45,6 @@ func newBridge(client *rpc.ClientRestartWrapper, prompter UserPrompter, printer } } -func (b *bridge) setContext(ctx context.Context) { - b.ctx = ctx -} - // NewAccount is a wrapper around the personal.newAccount RPC method that uses a // non-echoing password prompt to aquire the passphrase and executes the original // RPC method (saved in jeth.newAccount) with it to actually execute the RPC call. @@ -132,6 +126,44 @@ func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) { return val } +// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password +// prompt to aquire the passphrase and executes the original RPC method (saved in +// jeth.sign) with it to actually execute the RPC call. +func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) { + var ( + message = call.Argument(0) + account = call.Argument(1) + passwd = call.Argument(2) + ) + + if !message.IsString() { + throwJSException("first argument must be the message to sign") + } + if !account.IsString() { + throwJSException("second argument must be the account to sign with") + } + + // if the password is not given or null ask the user and ensure password is a string + if passwd.IsUndefined() || passwd.IsNull() { + fmt.Fprintf(b.printer, "Give password for account %s\n", account) + if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + } + if !passwd.IsString() { + throwJSException("third argument must be the password to unlock the account") + } + + // Send the request to the backend and return + val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd) + if err != nil { + throwJSException(err.Error()) + } + return val +} + // Sleep will block the console for the specified number of seconds. func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) { if call.Argument(0).IsNumber() { @@ -228,26 +260,7 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) { resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`) resp.Set("id", req.Id) var result json.RawMessage - - client := b.client.Client() - errc := make(chan error, 1) - errc2 := make(chan error) - go func(){ - if b.ctx != nil { - select { - case <-b.ctx.Done(): - b.client.Restart() - errc2 <- b.ctx.Err() - case err := <-errc: - errc2 <- err - } - } else { - errc2 <- <-errc - } - }() - errc <- client.Call(&result, req.Method, req.Params...) - err = <-errc2 - + err = b.client.Call(&result, req.Method, req.Params...) switch err := err.(type) { case nil: if result == nil { diff --git a/vendor/github.com/ethereum/go-ethereum/console/console.go b/vendor/github.com/ethereum/go-ethereum/console/console.go index 449deeaf3..6e3d7e43c 100644 --- a/vendor/github.com/ethereum/go-ethereum/console/console.go +++ b/vendor/github.com/ethereum/go-ethereum/console/console.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -26,7 +26,6 @@ import ( "regexp" "sort" "strings" - "time" "github.com/ethereum/go-ethereum/internal/jsre" "github.com/ethereum/go-ethereum/internal/web3ext" @@ -34,7 +33,6 @@ import ( "github.com/mattn/go-colorable" "github.com/peterh/liner" "github.com/robertkrimen/otto" - "golang.org/x/net/context" ) var ( @@ -52,27 +50,26 @@ const DefaultPrompt = "> " // Config is te collection of configurations to fine tune the behavior of the // JavaScript console. type Config struct { - DataDir string // Data directory to store the console history at - DocRoot string // Filesystem path from where to load JavaScript files from - Client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through - Prompt string // Input prompt prefix string (defaults to DefaultPrompt) - Prompter UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter) - Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout) - Preload []string // Absolute paths to JavaScript files to preload + DataDir string // Data directory to store the console history at + DocRoot string // Filesystem path from where to load JavaScript files from + Client *rpc.Client // RPC client to execute Ethereum requests through + Prompt string // Input prompt prefix string (defaults to DefaultPrompt) + Prompter UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter) + Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout) + Preload []string // Absolute paths to JavaScript files to preload } // Console is a JavaScript interpreted runtime environment. It is a fully fleged // JavaScript console attached to a running node via an external or in-process RPC // client. type Console struct { - client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through - jsre *jsre.JSRE // JavaScript runtime environment running the interpreter - prompt string // Input prompt prefix string - prompter UserPrompter // Input prompter to allow interactive user feedback - histPath string // Absolute path to the console scrollback history - history []string // Scroll history maintained by the console - printer io.Writer // Output writer to serialize any display strings to - setContext func(context.Context) + client *rpc.Client // RPC client to execute Ethereum requests through + jsre *jsre.JSRE // JavaScript runtime environment running the interpreter + prompt string // Input prompt prefix string + prompter UserPrompter // Input prompter to allow interactive user feedback + histPath string // Absolute path to the console scrollback history + history []string // Scroll history maintained by the console + printer io.Writer // Output writer to serialize any display strings to } func New(config Config) (*Console, error) { @@ -106,7 +103,6 @@ func New(config Config) (*Console, error) { func (c *Console) init(preload []string) error { // Initialize the JavaScript <-> Go RPC bridge bridge := newBridge(c.client, c.prompter, c.printer) - c.setContext = bridge.setContext c.jsre.Set("jeth", struct{}{}) jethObj, _ := c.jsre.Get("jeth") @@ -131,7 +127,7 @@ func (c *Console) init(preload []string) error { return fmt.Errorf("web3 provider: %v", err) } // Load the supported APIs into the JavaScript runtime environment - apis, err := c.client.Client().SupportedModules() + apis, err := c.client.SupportedModules() if err != nil { return fmt.Errorf("api modules: %v", err) } @@ -160,10 +156,9 @@ func (c *Console) init(preload []string) error { if err != nil { return err } - // Override the unlockAccount and newAccount methods since these require user interaction. - // Assign the jeth.unlockAccount and jeth.newAccount in the Console the original web3 callbacks. - // These will be called by the jeth.* methods after they got the password from the user and send - // the original web3 request to the backend. + // Override the unlockAccount, newAccount and sign methods since these require user interaction. + // Assign these method in the Console the original web3 callbacks. These will be called by the jeth.* + // methods after they got the password from the user and send the original web3 request to the backend. if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil { return fmt.Errorf("personal.unlockAccount: %v", err) @@ -171,8 +166,12 @@ func (c *Console) init(preload []string) error { if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil { return fmt.Errorf("personal.newAccount: %v", err) } + if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil { + return fmt.Errorf("personal.sign: %v", err) + } obj.Set("unlockAccount", bridge.UnlockAccount) obj.Set("newAccount", bridge.NewAccount) + obj.Set("sign", bridge.Sign) } } // The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer. @@ -257,7 +256,7 @@ func (c *Console) Welcome() { console.log(" datadir: " + admin.datadir); `) // List all the supported modules for the user to call - if apis, err := c.client.Client().SupportedModules(); err == nil { + if apis, err := c.client.SupportedModules(); err == nil { modules := make([]string, 0, len(apis)) for api, version := range apis { modules = append(modules, fmt.Sprintf("%s:%s", api, version)) @@ -351,12 +350,7 @@ func (c *Console) Interactive() { } } } - done := make(chan struct{}) - ctx, _ := context.WithTimeout(context.Background(), time.Second*5) - c.setContext(ctx) c.Evaluate(input) - c.setContext(nil) - close(done) input = "" } } diff --git a/vendor/github.com/ethereum/go-ethereum/console/prompter.go b/vendor/github.com/ethereum/go-ethereum/console/prompter.go index 0e4a8a53e..5946d9ece 100644 --- a/vendor/github.com/ethereum/go-ethereum/console/prompter.go +++ b/vendor/github.com/ethereum/go-ethereum/console/prompter.go @@ -95,7 +95,7 @@ func newTerminalPrompter() *terminalPrompter { } p.SetCtrlCAborts(true) p.SetTabCompletionStyle(liner.TabPrints) - + p.SetMultiLineMode(true) return p } diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go new file mode 100644 index 000000000..b2b2365f3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go @@ -0,0 +1,68 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package chequebook + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +const Version = "1.0" + +var errNoChequebook = errors.New("no chequebook") + +type Api struct { + chequebookf func() *Chequebook +} + +func NewApi(ch func() *Chequebook) *Api { + return &Api{ch} +} + +func (self *Api) Balance() (string, error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Balance().String(), nil +} + +func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) { + ch := self.chequebookf() + if ch == nil { + return nil, errNoChequebook + } + return ch.Issue(beneficiary, amount) +} + +func (self *Api) Cash(cheque *Cheque) (txhash string, err error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Cash(cheque) +} + +func (self *Api) Deposit(amount *big.Int) (txhash string, err error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Deposit(amount) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go new file mode 100644 index 000000000..5ece1391b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go @@ -0,0 +1,642 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package chequebook package wraps the 'chequebook' Ethereum smart contract. +// +// The functions in this package allow using chequebook for +// issuing, receiving, verifying cheques in ether; (auto)cashing cheques in ether +// as well as (auto)depositing ether to the chequebook contract. +package chequebook + +//go:generate abigen --sol contract/chequebook.sol --pkg contract --out contract/chequebook.go +//go:generate go run ./gencode.go + +import ( + "bytes" + "crypto/ecdsa" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "golang.org/x/net/context" +) + +// TODO(zelig): watch peer solvency and notify of bouncing cheques +// TODO(zelig): enable paying with cheque by signing off + +// Some functionality require interacting with the blockchain: +// * setting current balance on peer's chequebook +// * sending the transaction to cash the cheque +// * depositing ether to the chequebook +// * watching incoming ether + +var ( + gasToCash = big.NewInt(2000000) // gas cost of a cash transaction using chequebook + // gasToDeploy = big.NewInt(3000000) +) + +// Backend wraps all methods required for chequebook operation. +type Backend interface { + bind.ContractBackend + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + BalanceAt(ctx context.Context, address common.Address, blockNum *big.Int) (*big.Int, error) +} + +// Cheque represents a payment promise to a single beneficiary. +type Cheque struct { + Contract common.Address // address of chequebook, needed to avoid cross-contract submission + Beneficiary common.Address + Amount *big.Int // cumulative amount of all funds sent + Sig []byte // signature Sign(Keccak256(contract, beneficiary, amount), prvKey) +} + +func (self *Cheque) String() string { + return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig) +} + +type Params struct { + ContractCode, ContractAbi string +} + +var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI} + +// Chequebook can create and sign cheques from a single contract to multiple beneficiaries. +// It is the outgoing payment handler for peer to peer micropayments. +type Chequebook struct { + path string // path to chequebook file + prvKey *ecdsa.PrivateKey // private key to sign cheque with + lock sync.Mutex // + backend Backend // blockchain API + quit chan bool // when closed causes autodeposit to stop + owner common.Address // owner address (derived from pubkey) + contract *contract.Chequebook // abigen binding + session *contract.ChequebookSession // abigen binding with Tx Opts + + // persisted fields + balance *big.Int // not synced with blockchain + contractAddr common.Address // contract address + sent map[common.Address]*big.Int //tallies for beneficiarys + + txhash string // tx hash of last deposit tx + threshold *big.Int // threshold that triggers autodeposit if not nil + buffer *big.Int // buffer to keep on top of balance for fork protection +} + +func (self *Chequebook) String() string { + return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey) +} + +// NewChequebook creates a new Chequebook. +func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) { + balance := new(big.Int) + sent := make(map[common.Address]*big.Int) + + chbook, err := contract.NewChequebook(contractAddr, backend) + if err != nil { + return nil, err + } + transactOpts := bind.NewKeyedTransactor(prvKey) + session := &contract.ChequebookSession{ + Contract: chbook, + TransactOpts: *transactOpts, + } + + self = &Chequebook{ + prvKey: prvKey, + balance: balance, + contractAddr: contractAddr, + sent: sent, + path: path, + backend: backend, + owner: transactOpts.From, + contract: chbook, + session: session, + } + + if (contractAddr != common.Address{}) { + self.setBalanceFromBlockChain() + glog.V(logger.Detail).Infof("new chequebook initialised from %s (owner: %v, balance: %s)", contractAddr.Hex(), self.owner.Hex(), self.balance.String()) + } + return +} + +func (self *Chequebook) setBalanceFromBlockChain() { + balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil) + if err != nil { + glog.V(logger.Error).Infof("can't get balance: %v", err) + } else { + self.balance.Set(balance) + } +} + +// LoadChequebook loads a chequebook from disk (file path). +func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) { + var data []byte + data, err = ioutil.ReadFile(path) + if err != nil { + return + } + + self, _ = NewChequebook(path, common.Address{}, prvKey, backend) + + err = json.Unmarshal(data, self) + if err != nil { + return nil, err + } + if checkBalance { + self.setBalanceFromBlockChain() + } + + glog.V(logger.Detail).Infof("loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contractAddr.Hex(), self.owner.Hex(), self.balance, path) + + return +} + +// chequebookFile is the JSON representation of a chequebook. +type chequebookFile struct { + Balance string + Contract string + Owner string + Sent map[string]string +} + +// UnmarshalJSON deserialises a chequebook. +func (self *Chequebook) UnmarshalJSON(data []byte) error { + var file chequebookFile + err := json.Unmarshal(data, &file) + if err != nil { + return err + } + _, ok := self.balance.SetString(file.Balance, 10) + if !ok { + return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance) + } + self.contractAddr = common.HexToAddress(file.Contract) + for addr, sent := range file.Sent { + self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10) + if !ok { + return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent) + } + } + return nil +} + +// MarshalJSON serialises a chequebook. +func (self *Chequebook) MarshalJSON() ([]byte, error) { + var file = &chequebookFile{ + Balance: self.balance.String(), + Contract: self.contractAddr.Hex(), + Owner: self.owner.Hex(), + Sent: make(map[string]string), + } + for addr, sent := range self.sent { + file.Sent[addr.Hex()] = sent.String() + } + return json.Marshal(file) +} + +// Save persists the chequebook on disk, remembering balance, contract address and +// cumulative amount of funds sent for each beneficiary. +func (self *Chequebook) Save() (err error) { + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + glog.V(logger.Detail).Infof("saving chequebook (%s) to %v", self.contractAddr.Hex(), self.path) + + return ioutil.WriteFile(self.path, data, os.ModePerm) +} + +// Stop quits the autodeposit go routine to terminate +func (self *Chequebook) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.quit != nil { + close(self.quit) + self.quit = nil + } +} + +// Issue creates a cheque signed by the chequebook owner's private key. The +// signer commits to a contract (one that they own), a beneficiary and amount. +func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) { + defer self.lock.Unlock() + self.lock.Lock() + + if amount.Cmp(common.Big0) <= 0 { + return nil, fmt.Errorf("amount must be greater than zero (%v)", amount) + } + if self.balance.Cmp(amount) < 0 { + err = fmt.Errorf("insufficent funds to issue cheque for amount: %v. balance: %v", amount, self.balance) + } else { + var sig []byte + sent, found := self.sent[beneficiary] + if !found { + sent = new(big.Int) + self.sent[beneficiary] = sent + } + sum := new(big.Int).Set(sent) + sum.Add(sum, amount) + + sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey) + if err == nil { + ch = &Cheque{ + Contract: self.contractAddr, + Beneficiary: beneficiary, + Amount: sum, + Sig: sig, + } + sent.Set(sum) + self.balance.Sub(self.balance, amount) // subtract amount from balance + } + } + + // auto deposit if threshold is set and balance is less then threshold + // note this is called even if issueing cheque fails + // so we reattempt depositing + if self.threshold != nil { + if self.balance.Cmp(self.threshold) < 0 { + send := new(big.Int).Sub(self.buffer, self.balance) + self.deposit(send) + } + } + + return +} + +// Cash is a convenience method to cash any cheque. +func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) { + return ch.Cash(self.session) +} + +// data to sign: contract address, beneficiary, cumulative amount of funds ever sent +func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte { + bigamount := sum.Bytes() + if len(bigamount) > 32 { + return nil + } + var amount32 [32]byte + copy(amount32[32-len(bigamount):32], bigamount) + input := append(contract.Bytes(), beneficiary.Bytes()...) + input = append(input, amount32[:]...) + return crypto.Keccak256(input) +} + +// Balance returns the current balance of the chequebook. +func (self *Chequebook) Balance() *big.Int { + defer self.lock.Unlock() + self.lock.Lock() + return new(big.Int).Set(self.balance) +} + +// Owner returns the owner account of the chequebook. +func (self *Chequebook) Owner() common.Address { + return self.owner +} + +// Address returns the on-chain contract address of the chequebook. +func (self *Chequebook) Address() common.Address { + return self.contractAddr +} + +// Deposit deposits money to the chequebook account. +func (self *Chequebook) Deposit(amount *big.Int) (string, error) { + defer self.lock.Unlock() + self.lock.Lock() + return self.deposit(amount) +} + +// deposit deposits amount to the chequebook account. +// The caller must hold self.lock. +func (self *Chequebook) deposit(amount *big.Int) (string, error) { + // since the amount is variable here, we do not use sessions + depositTransactor := bind.NewKeyedTransactor(self.prvKey) + depositTransactor.Value = amount + chbookRaw := &contract.ChequebookRaw{Contract: self.contract} + tx, err := chbookRaw.Transfer(depositTransactor) + if err != nil { + glog.V(logger.Warn).Infof("error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contractAddr.Hex(), self.balance, self.buffer, err) + return "", err + } + // assume that transaction is actually successful, we add the amount to balance right away + self.balance.Add(self.balance, amount) + glog.V(logger.Detail).Infof("deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contractAddr.Hex(), self.balance, self.buffer) + return tx.Hash().Hex(), nil +} + +// AutoDeposit (re)sets interval time and amount which triggers sending funds to the +// chequebook. Contract backend needs to be set if threshold is not less than buffer, then +// deposit will be triggered on every new cheque issued. +func (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { + defer self.lock.Unlock() + self.lock.Lock() + self.threshold = threshold + self.buffer = buffer + self.autoDeposit(interval) +} + +// autoDeposit starts a goroutine that periodically sends funds to the chequebook +// contract caller holds the lock the go routine terminates if Chequebook.quit is closed. +func (self *Chequebook) autoDeposit(interval time.Duration) { + if self.quit != nil { + close(self.quit) + self.quit = nil + } + // if threshold >= balance autodeposit after every cheque issued + if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 { + return + } + + ticker := time.NewTicker(interval) + self.quit = make(chan bool) + quit := self.quit + go func() { + FOR: + for { + select { + case <-quit: + break FOR + case <-ticker.C: + self.lock.Lock() + if self.balance.Cmp(self.buffer) < 0 { + amount := new(big.Int).Sub(self.buffer, self.balance) + txhash, err := self.deposit(amount) + if err == nil { + self.txhash = txhash + } + } + self.lock.Unlock() + } + } + }() + return +} + +// Outbox can issue cheques from a single contract to a single beneficiary. +type Outbox struct { + chequeBook *Chequebook + beneficiary common.Address +} + +// NewOutbox creates an outbox. +func NewOutbox(chbook *Chequebook, beneficiary common.Address) *Outbox { + return &Outbox{chbook, beneficiary} +} + +// Issue creates cheque. +func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) { + return self.chequeBook.Issue(self.beneficiary, amount) +} + +// AutoDeposit enables auto-deposits on the underlying chequebook. +func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { + self.chequeBook.AutoDeposit(interval, threshold, buffer) +} + +// Stop helps satisfy the swap.OutPayment interface. +func (self *Outbox) Stop() {} + +// String implements fmt.Stringer. +func (self *Outbox) String() string { + return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance()) +} + +// Inbox can deposit, verify and cash cheques from a single contract to a single +// beneficiary. It is the incoming payment handler for peer to peer micropayments. +type Inbox struct { + lock sync.Mutex + contract common.Address // peer's chequebook contract + beneficiary common.Address // local peer's receiving address + sender common.Address // local peer's address to send cashing tx from + signer *ecdsa.PublicKey // peer's public key + txhash string // tx hash of last cashing tx + abigen bind.ContractBackend // blockchain API + session *contract.ChequebookSession // abi contract backend with tx opts + quit chan bool // when closed causes autocash to stop + maxUncashed *big.Int // threshold that triggers autocashing + cashed *big.Int // cumulative amount cashed + cheque *Cheque // last cheque, nil if none yet received +} + +// NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated +// from blockchain when first cheque is received. +func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (self *Inbox, err error) { + if signer == nil { + return nil, fmt.Errorf("signer is null") + } + chbook, err := contract.NewChequebook(contractAddr, abigen) + if err != nil { + return nil, err + } + transactOpts := bind.NewKeyedTransactor(prvKey) + transactOpts.GasLimit = gasToCash + session := &contract.ChequebookSession{ + Contract: chbook, + TransactOpts: *transactOpts, + } + sender := transactOpts.From + + self = &Inbox{ + contract: contractAddr, + beneficiary: beneficiary, + sender: sender, + signer: signer, + session: session, + cashed: new(big.Int).Set(common.Big0), + } + glog.V(logger.Detail).Infof("initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer)) + return +} + +func (self *Inbox) String() string { + return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount) +} + +// Stop quits the autocash goroutine. +func (self *Inbox) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.quit != nil { + close(self.quit) + self.quit = nil + } +} + +// Cash attempts to cash the current cheque. +func (self *Inbox) Cash() (txhash string, err error) { + if self.cheque != nil { + txhash, err = self.cheque.Cash(self.session) + glog.V(logger.Detail).Infof("cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex()) + self.cashed = self.cheque.Amount + } + return +} + +// AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed +// cheque if maxUncashed is set to 0, then autocash on receipt. +func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) { + defer self.lock.Unlock() + self.lock.Lock() + self.maxUncashed = maxUncashed + self.autoCash(cashInterval) +} + +// autoCash starts a loop that periodically clears the last check +// if the peer is trusted. Clearing period could be 24h or a week. +// +// The caller must hold self.lock. +func (self *Inbox) autoCash(cashInterval time.Duration) { + if self.quit != nil { + close(self.quit) + self.quit = nil + } + // if maxUncashed is set to 0, then autocash on receipt + if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Cmp(common.Big0) == 0 { + return + } + + ticker := time.NewTicker(cashInterval) + self.quit = make(chan bool) + quit := self.quit + go func() { + FOR: + for { + select { + case <-quit: + break FOR + case <-ticker.C: + self.lock.Lock() + if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 { + txhash, err := self.Cash() + if err == nil { + self.txhash = txhash + } + } + self.lock.Unlock() + } + } + }() + return +} + +// Receive is called to deposit the latest cheque to the incoming Inbox. +// The given promise must be a *Cheque. +func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) { + ch := promise.(*Cheque) + + defer self.lock.Unlock() + self.lock.Lock() + + var sum *big.Int + if self.cheque == nil { + // the sum is checked against the blockchain once a check is received + tally, err := self.session.Sent(self.beneficiary) + if err != nil { + return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err) + } + sum = tally + } else { + sum = self.cheque.Amount + } + + amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum) + var uncashed *big.Int + if err == nil { + self.cheque = ch + + if self.maxUncashed != nil { + uncashed = new(big.Int).Sub(ch.Amount, self.cashed) + if self.maxUncashed.Cmp(uncashed) < 0 { + self.Cash() + } + } + glog.V(logger.Detail).Infof("received cheque of %v wei in inbox (%s, uncashed: %v)", amount, self.contract.Hex(), uncashed) + } + + return amount, err +} + +// Verify verifies cheque for signer, contract, beneficiary, amount, valid signature. +func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) { + glog.V(logger.Detail).Infof("verify cheque: %v - sum: %v", self, sum) + if sum == nil { + return nil, fmt.Errorf("invalid amount") + } + + if self.Beneficiary != beneficiary { + return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex()) + } + if self.Contract != contract { + return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex()) + } + + amount := new(big.Int).Set(self.Amount) + if sum != nil { + amount.Sub(amount, sum) + if amount.Cmp(common.Big0) <= 0 { + return nil, fmt.Errorf("incorrect amount: %v <= 0", amount) + } + } + + pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.Sig) + if err != nil { + return nil, fmt.Errorf("invalid signature: %v", err) + } + if !bytes.Equal(crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) { + return nil, fmt.Errorf("signer mismatch: %x != %x", crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) + } + return amount, nil +} + +// v/r/s representation of signature +func sig2vrs(sig []byte) (v byte, r, s [32]byte) { + v = sig[64] + 27 + copy(r[:], sig[:32]) + copy(s[:], sig[32:64]) + return +} + +// Cash cashes the cheque by sending an Ethereum transaction. +func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) { + v, r, s := sig2vrs(self.Sig) + tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s) + if err != nil { + return "", err + } + return tx.Hash().Hex(), nil +} + +// ValidateCode checks that the on-chain code at address matches the expected chequebook +// contract code. This is used to detect suicided chequebooks. +func ValidateCode(ctx context.Context, b Backend, address common.Address) (ok bool, err error) { + code, err := b.CodeAt(ctx, address, nil) + if err != nil { + return false, err + } + return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go new file mode 100644 index 000000000..47090152c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go @@ -0,0 +1,541 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package contract + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// ChequebookABI is the input ABI used to generate the binding from. +const ChequebookABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"sent","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"},{"name":"sig_v","type":"uint8"},{"name":"sig_r","type":"bytes32"},{"name":"sig_s","type":"bytes32"}],"name":"cash","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"deadbeat","type":"address"}],"name":"Overdraft","type":"event"}]` + +// ChequebookBin is the compiled bytecode used for deploying new contracts. +const ChequebookBin = `0x606060405260008054600160a060020a031916331790556101ff806100246000396000f3606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100bd57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bf575b505050505050565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a0390811691161461015a576100a3565b600160a060020a038681166000908152600160205260409020543090911631908603106101b357604060008181208790559051600160a060020a0388169190819081818181818881f1935050505015156100a357610002565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff` + +// DeployChequebook deploys a new Ethereum contract, binding an instance of Chequebook to it. +func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Chequebook, error) { + parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ChequebookBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil +} + +// Chequebook is an auto generated Go binding around an Ethereum contract. +type Chequebook struct { + ChequebookCaller // Read-only binding to the contract + ChequebookTransactor // Write-only binding to the contract +} + +// ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract. +type ChequebookCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ChequebookTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ChequebookTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ChequebookSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ChequebookSession struct { + Contract *Chequebook // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ChequebookCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ChequebookCallerSession struct { + Contract *ChequebookCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ChequebookTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ChequebookTransactorSession struct { + Contract *ChequebookTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ChequebookRaw is an auto generated low-level Go binding around an Ethereum contract. +type ChequebookRaw struct { + Contract *Chequebook // Generic contract binding to access the raw methods on +} + +// ChequebookCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ChequebookCallerRaw struct { + Contract *ChequebookCaller // Generic read-only contract binding to access the raw methods on +} + +// ChequebookTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ChequebookTransactorRaw struct { + Contract *ChequebookTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewChequebook creates a new instance of Chequebook, bound to a specific deployed contract. +func NewChequebook(address common.Address, backend bind.ContractBackend) (*Chequebook, error) { + contract, err := bindChequebook(address, backend, backend) + if err != nil { + return nil, err + } + return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil +} + +// NewChequebookCaller creates a new read-only instance of Chequebook, bound to a specific deployed contract. +func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*ChequebookCaller, error) { + contract, err := bindChequebook(address, caller, nil) + if err != nil { + return nil, err + } + return &ChequebookCaller{contract: contract}, nil +} + +// NewChequebookTransactor creates a new write-only instance of Chequebook, bound to a specific deployed contract. +func NewChequebookTransactor(address common.Address, transactor bind.ContractTransactor) (*ChequebookTransactor, error) { + contract, err := bindChequebook(address, nil, transactor) + if err != nil { + return nil, err + } + return &ChequebookTransactor{contract: contract}, nil +} + +// bindChequebook binds a generic wrapper to an already deployed contract. +func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Chequebook *ChequebookRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Chequebook.Contract.ChequebookCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Chequebook *ChequebookRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.Contract.ChequebookTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Chequebook *ChequebookRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Chequebook.Contract.ChequebookTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Chequebook *ChequebookCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Chequebook.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Chequebook *ChequebookTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Chequebook *ChequebookTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Chequebook.Contract.contract.Transact(opts, method, params...) +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookCaller) Sent(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _Chequebook.contract.Call(opts, out, "sent", arg0) + return *ret0, err +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookSession) Sent(arg0 common.Address) (*big.Int, error) { + return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookCallerSession) Sent(arg0 common.Address) (*big.Int, error) { + return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookTransactor) Cash(opts *bind.TransactOpts, beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.contract.Transact(opts, "cash", beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookTransactorSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.contract.Transact(opts, "kill") +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookSession) Kill() (*types.Transaction, error) { + return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) { + return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) +} + +// MortalABI is the input ABI used to generate the binding from. +const MortalABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"}]` + +// MortalBin is the compiled bytecode used for deploying new contracts. +const MortalBin = `0x606060405260008054600160a060020a03191633179055605c8060226000396000f3606060405260e060020a600035046341c0e1b58114601a575b005b60186000543373ffffffffffffffffffffffffffffffffffffffff90811691161415605a5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b56` + +// DeployMortal deploys a new Ethereum contract, binding an instance of Mortal to it. +func DeployMortal(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Mortal, error) { + parsed, err := abi.JSON(strings.NewReader(MortalABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(MortalBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil +} + +// Mortal is an auto generated Go binding around an Ethereum contract. +type Mortal struct { + MortalCaller // Read-only binding to the contract + MortalTransactor // Write-only binding to the contract +} + +// MortalCaller is an auto generated read-only Go binding around an Ethereum contract. +type MortalCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MortalTransactor is an auto generated write-only Go binding around an Ethereum contract. +type MortalTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MortalSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type MortalSession struct { + Contract *Mortal // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MortalCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type MortalCallerSession struct { + Contract *MortalCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// MortalTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type MortalTransactorSession struct { + Contract *MortalTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MortalRaw is an auto generated low-level Go binding around an Ethereum contract. +type MortalRaw struct { + Contract *Mortal // Generic contract binding to access the raw methods on +} + +// MortalCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type MortalCallerRaw struct { + Contract *MortalCaller // Generic read-only contract binding to access the raw methods on +} + +// MortalTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type MortalTransactorRaw struct { + Contract *MortalTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewMortal creates a new instance of Mortal, bound to a specific deployed contract. +func NewMortal(address common.Address, backend bind.ContractBackend) (*Mortal, error) { + contract, err := bindMortal(address, backend, backend) + if err != nil { + return nil, err + } + return &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil +} + +// NewMortalCaller creates a new read-only instance of Mortal, bound to a specific deployed contract. +func NewMortalCaller(address common.Address, caller bind.ContractCaller) (*MortalCaller, error) { + contract, err := bindMortal(address, caller, nil) + if err != nil { + return nil, err + } + return &MortalCaller{contract: contract}, nil +} + +// NewMortalTransactor creates a new write-only instance of Mortal, bound to a specific deployed contract. +func NewMortalTransactor(address common.Address, transactor bind.ContractTransactor) (*MortalTransactor, error) { + contract, err := bindMortal(address, nil, transactor) + if err != nil { + return nil, err + } + return &MortalTransactor{contract: contract}, nil +} + +// bindMortal binds a generic wrapper to an already deployed contract. +func bindMortal(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(MortalABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Mortal *MortalRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Mortal.Contract.MortalCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Mortal *MortalRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.Contract.MortalTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Mortal *MortalRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Mortal.Contract.MortalTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Mortal *MortalCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Mortal.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Mortal *MortalTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Mortal *MortalTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Mortal.Contract.contract.Transact(opts, method, params...) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.contract.Transact(opts, "kill") +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalSession) Kill() (*types.Transaction, error) { + return _Mortal.Contract.Kill(&_Mortal.TransactOpts) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalTransactorSession) Kill() (*types.Transaction, error) { + return _Mortal.Contract.Kill(&_Mortal.TransactOpts) +} + +// OwnedABI is the input ABI used to generate the binding from. +const OwnedABI = `[{"inputs":[],"type":"constructor"}]` + +// OwnedBin is the compiled bytecode used for deploying new contracts. +const OwnedBin = `0x606060405260008054600160a060020a0319163317905560068060226000396000f3606060405200` + +// DeployOwned deploys a new Ethereum contract, binding an instance of Owned to it. +func DeployOwned(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Owned, error) { + parsed, err := abi.JSON(strings.NewReader(OwnedABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(OwnedBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil +} + +// Owned is an auto generated Go binding around an Ethereum contract. +type Owned struct { + OwnedCaller // Read-only binding to the contract + OwnedTransactor // Write-only binding to the contract +} + +// OwnedCaller is an auto generated read-only Go binding around an Ethereum contract. +type OwnedCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// OwnedTransactor is an auto generated write-only Go binding around an Ethereum contract. +type OwnedTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// OwnedSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type OwnedSession struct { + Contract *Owned // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// OwnedCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type OwnedCallerSession struct { + Contract *OwnedCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// OwnedTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type OwnedTransactorSession struct { + Contract *OwnedTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// OwnedRaw is an auto generated low-level Go binding around an Ethereum contract. +type OwnedRaw struct { + Contract *Owned // Generic contract binding to access the raw methods on +} + +// OwnedCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type OwnedCallerRaw struct { + Contract *OwnedCaller // Generic read-only contract binding to access the raw methods on +} + +// OwnedTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type OwnedTransactorRaw struct { + Contract *OwnedTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewOwned creates a new instance of Owned, bound to a specific deployed contract. +func NewOwned(address common.Address, backend bind.ContractBackend) (*Owned, error) { + contract, err := bindOwned(address, backend, backend) + if err != nil { + return nil, err + } + return &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil +} + +// NewOwnedCaller creates a new read-only instance of Owned, bound to a specific deployed contract. +func NewOwnedCaller(address common.Address, caller bind.ContractCaller) (*OwnedCaller, error) { + contract, err := bindOwned(address, caller, nil) + if err != nil { + return nil, err + } + return &OwnedCaller{contract: contract}, nil +} + +// NewOwnedTransactor creates a new write-only instance of Owned, bound to a specific deployed contract. +func NewOwnedTransactor(address common.Address, transactor bind.ContractTransactor) (*OwnedTransactor, error) { + contract, err := bindOwned(address, nil, transactor) + if err != nil { + return nil, err + } + return &OwnedTransactor{contract: contract}, nil +} + +// bindOwned binds a generic wrapper to an already deployed contract. +func bindOwned(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(OwnedABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Owned *OwnedRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Owned.Contract.OwnedCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Owned *OwnedRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Owned.Contract.OwnedTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Owned *OwnedRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Owned.Contract.OwnedTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Owned *OwnedCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Owned.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Owned *OwnedTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Owned.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Owned *OwnedTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Owned.Contract.contract.Transact(opts, method, params...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol new file mode 100644 index 000000000..eefe6c063 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol @@ -0,0 +1,45 @@ +import "mortal"; + +/// @title Chequebook for Ethereum micropayments +/// @author Daniel A. Nagy +contract chequebook is mortal { + // Cumulative paid amount in wei to each beneficiary + mapping (address => uint256) public sent; + + /// @notice Overdraft event + event Overdraft(address deadbeat); + + /// @notice Cash cheque + /// + /// @param beneficiary beneficiary address + /// @param amount cumulative amount in wei + /// @param sig_v signature parameter v + /// @param sig_r signature parameter r + /// @param sig_s signature parameter s + /// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount + function cash(address beneficiary, uint256 amount, + uint8 sig_v, bytes32 sig_r, bytes32 sig_s) { + // Check if the cheque is old. + // Only cheques that are more recent than the last cashed one are considered. + if(amount <= sent[beneficiary]) return; + // Check the digital signature of the cheque. + bytes32 hash = sha3(address(this), beneficiary, amount); + if(owner != ecrecover(hash, sig_v, sig_r, sig_s)) return; + // Attempt sending the difference between the cumulative amount on the cheque + // and the cumulative amount on the last cashed cheque to beneficiary. + if (amount - sent[beneficiary] >= this.balance) { + // update the cumulative amount before sending + sent[beneficiary] = amount; + if (!beneficiary.send(amount - sent[beneficiary])) { + // Upon failure to execute send, revert everything + throw; + } + } else { + // Upon failure, punish owner for writing a bounced cheque. + // owner.sendToDebtorsPrison(); + Overdraft(owner); + // Compensate beneficiary. + suicide(beneficiary); + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go new file mode 100644 index 000000000..b08e04e71 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go @@ -0,0 +1,5 @@ +package contract + +// ContractDeployedCode is used to detect suicides. This constant needs to be +// updated when the contract code is changed. +const ContractDeployedCode = "0x606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100bd57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bf575b505050505050565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a0390811691161461015a576100a3565b600160a060020a038681166000908152600160205260409020543090911631908603106101b357604060008181208790559051600160a060020a0388169190819081818181818881f1935050505015156100a357610002565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff" diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go new file mode 100644 index 000000000..faa927279 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go @@ -0,0 +1,71 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build none + +// This program generates contract/code.go, which contains the chequebook code +// after deployment. +package main + +import ( + "fmt" + "io/ioutil" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAccount = core.GenesisAccount{ + Address: crypto.PubkeyToAddress(testKey.PublicKey), + Balance: big.NewInt(500000000000), + } +) + +func main() { + backend := backends.NewSimulatedBackend(testAccount) + auth := bind.NewKeyedTransactor(testKey) + + // Deploy the contract, get the code. + addr, _, _, err := contract.DeployChequebook(auth, backend) + if err != nil { + panic(err) + } + backend.Commit() + code, err := backend.CodeAt(nil, addr, nil) + if err != nil { + panic(err) + } + if len(code) == 0 { + panic("empty code") + } + + // Write the output file. + content := fmt.Sprintf(`package contract + +// ContractDeployedCode is used to detect suicides. This constant needs to be +// updated when the contract code is changed. +const ContractDeployedCode = "%#x" +`, code) + if err := ioutil.WriteFile("contract/code.go", []byte(content), 0644); err != nil { + panic(err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go new file mode 100644 index 000000000..aca16de50 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go @@ -0,0 +1,921 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package contract + +import ( + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// ENSABI is the input ABI used to generate the binding from. +const ENSABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"type":"function"},{"inputs":[{"name":"owner","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"}]` + +// ENSBin is the compiled bytecode used for deploying new contracts. +const ENSBin = `0x606060405260405160208061032683395060806040525160008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a03191682179055506102c88061005e6000396000f3606060405260e060020a60003504630178b8bf811461004757806302571be31461006e57806306ab5923146100915780631896f70a146100c85780635b0fc9c3146100fc575b005b610130600435600081815260208190526040902060010154600160a060020a03165b919050565b610130600435600081815260208190526040902054600160a060020a0316610069565b6100456004356024356044356000838152602081905260408120548490600160a060020a0390811633919091161461014d57610002565b6100456004356024356000828152602081905260409020548290600160a060020a039081163391909116146101e757610002565b6100456004356024356000828152602081905260409020548290600160a060020a0390811633919091161461025957610002565b60408051600160a060020a03929092168252519081900360200190f35b60408051868152602081810187905282519182900383018220600160a060020a03871683529251929450869288927fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8292908290030190a382600060005060008460001916815260200190815260200160002060005060000160006101000a815481600160a060020a03021916908302179055505050505050565b60408051600160a060020a0384168152905184917f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0919081900360200190a2506000828152602081905260409020600101805473ffffffffffffffffffffffffffffffffffffffff1916821790555050565b60408051600160a060020a0384168152905184917fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266919081900360200190a2506000828152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff191682179055505056` + +// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it. +func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend, owner common.Address) (common.Address, *types.Transaction, *ENS, error) { + parsed, err := abi.JSON(strings.NewReader(ENSABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend, owner) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil +} + +// ENS is an auto generated Go binding around an Ethereum contract. +type ENS struct { + ENSCaller // Read-only binding to the contract + ENSTransactor // Write-only binding to the contract +} + +// ENSCaller is an auto generated read-only Go binding around an Ethereum contract. +type ENSCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ENSTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ENSTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ENSSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ENSSession struct { + Contract *ENS // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ENSCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ENSCallerSession struct { + Contract *ENSCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ENSTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ENSTransactorSession struct { + Contract *ENSTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ENSRaw is an auto generated low-level Go binding around an Ethereum contract. +type ENSRaw struct { + Contract *ENS // Generic contract binding to access the raw methods on +} + +// ENSCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ENSCallerRaw struct { + Contract *ENSCaller // Generic read-only contract binding to access the raw methods on +} + +// ENSTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ENSTransactorRaw struct { + Contract *ENSTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewENS creates a new instance of ENS, bound to a specific deployed contract. +func NewENS(address common.Address, backend bind.ContractBackend) (*ENS, error) { + contract, err := bindENS(address, backend, backend) + if err != nil { + return nil, err + } + return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil +} + +// NewENSCaller creates a new read-only instance of ENS, bound to a specific deployed contract. +func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCaller, error) { + contract, err := bindENS(address, caller, nil) + if err != nil { + return nil, err + } + return &ENSCaller{contract: contract}, nil +} + +// NewENSTransactor creates a new write-only instance of ENS, bound to a specific deployed contract. +func NewENSTransactor(address common.Address, transactor bind.ContractTransactor) (*ENSTransactor, error) { + contract, err := bindENS(address, nil, transactor) + if err != nil { + return nil, err + } + return &ENSTransactor{contract: contract}, nil +} + +// bindENS binds a generic wrapper to an already deployed contract. +func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ENSABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ENS *ENSRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ENS.Contract.ENSCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ENS *ENSRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ENS.Contract.ENSTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ENS *ENSRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ENS.Contract.ENSTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ENS *ENSCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ENS.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ENS *ENSTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ENS.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ENS *ENSTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ENS.Contract.contract.Transact(opts, method, params...) +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSCaller) Owner(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _ENS.contract.Call(opts, out, "owner", node) + return *ret0, err +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSSession) Owner(node [32]byte) (common.Address, error) { + return _ENS.Contract.Owner(&_ENS.CallOpts, node) +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSCallerSession) Owner(node [32]byte) (common.Address, error) { + return _ENS.Contract.Owner(&_ENS.CallOpts, node) +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSCaller) Resolver(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _ENS.contract.Call(opts, out, "resolver", node) + return *ret0, err +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSSession) Resolver(node [32]byte) (common.Address, error) { + return _ENS.Contract.Resolver(&_ENS.CallOpts, node) +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSCallerSession) Resolver(node [32]byte) (common.Address, error) { + return _ENS.Contract.Resolver(&_ENS.CallOpts, node) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSTransactor) SetOwner(opts *bind.TransactOpts, node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setOwner", node, owner) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSSession) SetOwner(node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetOwner(&_ENS.TransactOpts, node, owner) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSTransactorSession) SetOwner(node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetOwner(&_ENS.TransactOpts, node, owner) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSTransactor) SetResolver(opts *bind.TransactOpts, node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setResolver", node, resolver) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSSession) SetResolver(node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetResolver(&_ENS.TransactOpts, node, resolver) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSTransactorSession) SetResolver(node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetResolver(&_ENS.TransactOpts, node, resolver) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSTransactor) SetSubnodeOwner(opts *bind.TransactOpts, node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setSubnodeOwner", node, label, owner) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSSession) SetSubnodeOwner(node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetSubnodeOwner(&_ENS.TransactOpts, node, label, owner) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSTransactorSession) SetSubnodeOwner(node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetSubnodeOwner(&_ENS.TransactOpts, node, label, owner) +} + +// FIFSRegistrarABI is the input ABI used to generate the binding from. +const FIFSRegistrarABI = `[{"constant":false,"inputs":[{"name":"subnode","type":"bytes32"},{"name":"owner","type":"address"}],"name":"register","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"},{"name":"node","type":"bytes32"}],"type":"constructor"}]` + +// FIFSRegistrarBin is the compiled bytecode used for deploying new contracts. +const FIFSRegistrarBin = `0x6060604081815280610620833960a090525160805160008054600160a060020a031916831790558160a0610367806100878339018082600160a060020a03168152602001915050604051809103906000f0600160006101000a815481600160a060020a0302191690830217905550806002600050819055505050610232806103ee6000396000f3606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056606060405260e060020a6000350463d22057a9811461001b575b005b61001960043560243560025460408051918252602082810185905260008054835194859003840185207f02571be300000000000000000000000000000000000000000000000000000000865260048601819052935193949193600160a060020a03909116926302571be39260248181019391829003018187876161da5a03f11561000257505060405151915050600160a060020a0381166000148015906100d4575033600160a060020a031681600160a060020a031614155b156100de57610002565b60408051600080546002547f06ab592300000000000000000000000000000000000000000000000000000000845260048401526024830188905230600160a060020a03908116604485015293519316926306ab5923926064818101939291829003018183876161da5a03f11561000257505060008054600154604080517f1896f70a00000000000000000000000000000000000000000000000000000000815260048101889052600160a060020a0392831660248201529051929091169350631896f70a926044828101939192829003018183876161da5a03f11561000257505060008054604080517f5b0fc9c300000000000000000000000000000000000000000000000000000000815260048101879052600160a060020a0388811660248301529151929091169350635b0fc9c3926044828101939192829003018183876161da5a03f115610002575050505050505056` + +// DeployFIFSRegistrar deploys a new Ethereum contract, binding an instance of FIFSRegistrar to it. +func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address, node [32]byte) (common.Address, *types.Transaction, *FIFSRegistrar, error) { + parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(FIFSRegistrarBin), backend, ensAddr, node) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil +} + +// FIFSRegistrar is an auto generated Go binding around an Ethereum contract. +type FIFSRegistrar struct { + FIFSRegistrarCaller // Read-only binding to the contract + FIFSRegistrarTransactor // Write-only binding to the contract +} + +// FIFSRegistrarCaller is an auto generated read-only Go binding around an Ethereum contract. +type FIFSRegistrarCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// FIFSRegistrarTransactor is an auto generated write-only Go binding around an Ethereum contract. +type FIFSRegistrarTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// FIFSRegistrarSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type FIFSRegistrarSession struct { + Contract *FIFSRegistrar // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// FIFSRegistrarCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type FIFSRegistrarCallerSession struct { + Contract *FIFSRegistrarCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// FIFSRegistrarTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type FIFSRegistrarTransactorSession struct { + Contract *FIFSRegistrarTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// FIFSRegistrarRaw is an auto generated low-level Go binding around an Ethereum contract. +type FIFSRegistrarRaw struct { + Contract *FIFSRegistrar // Generic contract binding to access the raw methods on +} + +// FIFSRegistrarCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type FIFSRegistrarCallerRaw struct { + Contract *FIFSRegistrarCaller // Generic read-only contract binding to access the raw methods on +} + +// FIFSRegistrarTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type FIFSRegistrarTransactorRaw struct { + Contract *FIFSRegistrarTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewFIFSRegistrar creates a new instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrar(address common.Address, backend bind.ContractBackend) (*FIFSRegistrar, error) { + contract, err := bindFIFSRegistrar(address, backend, backend) + if err != nil { + return nil, err + } + return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil +} + +// NewFIFSRegistrarCaller creates a new read-only instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller) (*FIFSRegistrarCaller, error) { + contract, err := bindFIFSRegistrar(address, caller, nil) + if err != nil { + return nil, err + } + return &FIFSRegistrarCaller{contract: contract}, nil +} + +// NewFIFSRegistrarTransactor creates a new write-only instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrarTransactor(address common.Address, transactor bind.ContractTransactor) (*FIFSRegistrarTransactor, error) { + contract, err := bindFIFSRegistrar(address, nil, transactor) + if err != nil { + return nil, err + } + return &FIFSRegistrarTransactor{contract: contract}, nil +} + +// bindFIFSRegistrar binds a generic wrapper to an already deployed contract. +func bindFIFSRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_FIFSRegistrar *FIFSRegistrarRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _FIFSRegistrar.Contract.FIFSRegistrarCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_FIFSRegistrar *FIFSRegistrarRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_FIFSRegistrar *FIFSRegistrarRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_FIFSRegistrar *FIFSRegistrarCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _FIFSRegistrar.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.contract.Transact(opts, method, params...) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarTransactor) Register(opts *bind.TransactOpts, subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.contract.Transact(opts, "register", subnode, owner) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarTransactorSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner) +} + +// PublicResolverABI is the input ABI used to generate the binding from. +const PublicResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]` + +// PublicResolverBin is the compiled bytecode used for deploying new contracts. +const PublicResolverBin = `0x606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056` + +// DeployPublicResolver deploys a new Ethereum contract, binding an instance of PublicResolver to it. +func DeployPublicResolver(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address) (common.Address, *types.Transaction, *PublicResolver, error) { + parsed, err := abi.JSON(strings.NewReader(PublicResolverABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(PublicResolverBin), backend, ensAddr) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &PublicResolver{PublicResolverCaller: PublicResolverCaller{contract: contract}, PublicResolverTransactor: PublicResolverTransactor{contract: contract}}, nil +} + +// PublicResolver is an auto generated Go binding around an Ethereum contract. +type PublicResolver struct { + PublicResolverCaller // Read-only binding to the contract + PublicResolverTransactor // Write-only binding to the contract +} + +// PublicResolverCaller is an auto generated read-only Go binding around an Ethereum contract. +type PublicResolverCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PublicResolverTransactor is an auto generated write-only Go binding around an Ethereum contract. +type PublicResolverTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PublicResolverSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type PublicResolverSession struct { + Contract *PublicResolver // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PublicResolverCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type PublicResolverCallerSession struct { + Contract *PublicResolverCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// PublicResolverTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type PublicResolverTransactorSession struct { + Contract *PublicResolverTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PublicResolverRaw is an auto generated low-level Go binding around an Ethereum contract. +type PublicResolverRaw struct { + Contract *PublicResolver // Generic contract binding to access the raw methods on +} + +// PublicResolverCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type PublicResolverCallerRaw struct { + Contract *PublicResolverCaller // Generic read-only contract binding to access the raw methods on +} + +// PublicResolverTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type PublicResolverTransactorRaw struct { + Contract *PublicResolverTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewPublicResolver creates a new instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolver(address common.Address, backend bind.ContractBackend) (*PublicResolver, error) { + contract, err := bindPublicResolver(address, backend, backend) + if err != nil { + return nil, err + } + return &PublicResolver{PublicResolverCaller: PublicResolverCaller{contract: contract}, PublicResolverTransactor: PublicResolverTransactor{contract: contract}}, nil +} + +// NewPublicResolverCaller creates a new read-only instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolverCaller(address common.Address, caller bind.ContractCaller) (*PublicResolverCaller, error) { + contract, err := bindPublicResolver(address, caller, nil) + if err != nil { + return nil, err + } + return &PublicResolverCaller{contract: contract}, nil +} + +// NewPublicResolverTransactor creates a new write-only instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolverTransactor(address common.Address, transactor bind.ContractTransactor) (*PublicResolverTransactor, error) { + contract, err := bindPublicResolver(address, nil, transactor) + if err != nil { + return nil, err + } + return &PublicResolverTransactor{contract: contract}, nil +} + +// bindPublicResolver binds a generic wrapper to an already deployed contract. +func bindPublicResolver(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(PublicResolverABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PublicResolver *PublicResolverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _PublicResolver.Contract.PublicResolverCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PublicResolver *PublicResolverRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PublicResolver.Contract.PublicResolverTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PublicResolver *PublicResolverRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PublicResolver.Contract.PublicResolverTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PublicResolver *PublicResolverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _PublicResolver.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PublicResolver *PublicResolverTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PublicResolver.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PublicResolver *PublicResolverTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PublicResolver.Contract.contract.Transact(opts, method, params...) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverCaller) Addr(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _PublicResolver.contract.Call(opts, out, "addr", node) + return *ret0, err +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverSession) Addr(node [32]byte) (common.Address, error) { + return _PublicResolver.Contract.Addr(&_PublicResolver.CallOpts, node) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverCallerSession) Addr(node [32]byte) (common.Address, error) { + return _PublicResolver.Contract.Addr(&_PublicResolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverCaller) Content(opts *bind.CallOpts, node [32]byte) ([32]byte, error) { + var ( + ret0 = new([32]byte) + ) + out := ret0 + err := _PublicResolver.contract.Call(opts, out, "content", node) + return *ret0, err +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverSession) Content(node [32]byte) ([32]byte, error) { + return _PublicResolver.Contract.Content(&_PublicResolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverCallerSession) Content(node [32]byte) ([32]byte, error) { + return _PublicResolver.Contract.Content(&_PublicResolver.CallOpts, node) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "has", node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverTransactor) SetAddr(opts *bind.TransactOpts, node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "setAddr", node, addr) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverSession) SetAddr(node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.Contract.SetAddr(&_PublicResolver.TransactOpts, node, addr) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverTransactorSession) SetAddr(node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.Contract.SetAddr(&_PublicResolver.TransactOpts, node, addr) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverTransactor) SetContent(opts *bind.TransactOpts, node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "setContent", node, hash) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverSession) SetContent(node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.SetContent(&_PublicResolver.TransactOpts, node, hash) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverTransactorSession) SetContent(node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.SetContent(&_PublicResolver.TransactOpts, node, hash) +} + +// ResolverABI is the input ABI used to generate the binding from. +const ResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]` + +// ResolverBin is the compiled bytecode used for deploying new contracts. +const ResolverBin = `0x` + +// DeployResolver deploys a new Ethereum contract, binding an instance of Resolver to it. +func DeployResolver(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Resolver, error) { + parsed, err := abi.JSON(strings.NewReader(ResolverABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ResolverBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil +} + +// Resolver is an auto generated Go binding around an Ethereum contract. +type Resolver struct { + ResolverCaller // Read-only binding to the contract + ResolverTransactor // Write-only binding to the contract +} + +// ResolverCaller is an auto generated read-only Go binding around an Ethereum contract. +type ResolverCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ResolverTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ResolverTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ResolverSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ResolverSession struct { + Contract *Resolver // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ResolverCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ResolverCallerSession struct { + Contract *ResolverCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ResolverTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ResolverTransactorSession struct { + Contract *ResolverTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ResolverRaw is an auto generated low-level Go binding around an Ethereum contract. +type ResolverRaw struct { + Contract *Resolver // Generic contract binding to access the raw methods on +} + +// ResolverCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ResolverCallerRaw struct { + Contract *ResolverCaller // Generic read-only contract binding to access the raw methods on +} + +// ResolverTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ResolverTransactorRaw struct { + Contract *ResolverTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewResolver creates a new instance of Resolver, bound to a specific deployed contract. +func NewResolver(address common.Address, backend bind.ContractBackend) (*Resolver, error) { + contract, err := bindResolver(address, backend, backend) + if err != nil { + return nil, err + } + return &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil +} + +// NewResolverCaller creates a new read-only instance of Resolver, bound to a specific deployed contract. +func NewResolverCaller(address common.Address, caller bind.ContractCaller) (*ResolverCaller, error) { + contract, err := bindResolver(address, caller, nil) + if err != nil { + return nil, err + } + return &ResolverCaller{contract: contract}, nil +} + +// NewResolverTransactor creates a new write-only instance of Resolver, bound to a specific deployed contract. +func NewResolverTransactor(address common.Address, transactor bind.ContractTransactor) (*ResolverTransactor, error) { + contract, err := bindResolver(address, nil, transactor) + if err != nil { + return nil, err + } + return &ResolverTransactor{contract: contract}, nil +} + +// bindResolver binds a generic wrapper to an already deployed contract. +func bindResolver(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ResolverABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Resolver *ResolverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Resolver.Contract.ResolverCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Resolver *ResolverRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Resolver.Contract.ResolverTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Resolver *ResolverRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Resolver.Contract.ResolverTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Resolver *ResolverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Resolver.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Resolver *ResolverTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Resolver.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Resolver *ResolverTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Resolver.Contract.contract.Transact(opts, method, params...) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverCaller) Addr(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _Resolver.contract.Call(opts, out, "addr", node) + return *ret0, err +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverSession) Addr(node [32]byte) (common.Address, error) { + return _Resolver.Contract.Addr(&_Resolver.CallOpts, node) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverCallerSession) Addr(node [32]byte) (common.Address, error) { + return _Resolver.Contract.Addr(&_Resolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverCaller) Content(opts *bind.CallOpts, node [32]byte) ([32]byte, error) { + var ( + ret0 = new([32]byte) + ) + out := ret0 + err := _Resolver.contract.Call(opts, out, "content", node) + return *ret0, err +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverSession) Content(node [32]byte) ([32]byte, error) { + return _Resolver.Contract.Content(&_Resolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverCallerSession) Content(node [32]byte) ([32]byte, error) { + return _Resolver.Contract.Content(&_Resolver.CallOpts, node) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.contract.Transact(opts, "has", node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol new file mode 100644 index 000000000..114cd7319 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol @@ -0,0 +1,226 @@ +// Ethereum Name Service contracts by Nick Johnson +// +// To the extent possible under law, the person who associated CC0 with +// ENS contracts has waived all copyright and related or neighboring rights +// to ENS. +// +// You should have received a copy of the CC0 legalcode along with this +// work. If not, see . + +/** + * The ENS registry contract. + */ +contract ENS { + struct Record { + address owner; + address resolver; + } + + mapping(bytes32=>Record) records; + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the owner of a node changes the resolver for that node. + event NewResolver(bytes32 indexed node, address resolver); + + // Permits modifications only by the owner of the specified node. + modifier only_owner(bytes32 node) { + if(records[node].owner != msg.sender) throw; + _ + } + + /** + * Constructs a new ENS registrar, with the provided address as the owner of the root node. + */ + function ENS(address owner) { + records[0].owner = owner; + } + + /** + * Returns the address that owns the specified node. + */ + function owner(bytes32 node) constant returns (address) { + return records[node].owner; + } + + /** + * Returns the address of the resolver for the specified node. + */ + function resolver(bytes32 node) constant returns (address) { + return records[node].resolver; + } + + /** + * Transfers ownership of a node to a new address. May only be called by the current + * owner of the node. + * @param node The node to transfer ownership of. + * @param owner The address of the new owner. + */ + function setOwner(bytes32 node, address owner) only_owner(node) { + Transfer(node, owner); + records[node].owner = owner; + } + + /** + * Transfers ownership of a subnode sha3(node, label) to a new address. May only be + * called by the owner of the parent node. + * @param node The parent node. + * @param label The hash of the label specifying the subnode. + * @param owner The address of the new owner. + */ + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) only_owner(node) { + var subnode = sha3(node, label); + NewOwner(node, label, owner); + records[subnode].owner = owner; + } + + /** + * Sets the resolver address for the specified node. + * @param node The node to update. + * @param resolver The address of the resolver. + */ + function setResolver(bytes32 node, address resolver) only_owner(node) { + NewResolver(node, resolver); + records[node].resolver = resolver; + } +} + +/** + * A registrar that allocates subdomains to the first person to claim them. It also deploys + * a simple resolver contract and sets that as the default resolver on new names for + * convenience. + */ +contract FIFSRegistrar { + ENS ens; + PublicResolver defaultResolver; + bytes32 rootNode; + + /** + * Constructor. + * @param ensAddr The address of the ENS registry. + * @param node The node that this registrar administers. + */ + function FIFSRegistrar(address ensAddr, bytes32 node) { + ens = ENS(ensAddr); + defaultResolver = new PublicResolver(ensAddr); + rootNode = node; + } + + /** + * Register a name, or change the owner of an existing registration. + * @param subnode The hash of the label to register. + * @param owner The address of the new owner. + */ + function register(bytes32 subnode, address owner) { + var node = sha3(rootNode, subnode); + var currentOwner = ens.owner(node); + if(currentOwner != 0 && currentOwner != msg.sender) + throw; + + // Temporarily set ourselves as the owner + ens.setSubnodeOwner(rootNode, subnode, this); + // Set up the default resolver + ens.setResolver(node, defaultResolver); + // Set the owner to the real owner + ens.setOwner(node, owner); + } +} + +contract Resolver { + event AddrChanged(bytes32 indexed node, address a); + event ContentChanged(bytes32 indexed node, bytes32 hash); + + function has(bytes32 node, bytes32 kind) returns (bool); + function addr(bytes32 node) constant returns (address ret); + function content(bytes32 node) constant returns (bytes32 ret); +} + +/** + * A simple resolver anyone can use; only allows the owner of a node to set its + * address. + */ +contract PublicResolver is Resolver { + ENS ens; + mapping(bytes32=>address) addresses; + mapping(bytes32=>bytes32) contents; + + modifier only_owner(bytes32 node) { + if(ens.owner(node) != msg.sender) throw; + _ + } + + /** + * Constructor. + * @param ensAddr The ENS registrar contract. + */ + function PublicResolver(address ensAddr) { + ens = ENS(ensAddr); + } + + /** + * Fallback function. + */ + function() { + throw; + } + + /** + * Returns true if the specified node has the specified record type. + * @param node The ENS node to query. + * @param kind The record type name, as specified in EIP137. + * @return True if this resolver has a record of the provided type on the + * provided node. + */ + function has(bytes32 node, bytes32 kind) returns (bool) { + return (kind == "addr" && addresses[node] != 0) || + (kind == "content" && contents[node] != 0); + } + + /** + * Returns the address associated with an ENS node. + * @param node The ENS node to query. + * @return The associated address. + */ + function addr(bytes32 node) constant returns (address ret) { + ret = addresses[node]; + if(ret == 0) + throw; + } + + /** + * Returns the content hash associated with an ENS node. + * @param node The ENS node to query. + * @return The associated content hash. + */ + function content(bytes32 node) constant returns (bytes32 ret) { + ret = contents[node]; + if(ret == 0) + throw; + } + + /** + * Sets the address associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param addr The address to set. + */ + function setAddr(bytes32 node, address addr) only_owner(node) { + addresses[node] = addr; + AddrChanged(node, addr); + } + + /** + * Sets the content hash associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param hash The content hash to set. + */ + function setContent(bytes32 node, bytes32 hash) only_owner(node) { + contents[node] = hash; + ContentChanged(node, hash); + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go new file mode 100644 index 000000000..7806742cf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go @@ -0,0 +1,178 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ens + +//go:generate abigen --sol contract/ens.sol --pkg contract --out contract/ens.go + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/ens/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// swarm domain name registry and resolver +type ENS struct { + *contract.ENSSession + contractBackend bind.ContractBackend +} + +// NewENS creates a struct exposing convenient high-level operations for interacting with +// the Ethereum Name Service. +func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contractBackend bind.ContractBackend) (*ENS, error) { + ens, err := contract.NewENS(contractAddr, contractBackend) + if err != nil { + return nil, err + } + + return &ENS{ + &contract.ENSSession{ + Contract: ens, + TransactOpts: *transactOpts, + }, + contractBackend, + }, nil +} + +// DeployENS deploys an instance of the ENS nameservice, with a 'first in first served' root registrar. +func DeployENS(transactOpts *bind.TransactOpts, contractBackend bind.ContractBackend) (*ENS, error) { + // Deploy the ENS registry + ensAddr, _, _, err := contract.DeployENS(transactOpts, contractBackend, transactOpts.From) + if err != nil { + return nil, err + } + + ens, err := NewENS(transactOpts, ensAddr, contractBackend) + if err != nil { + return nil, err + } + + // Deploy the registrar + regAddr, _, _, err := contract.DeployFIFSRegistrar(transactOpts, contractBackend, ensAddr, [32]byte{}) + if err != nil { + return nil, err + } + + // Set the registrar as owner of the ENS root + _, err = ens.SetOwner([32]byte{}, regAddr) + if err != nil { + return nil, err + } + + return ens, nil +} + +func ensParentNode(name string) (common.Hash, common.Hash) { + parts := strings.SplitN(name, ".", 2) + label := crypto.Keccak256Hash([]byte(parts[0])) + if len(parts) == 1 { + return [32]byte{}, label + } else { + parentNode, parentLabel := ensParentNode(parts[1]) + return crypto.Keccak256Hash(parentNode[:], parentLabel[:]), label + } +} + +func ensNode(name string) common.Hash { + parentNode, parentLabel := ensParentNode(name) + return crypto.Keccak256Hash(parentNode[:], parentLabel[:]) +} + +func (self *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) { + resolverAddr, err := self.Resolver(node) + if err != nil { + return nil, err + } + + resolver, err := contract.NewPublicResolver(resolverAddr, self.contractBackend) + if err != nil { + return nil, err + } + + return &contract.PublicResolverSession{ + Contract: resolver, + TransactOpts: self.TransactOpts, + }, nil +} + +func (self *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) { + registrarAddr, err := self.Owner(node) + if err != nil { + return nil, err + } + + registrar, err := contract.NewFIFSRegistrar(registrarAddr, self.contractBackend) + if err != nil { + return nil, err + } + + return &contract.FIFSRegistrarSession{ + Contract: registrar, + TransactOpts: self.TransactOpts, + }, nil +} + +// Resolve is a non-transactional call that returns the content hash associated with a name. +func (self *ENS) Resolve(name string) (common.Hash, error) { + node := ensNode(name) + + resolver, err := self.getResolver(node) + if err != nil { + return common.Hash{}, err + } + + ret, err := resolver.Content(node) + if err != nil { + return common.Hash{}, err + } + + return common.BytesToHash(ret[:]), nil +} + +// Register registers a new domain name for the caller, making them the owner of the new name. +// Only works if the registrar for the parent domain implements the FIFS registrar protocol. +func (self *ENS) Register(name string) (*types.Transaction, error) { + parentNode, label := ensParentNode(name) + + registrar, err := self.getRegistrar(parentNode) + if err != nil { + return nil, err + } + + opts := self.TransactOpts + opts.GasLimit = big.NewInt(200000) + return registrar.Contract.Register(&opts, label, self.TransactOpts.From) +} + +// SetContentHash sets the content hash associated with a name. Only works if the caller +// owns the name, and the associated resolver implements a `setContent` function. +func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) { + node := ensNode(name) + + resolver, err := self.getResolver(node) + if err != nil { + return nil, err + } + + opts := self.TransactOpts + opts.GasLimit = big.NewInt(200000) + return resolver.Contract.SetContent(&opts, node, hash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/release/contract.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go similarity index 61% rename from vendor/github.com/ethereum/go-ethereum/release/contract.go rename to vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go index 51c1443d4..6a0b09931 100644 --- a/vendor/github.com/ethereum/go-ethereum/release/contract.go +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go @@ -14,10 +14,10 @@ import ( ) // ReleaseOracleABI is the input ABI used to generate the binding from. -const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` +const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` // ReleaseOracleBin is the compiled bytecode used for deploying new contracts. -const ReleaseOracleBin = `0x60606040526040516114033803806114038339810160405280510160008151600014156100ce57600160a060020a0333168152602081905260408120805460ff191660019081179091558054808201808355828183801582901161015e5781836000526020600020918201910161015e91905b8082111561019957600081558401610072565b505050919090600052602060002090016000848481518110156100025790602001906020020151909190916101000a815481600160a060020a0302191690830217905550506001015b815181101561018957600160006000506000848481518110156100025790602001906020020151600160a060020a0316815260200190815260200160002060006101000a81548160ff0219169083021790555060016000508054806001018281815481835581811511610085576000839052610085906000805160206113e3833981519152908101908301610072565b5050506000929092526000805160206113e3833981519152018054600160a060020a03191633179055505b50506112458061019e6000396000f35b50905600606060405236156100775760e060020a600035046326db7648811461007957806346f0975a1461019e5780635c3d005d1461020a57806364ed31fe146102935780639d888e861461038d578063bc8fbbf8146103b2578063bf8ecf9c146103fb578063d0e0813a14610467578063d67cbec914610478575b005b610495604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a938490049093029390926007929184919083018282801561012657602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610107575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561018357602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610164575b50505050509050955095509550955095509550909192939495565b6040805160208181018352600082526001805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101e0575b505050505090505b90565b6100776004356106d78160005b600160a060020a033316600090815260208190526040812054819060ff16156106df57600160a060020a038416815260026020526040812091505b81548110156106e7578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610732576106df565b6105926004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561032057602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610301575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561037d57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161035e575b5050505050905091509150915091565b6106176000600060006000600060006008600050805490506000141561064e576106cf565b6100776106e56000808080805b600160a060020a033316600090815260208190526040812054819060ff161561121c57821580156103f1575060065481145b15610ca55761121c565b6040805160208181018352600082526003805484518184028101840190955280855261054894928301828280156101ff57602002820191906000526020600020908154600160a060020a03168152600191909101906020018083116101e0575b50505050509050610207565b6100776004356106d7816001610217565b6100776004356024356044356064356106df8484848460016103bf565b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff16815260200184815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019850505050505050505060405180910390f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f15090500194505050505060405180910390f35b6040805163ffffffff9687168152948616602086015292909416838301526060830152608082019290925290519081900360a00190f35b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b50565b505050505b50505050565b565b5060005b600182015481101561073a5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610784576106df565b600101610252565b8154600014801561074f575060018201546000145b156107ac576003805460018101808355828183801582901161078c5781836000526020600020918201910161078c9190610834565b6001016106eb565b5050506000928352506020909120018054600160a060020a031916851790555b821561084c578154600181018084558391908281838015829011610881578183600052602060002091820191016108819190610834565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b5c91908101905b808211156108485760008155600101610834565b5090565b816001016000508054806001018281815481835581811511610933578183600052602060002091820191016109339190610834565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108b5576106df565b8280156108db5750600160a060020a03841660009081526020819052604090205460ff16155b1561096a57600160a060020a0384166000908152602081905260409020805460ff19166001908117909155805480820180835582818380158290116107e3578183600052602060002091820191016107e39190610834565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108b5576106df565b821580156109905750600160a060020a03841660009081526020819052604090205460ff165b156108035750600160a060020a0383166000908152602081905260408120805460ff191690555b6001548110156108035783600160a060020a03166001600050828154811015610002576000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60154600160a060020a03161415610ad057600180546000198101908110156100025760009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601909054906101000a9004600160a060020a03166001600050828154811015610002577fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6018054600160a060020a03191690921790915580546000198101808355909190828015829011610ad857818360005260206000209182019101610ad89190610834565b6001016109b7565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b32907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5060018201805460008083559182526020909120610b5291810190610834565b5050505050610803565b5060018201805460008083559182526020909120610b7c91810190610834565b506000925050505b6003548110156106df5783600160a060020a03166003600050828154811015610002576000919091527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154600160a060020a03161415610c9d57600380546000198101908110156100025760009182527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01909054906101000a9004600160a060020a03166003600050828154811015610002577fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018054600160a060020a031916909217909155805460001981018083559091908280158290116106da578183600052602060002091820191016106da9190610834565b600101610b84565b60065460001415610d03576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d6d575060045463ffffffff8881169116141580610d395750600454640100000000900463ffffffff90811690871614155b80610d56575060045463ffffffff868116604060020a9092041614155b80610d6d5750600454606060020a90819004028414155b15610d775761121c565b506006905060005b8154811015610dc0578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610e0b5761121c565b5060005b6001820154811015610e135733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610e485761121c565b600101610d7f565b8215610e50578154600181018084558391908281838015829011610e8557600083815260209020610e85918101908301610834565b600101610dc4565b816001016000508054806001018281815481835581811511610f0857818360005260206000209182019101610f089190610834565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610eb95761121c565b8215610f3f576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f9457600402816004028360005260206000209182019101610f9491906110ae565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610eb95761121c565b600060048181556005805467ffffffffffffffff19169055600680548382558184529192918290611225907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9290921691909117905560068054600284018054828255600082815260209020949594919283929182019185821561110d5760005260206000209182015b8281111561110d57825482559160010191906001019061108b565b505050506004015b8082111561084857600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110ed9190810190610834565b50600182018054600080835591825260209091206110a691810190610834565b506111339291505b80821115610848578054600160a060020a0319168155600101611115565b50506001818101805491840180548083556000838152602090209293830192909182156111815760005260206000209182015b82811115611181578254825591600101919060010190611166565b5061118d929150611115565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529197509195509093508492506111ec91507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610834565b506001820180546000808355918252602090912061120c91810190610834565b505050505061121c565b50505050505b50505050505050565b50600182018054600080835591825260209091206112169181019061083456b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6` +const ReleaseOracleBin = `0x606060405260405161135338038061135383398101604052805101600081516000141561008457600160a060020a0333168152602081905260408120805460ff19166001908117909155805480820180835582818380158290116100ff576000838152602090206100ff9181019083015b8082111561012f5760008155600101610070565b5060005b815181101561011f5760016000600050600084848151811015610002576020908102909101810151600160a060020a03168252810191909152604001600020805460ff1916909117905560018054808201808355828183801582901161013357600083815260209020610133918101908301610070565b5050506000928352506020909120018054600160a060020a031916331790555b50506111df806101746000396000f35b5090565b50505091909060005260206000209001600084848151811015610002575050506020838102850101518154600160a060020a0319161790555060010161008856606060405236156100775760e060020a600035046326db7648811461007957806346f0975a1461019e5780635c3d005d1461020a57806364ed31fe146102935780639d888e861461038d578063bc8fbbf8146103b2578063bf8ecf9c146103fc578063d0e0813a14610468578063d67cbec914610479575b005b610496604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a938490049093029390926007929184919083018282801561012657602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610107575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561018357602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610164575b50505050509050955095509550955095509550909192939495565b6040805160208181018352600082526001805484518184028101840190955280855261055894928301828280156101ff57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101e0575b505050505090505b90565b61007760043561066d8160005b600160a060020a033316600090815260208190526040812054819060ff161561070057600160a060020a038416815260026020526040812091505b8154811015610706578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a0316141561075157610700565b6105a26004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561032057602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610301575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561037d57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161035e575b5050505050905091509150915091565b61062760006000600060006000600060086000508054905060001415610670576106f1565b6100776106f96000808080805b600160a060020a033316600090815260208190526040812054819060ff16156111b657821580156103f257506006546000145b15610c2e576111b6565b6040805160208181018352600082526003805484518184028101840190955280855261055894928301828280156101ff57602002820191906000526020600020908154600160a060020a03168152600191909101906020018083116101e0575b50505050509050610207565b61007760043561066d816001610217565b6100776004356024356044356064356107008484848460016103bf565b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff168152602001846bffffffffffffffffffffffff1916815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019850505050505050505060405180910390f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019250505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f15090500194505050505060405180910390f35b6040805163ffffffff9687168152948616602086015292909416838301526bffffffffffffffffffffffff19166060830152608082019290925290519081900360a00190f35b50565b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b565b505050505b50505050565b5060005b60018201548110156107595733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a031614156107a357610700565b600101610252565b8154600014801561076e575060018201546000145b156107cb57600380546001810180835582818380158290116107ab578183600052602060002091820191016107ab9190610851565b60010161070a565b5050506000928352506020909120018054600160a060020a031916851790555b821561086957815460018101808455839190828183801582901161089e5760008381526020902061089e918101908301610851565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b2f91908101905b808211156108655760008155600101610851565b5090565b816001016000508054806001018281815481835581811511610950578183600052602060002091820191016109509190610851565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108d257610700565b8280156108f85750600160a060020a03841660009081526020819052604090205460ff16155b1561098757600160a060020a0384166000908152602081905260409020805460ff1916600190811790915580548082018083558281838015829011610800578183600052602060002091820191016108009190610851565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108d257610700565b821580156109ad5750600160a060020a03841660009081526020819052604090205460ff165b156108205750600160a060020a0383166000908152602081905260408120805460ff191690555b6001548110156108205783600160a060020a0316600160005082815481101561000257600091825260209091200154600160a060020a03161415610aa357600180546000198101908110156100025760206000908120929052600180549290910154600160a060020a031691839081101561000257906000526020600020900160006101000a815481600160a060020a030219169083021790555060016000508054809190600190039090815481835581811511610aab57600083815260209020610aab918101908301610851565b6001016109d4565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b05907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b5060018201805460008083559182526020909120610b2591810190610851565b5050505050610820565b5060018201805460008083559182526020909120610b4f91810190610851565b506000925050505b6003548110156107005783600160a060020a0316600360005082815481101561000257600091825260209091200154600160a060020a03161415610c2657600380546000198101908110156100025760206000908120929052600380549290910154600160a060020a031691839081101561000257906000526020600020900160006101000a815481600160a060020a0302191690830217905550600360005080548091906001900390908154818355818115116106fb576000838152602090206106fb918101908301610851565b600101610b57565b60065460001415610c8c576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d08575060045463ffffffff8881169116141580610cc1575060045463ffffffff8781166401000000009092041614155b80610cde575060045463ffffffff868116604060020a9092041614155b80610d085750600454606060020a90819004026bffffffffffffffffffffffff1990811690851614155b15610d12576111b6565b506006905060005b8154811015610d5b578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610da6576111b6565b5060005b6001820154811015610dae5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610de3576111b6565b600101610d1a565b8215610deb578154600181018084558391908281838015829011610e2057600083815260209020610e20918101908301610851565b600101610d5f565b816001016000508054806001018281815481835581811511610ea357818360005260206000209182019101610ea39190610851565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610e54576111b6565b8215610eda576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f2f57600402816004028360005260206000209182019101610f2f9190611048565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610e54576111b6565b600060048181556005805467ffffffffffffffff191690556006805483825581845291929182906111bf907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9092169190911790556006805460028401805482825560008281526020902094959491928392918201918582156110a75760005260206000209182015b828111156110a7578254825591600101919060010190611025565b505050506004015b8082111561086557600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110879190810190610851565b506001820180546000808355918252602090912061104091810190610851565b506110cd9291505b80821115610865578054600160a060020a03191681556001016110af565b505060018181018054918401805480835560008381526020902092938301929091821561111b5760005260206000209182015b8281111561111b578254825591600101919060010190611100565b506111279291506110af565b5050600060048181556005805467ffffffffffffffff191690556006805483825581845291975091955090935084925061118691507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b50600182018054600080835591825260209091206111a691810190610851565b50505050506111b6565b50505050505b50505050505050565b50600182018054600080835591825260209091206111b09181019061085156` // DeployReleaseOracle deploys a new Ethereum contract, binding an instance of ReleaseOracle to it. func DeployReleaseOracle(auth *bind.TransactOpts, backend bind.ContractBackend, signers []common.Address) (common.Address, *types.Transaction, *ReleaseOracle, error) { @@ -87,7 +87,7 @@ type ReleaseOracleTransactorRaw struct { // NewReleaseOracle creates a new instance of ReleaseOracle, bound to a specific deployed contract. func NewReleaseOracle(address common.Address, backend bind.ContractBackend) (*ReleaseOracle, error) { - contract, err := bindReleaseOracle(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor)) + contract, err := bindReleaseOracle(address, backend, backend) if err != nil { return nil, err } diff --git a/vendor/github.com/ethereum/go-ethereum/release/contract.sol b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol similarity index 100% rename from vendor/github.com/ethereum/go-ethereum/release/contract.sol rename to vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol diff --git a/vendor/github.com/ethereum/go-ethereum/release/release.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go similarity index 98% rename from vendor/github.com/ethereum/go-ethereum/release/release.go rename to vendor/github.com/ethereum/go-ethereum/contracts/release/release.go index 7c358aece..cd79112cd 100644 --- a/vendor/github.com/ethereum/go-ethereum/release/release.go +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -17,6 +17,8 @@ // Package release contains the node service that tracks client releases. package release +//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go + import ( "fmt" "strings" diff --git a/vendor/github.com/ethereum/go-ethereum/core/block_validator.go b/vendor/github.com/ethereum/go-ethereum/core/block_validator.go index e5bc6178b..65f975345 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/block_validator.go +++ b/vendor/github.com/ethereum/go-ethereum/core/block_validator.go @@ -41,13 +41,13 @@ var ( // // BlockValidator implements Validator. type BlockValidator struct { - config *ChainConfig // Chain configuration options - bc *BlockChain // Canonical block chain - Pow pow.PoW // Proof of work used for validating + config *params.ChainConfig // Chain configuration options + bc *BlockChain // Canonical block chain + Pow pow.PoW // Proof of work used for validating } // NewBlockValidator returns a new block validator which is safe for re-use -func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { +func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { validator := &BlockValidator{ config: config, Pow: pow, @@ -93,14 +93,14 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { // Verify UncleHash before running other uncle validations unclesSha := types.CalcUncleHash(block.Uncles()) if unclesSha != header.UncleHash { - return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) + return fmt.Errorf("invalid uncles root hash (remote: %x local: %x)", header.UncleHash, unclesSha) } // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) // can be used by light clients to make sure they've received the correct Txs txSha := types.DeriveSha(block.Transactions()) if txSha != header.TxHash { - return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) + return fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha) } return nil @@ -113,23 +113,23 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) { header := block.Header() if block.GasUsed().Cmp(usedGas) != 0 { - return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas)) + return ValidationError(fmt.Sprintf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas)) } // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. rbloom := types.CreateBloom(receipts) if rbloom != header.Bloom { - return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom) + return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) } // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) receiptSha := types.DeriveSha(receipts) if receiptSha != header.ReceiptHash { - return fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha) + return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } // Validate the state root against the received state root and throw // an error if they don't match. - if root := statedb.IntermediateRoot(); header.Root != root { - return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) + if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { + return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } return nil } @@ -203,7 +203,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b // Validates a header. Returns an error if the header is invalid. // // See YP section 4.3.4. "Block Header Validity" -func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { +func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) } @@ -223,7 +223,7 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) if expd.Cmp(header.Difficulty) != 0 { - return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) + return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd) } a := new(big.Int).Set(parent.GasLimit) @@ -232,7 +232,7 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare b := new(big.Int).Set(parent.GasLimit) b = b.Div(b, params.GasLimitBoundDivisor) if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { - return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b) + return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b) } num := new(big.Int).Set(parent.Number) @@ -248,13 +248,21 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare } } // If all checks passed, validate the extra-data field for hard forks - return ValidateDAOHeaderExtraData(config, header) + if err := ValidateDAOHeaderExtraData(config, header); err != nil { + return err + } + if !uncle && config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { + if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { + return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) + } + } + return nil } // CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. -func CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { +func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) } else { diff --git a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go index edb12b1f4..9ace6d79a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -78,7 +79,7 @@ const ( // included in the canonical one where as GetBlockByNumber always represents the // canonical chain. type BlockChain struct { - config *ChainConfig // chain & network configuration + config *params.ChainConfig // chain & network configuration hc *HeaderChain chainDb ethdb.Database @@ -93,10 +94,11 @@ type BlockChain struct { currentBlock *types.Block // Current head of the block chain currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!) - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - blockCache *lru.Cache // Cache for the most recent entire blocks - futureBlocks *lru.Cache // future blocks are blocks added for later processing + stateCache *state.StateDB // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + blockCache *lru.Cache // Cache for the most recent entire blocks + futureBlocks *lru.Cache // future blocks are blocks added for later processing quit chan struct{} // blockchain quit channel running int32 // running must be called atomically @@ -112,7 +114,7 @@ type BlockChain struct { // NewBlockChain returns a fully initialised block chain using information // available in the database. It initialiser the default Ethereum Validator and // Processor. -func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { +func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -196,7 +198,15 @@ func (self *BlockChain) loadLastState() error { self.currentFastBlock = block } } - // Issue a status log and return + // Initialize a statedb cache to ensure singleton account bloom filter generation + statedb, err := state.New(self.currentBlock.Root(), self.chainDb) + if err != nil { + return err + } + self.stateCache = statedb + self.stateCache.GetAccount(common.Address{}) + + // Issue a status log for the user headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()) blockTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64()) fastTd := self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()) @@ -260,7 +270,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error { if block == nil { return fmt.Errorf("non existent block [%x…]", hash[:4]) } - if _, err := trie.NewSecure(block.Root(), self.chainDb); err != nil { + if _, err := trie.NewSecure(block.Root(), self.chainDb, 0); err != nil { return err } // If all checks out, manually set the head block @@ -348,7 +358,12 @@ func (self *BlockChain) AuxValidator() pow.PoW { return self.pow } // State returns a new mutable state based on the current HEAD block. func (self *BlockChain) State() (*state.StateDB, error) { - return state.New(self.CurrentBlock().Root(), self.chainDb) + return self.StateAt(self.CurrentBlock().Root()) +} + +// StateAt returns a new mutable state based on a particular point in time. +func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { + return self.stateCache.New(root) } // Reset purges the entire blockchain, restoring it to its genesis state. @@ -619,17 +634,19 @@ func (self *BlockChain) Rollback(chain []common.Hash) { } // SetReceiptsData computes all the non-consensus fields of the receipts -func SetReceiptsData(block *types.Block, receipts types.Receipts) { +func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) { + signer := types.MakeSigner(config, block.Number()) + transactions, logIndex := block.Transactions(), uint(0) for j := 0; j < len(receipts); j++ { // The transaction hash can be retrieved from the transaction itself receipts[j].TxHash = transactions[j].Hash() + tx, _ := transactions[j].AsMessage(signer) // The contract address can be derived from the transaction itself - if MessageCreatesContract(transactions[j]) { - from, _ := transactions[j].From() - receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce()) + if MessageCreatesContract(tx) { + receipts[j].ContractAddress = crypto.CreateAddress(tx.From(), tx.Nonce()) } // The used gas can be calculated based on previous receipts if j == 0 { @@ -651,6 +668,7 @@ func SetReceiptsData(block *types.Block, receipts types.Receipts) { // InsertReceiptChain attempts to complete an already existing header chain with // transaction and receipt data. +// XXX should this be moved to the test? func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { self.wg.Add(1) defer self.wg.Done() @@ -690,7 +708,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain continue } // Compute all the non-consensus fields of the receipts - SetReceiptsData(block, receipts) + SetReceiptsData(self.config, block, receipts) // Write all the data out into the database if err := WriteBody(self.chainDb, block.Hash(), block.NumberU64(), block.Body()); err != nil { errs[index] = fmt.Errorf("failed to write block body: %v", err) @@ -761,8 +779,12 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain // Report some public statistics so the user has a clue what's going on first, last := blockChain[0], blockChain[len(blockChain)-1] - glog.V(logger.Info).Infof("imported %d receipt(s) (%d ignored) in %v. #%d [%x… / %x…]", stats.processed, stats.ignored, - time.Since(start), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) + + ignored := "" + if stats.ignored > 0 { + ignored = fmt.Sprintf(" (%d ignored)", stats.ignored) + } + glog.V(logger.Info).Infof("imported %d receipts in %9v. #%d [%x… / %x…]%s", stats.processed, common.PrettyDuration(time.Since(start)), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4], ignored) return 0, nil } @@ -826,20 +848,16 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { // faster than direct delivery and requires much less mutex // acquiring. var ( - stats struct{ queued, processed, ignored int } + stats = insertStats{startTime: time.Now()} events = make([]interface{}, 0, len(chain)) coalescedLogs vm.Logs - tstart = time.Now() - - nonceChecked = make([]bool, len(chain)) - statedb *state.StateDB + nonceChecked = make([]bool, len(chain)) ) // Start the parallel nonce verifier. nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain) defer close(nonceAbort) - txcount := 0 for i, block := range chain { if atomic.LoadInt32(&self.procInterrupt) == 1 { glog.V(logger.Debug).Infoln("Premature abort during block chain processing") @@ -860,7 +878,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { if BadHashes[block.Hash()] { err := BadHashError(block.Hash()) - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } // Stage 1 validation of the block using the chain's validator @@ -892,36 +910,37 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { continue } - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } // Create a new statedb using the parent block and report an // error if it fails. - if statedb == nil { - statedb, err = state.New(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), self.chainDb) - } else { - err = statedb.Reset(chain[i-1].Root()) + switch { + case i == 0: + err = self.stateCache.Reset(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) + default: + err = self.stateCache.Reset(chain[i-1].Root()) } if err != nil { - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } // Process block using the parent state as reference point. - receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig) + receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{}) if err != nil { - reportBlock(block, err) + self.reportBlock(block, receipts, err) return i, err } // Validate the state using the default validator - err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas) + err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas) if err != nil { - reportBlock(block, err) + self.reportBlock(block, receipts, err) return i, err } // Write state changes to database - _, err = statedb.Commit() + _, err = self.stateCache.Commit(self.config.IsEIP158(block.Number())) if err != nil { return i, err } @@ -933,7 +952,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { return i, err } - txcount += len(block.Transactions()) // write the block to the chain and get the status status, err := self.WriteBlock(block) if err != nil { @@ -943,8 +961,9 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { switch status { case CanonStatTy: if glog.V(logger.Debug) { - glog.Infof("[%v] inserted block #%d (%d TXs %v G %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), block.GasUsed(), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) + glog.Infof("inserted block #%d [%x…] in %9v: %3d txs %7v gas %d uncles.", block.Number(), block.Hash().Bytes()[0:4], common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), block.GasUsed(), len(block.Uncles())) } + blockInsertTimer.UpdateSince(bstart) events = append(events, ChainEvent{block, block.Hash(), logs}) // This puts transactions in a extra db for rpc @@ -961,26 +980,78 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { } case SideStatTy: if glog.V(logger.Detail) { - glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) + 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}) case SplitStatTy: events = append(events, ChainSplitEvent{block, logs}) } + stats.processed++ + if glog.V(logger.Info) { + stats.usedGas += usedGas.Uint64() + stats.report(chain, i) + } } - if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) { - tend := time.Since(tstart) - start, end := chain[0], chain[len(chain)-1] - glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) - } go self.postChainEvents(events, coalescedLogs) return 0, nil } +// insertStats tracks and reports on block insertion. +type insertStats struct { + queued, processed, ignored int + usedGas uint64 + lastIndex int + startTime time.Time +} + +// statsReportLimit is the time limit during import after which we always print +// out progress. This avoids the user wondering what's going on. +const statsReportLimit = 8 * time.Second + +// report prints statistics if some number of blocks have been processed +// or more than a few seconds have passed since the last message. +func (st *insertStats) report(chain []*types.Block, index int) { + // Fetch the timings for the batch + var ( + now = time.Now() + elapsed = now.Sub(st.startTime) + ) + if elapsed == 0 { // Yes Windows, I'm looking at you + elapsed = 1 + } + // If we're at the last block of the batch or report period reached, log + if index == len(chain)-1 || elapsed >= statsReportLimit { + start, end := chain[st.lastIndex], chain[index] + txcount := countTransactions(chain[st.lastIndex : index+1]) + + extra := "" + if st.queued > 0 || st.ignored > 0 { + extra = fmt.Sprintf(" (%d queued %d ignored)", st.queued, st.ignored) + } + hashes := "" + if st.processed > 1 { + hashes = fmt.Sprintf("%x… / %x…", start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) + } else { + hashes = fmt.Sprintf("%x…", end.Hash().Bytes()[:4]) + } + glog.Infof("imported %d blocks, %5d txs (%7.3f Mg) in %9v (%6.3f Mg/s). #%v [%s]%s", st.processed, txcount, float64(st.usedGas)/1000000, common.PrettyDuration(elapsed), float64(st.usedGas)*1000/float64(elapsed), end.Number(), hashes, extra) + + *st = insertStats{startTime: now, lastIndex: index} + } +} + +func countTransactions(chain []*types.Block) (c int) { + for _, b := range chain { + c += len(b.Transactions()) + } + return c +} + // reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them // to be part of the new canonical chain and accumulates potential missing transactions and post an // event about them @@ -1136,10 +1207,23 @@ func (self *BlockChain) update() { } // reportBlock logs a bad block error. -func reportBlock(block *types.Block, err error) { +func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { if glog.V(logger.Error) { - glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex()) - glog.Errorf(" %v", err) + var receiptString string + for _, receipt := range receipts { + receiptString += fmt.Sprintf("\t%v\n", receipt) + } + glog.Errorf(` +########## BAD BLOCK ######### +Chain config: %v + +Number: %v +Hash: 0x%x +%v + +Error: %v +############################## +`, bc.config, block.Number(), block.Hash(), receiptString, err) } } @@ -1242,4 +1326,4 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { } // Config retrieves the blockchain's chain configuration. -func (self *BlockChain) Config() *ChainConfig { return self.config } +func (self *BlockChain) Config() *params.ChainConfig { return self.config } diff --git a/vendor/github.com/ethereum/go-ethereum/core/blocks.go b/vendor/github.com/ethereum/go-ethereum/core/blocks.go index ecccc541f..cf8c86507 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/blocks.go +++ b/vendor/github.com/ethereum/go-ethereum/core/blocks.go @@ -21,4 +21,5 @@ import "github.com/ethereum/go-ethereum/common" // Set of manually tracked bad hashes (usually hard forks) var BadHashes = map[common.Hash]bool{ common.HexToHash("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689"): true, + common.HexToHash("7d05d08cbc596a2e5e4f13b80a743e53e09221b5323c3a61946b20873e58583f"): true, } diff --git a/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go index 0b9a5f75d..e1dafb32d 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go +++ b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go @@ -35,8 +35,8 @@ import ( */ // MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. -func MakeChainConfig() *ChainConfig { - return &ChainConfig{ +func MakeChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: true, @@ -73,6 +73,8 @@ type BlockGen struct { txs []*types.Transaction receipts []*types.Receipt uncles []*types.Header + + config *params.ChainConfig } // SetCoinbase sets the coinbase of the generated block. @@ -106,7 +108,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -131,7 +133,7 @@ func (b *BlockGen) AddUncheckedReceipt(receipt *types.Receipt) { // TxNonce returns the next valid transaction nonce for the // account at addr. It panics if the account does not exist. func (b *BlockGen) TxNonce(addr common.Address) uint64 { - if !b.statedb.HasAccount(addr) { + if !b.statedb.Exist(addr) { panic("account does not exist") } return b.statedb.GetNonce(addr) @@ -178,10 +180,10 @@ func (b *BlockGen) OffsetTime(seconds int64) { // Blocks created by GenerateChain do not contain valid proof of work // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. -func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { +func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) { - b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb} + b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config} // Mutate the state and block according to any hard-fork specs if config == nil { @@ -203,7 +205,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, gen(i, b) } AccumulateRewards(statedb, h, b.uncles) - root, err := statedb.Commit() + root, err := statedb.Commit(config.IsEIP158(h.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } @@ -215,7 +217,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, if err != nil { panic(err) } - header := makeHeader(parent, statedb) + header := makeHeader(config, parent, statedb) block, receipt := genblock(i, header, statedb) blocks[i] = block receipts[i] = receipt @@ -224,7 +226,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, return blocks, receipts } -func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { +func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header { var time *big.Int if parent.Time() == nil { time = big.NewInt(10) @@ -232,7 +234,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } return &types.Header{ - Root: state.IntermediateRoot(), + Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), @@ -283,7 +285,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [ // makeBlockChain creates a deterministic chain of blocks rooted at parent. func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block { - blocks, _ := GenerateChain(nil, parent, db, n, func(i int, b *BlockGen) { + blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) return blocks diff --git a/vendor/github.com/ethereum/go-ethereum/core/config.go b/vendor/github.com/ethereum/go-ethereum/core/config.go deleted file mode 100644 index c0d065a57..000000000 --- a/vendor/github.com/ethereum/go-ethereum/core/config.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/core/vm" -) - -var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error - -// ChainConfig is the core config which determines the blockchain settings. -// -// ChainConfig is stored in the database on a per block basis. This means -// that any network, identified by its genesis block, can have its own -// set of configuration options. -type ChainConfig struct { - HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead) - DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) - DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork - - VmConfig vm.Config `json:"-"` -} - -// IsHomestead returns whether num is either equal to the homestead block or greater. -func (c *ChainConfig) IsHomestead(num *big.Int) bool { - if c.HomesteadBlock == nil || num == nil { - return false - } - return num.Cmp(c.HomesteadBlock) >= 0 -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/dao.go b/vendor/github.com/ethereum/go-ethereum/core/dao.go index e315c9884..1260c310a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/dao.go +++ b/vendor/github.com/ethereum/go-ethereum/core/dao.go @@ -33,7 +33,7 @@ import ( // with the fork specific extra-data set // b) if the node is pro-fork, require blocks in the specific range to have the // unique extra-data set. -func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error { +func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error { // Short circuit validation if the node doesn't care about the DAO fork if config.DAOForkBlock == nil { return nil diff --git a/vendor/github.com/ethereum/go-ethereum/core/database_util.go b/vendor/github.com/ethereum/go-ethereum/core/database_util.go index 73fac20aa..84669de35 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/database_util.go +++ b/vendor/github.com/ethereum/go-ethereum/core/database_util.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" "fmt" "math/big" @@ -28,6 +29,7 @@ import ( "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" "github.com/ethereum/go-ethereum/rlp" ) @@ -59,6 +61,8 @@ var ( oldBlockNumPrefix = []byte("block-num-") oldBlockReceiptsPrefix = []byte("receipts-block-") oldBlockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] + + ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error ) // encodeBlockNumber encodes a block number as big endian uint64 @@ -600,7 +604,7 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) { } // WriteChainConfig writes the chain config settings to the database. -func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error { +func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *params.ChainConfig) error { // short circuit and ignore if nil config. GetChainConfig // will return a default. if cfg == nil { @@ -616,13 +620,13 @@ func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) err } // GetChainConfig will fetch the network settings based on the given hash. -func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) { +func GetChainConfig(db ethdb.Database, hash common.Hash) (*params.ChainConfig, error) { jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...)) if len(jsonChainConfig) == 0 { return nil, ChainConfigNotFoundErr } - var config ChainConfig + var config params.ChainConfig if err := json.Unmarshal(jsonChainConfig, &config); err != nil { return nil, err } @@ -632,24 +636,24 @@ func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) { // FindCommonAncestor returns the last common ancestor of two block headers func FindCommonAncestor(db ethdb.Database, a, b *types.Header) *types.Header { - for a.GetNumberU64() > b.GetNumberU64() { - a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1) + for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { + a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1) if a == nil { return nil } } - for a.GetNumberU64() < b.GetNumberU64() { - b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1) + for an := a.Number.Uint64(); an < b.Number.Uint64(); { + b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1) if b == nil { return nil } } for a.Hash() != b.Hash() { - a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1) + a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1) if a == nil { return nil } - b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1) + b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1) if b == nil { return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go b/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go index 6ffe04ee8..cbb5a0fc8 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go +++ b/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go @@ -19,3 +19,5 @@ package core // defaultGenesisBlock is a gzip compressed dump of the official default Ethereum // genesis block. const defaultGenesisBlock = "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" + +const defaultTestnetGenesisBlock = "QlpoOTFBWSZTWezl5v8AGI59gHRQABBcBH/0FGQ/795qUAN+PcGHAoQMVIwm0zCSp+ihiAANB6gqp+mmp6eMmqo0AADRpoABgAGjTQBk0BoMgNAbSqNAyDQyaBoNAyAGBUkhNNNA0JGmgD0g0AekxJtVIgTtRupd52vqSSSVfG2ALQBhhdbCIgmAgUiIJnEQTviQF8kkN1IOKiRVVFVSVXHT1cHLHdiSQBlAAG2JtCtNgCS5IEB6dNtP782Z729Tbt3Z6+uFr7u3w79widyYSMAhtlL5XeGZWdwEBNdFm+Hh5gVRBoslgksAMkUKmYlSJRTxBsQAAKKlnd3WjPWrNd66NR7WAu6AQL40vqlny1We+p3l1QThy4gRd2lSVjEKISLDFrVJ1UtjPEPctVSZGjBTJWRzpdgRmCCprJWGe3DK9wvz4eGvdv1ZZcMvxGgdakeMAVEeFywVQWLWCrUWg60Y9ZLoAuZ0uSJJUELb8N2e7j05d3Tlz7vRwgk+LisedJALLVUgFjLGzfv5SEGPLlhhjpx2aaO8T87whcYDmgPLVDBI3RF1TLnzvymWtN732u93GiAGiBdm5s1q9Haru7+yCLJAk3wBDTtrGeMVh3iJmXHo+OBAsqoDOZwjKM3d3mZmcHdBJiSVouSKVkDg+L4vSzNRqs7u6NEaIiUKoYxERGfTWIhoiHwQFESGzta21qu1qtZ3cAXQshVAUybTLbWttWh3iHLoDJEgkACRVCzbeX774Z6558+mfDTdz058efTpDbXai+tddbQBbn/YA9fwxy8/Pq3HFW7deL8W447duuyI2RGqIAAA9SOqHeXzbRtGiXeHiYBMggAAIyQh/f5vW96Xvvu7b7777yMURRHFE6EkEnxRIiOlY2ijs7RV4gYIB0R4LmgB49xg0Oz0bVr2a95u4AZE6rTjl2w58Cntu1ZV6e3Lw3ne2RegHw7R58NdbtVlJEkuqRV2WKa/hpbPIx3bIyX43aNWmezrCBry2ePzkA8reSwB/xdyRThQkOzl5v8=" diff --git a/vendor/github.com/ethereum/go-ethereum/core/events.go b/vendor/github.com/ethereum/go-ethereum/core/events.go index bed8c7b90..322bcb769 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/events.go +++ b/vendor/github.com/ethereum/go-ethereum/core/events.go @@ -38,9 +38,6 @@ type PendingLogsEvent struct { // PendingStateEvent is posted pre mining and notifies of pending state changes. type PendingStateEvent struct{} -// NewBlockEvent is posted when a block has been imported. -type NewBlockEvent struct{ Block *types.Block } - // NewMinedBlockEvent is posted when a block has been imported. type NewMinedBlockEvent struct{ Block *types.Block } diff --git a/vendor/github.com/ethereum/go-ethereum/core/execution.go b/vendor/github.com/ethereum/go-ethereum/core/execution.go index 82143443c..e3ea1006c 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/execution.go +++ b/vendor/github.com/ethereum/go-ethereum/core/execution.go @@ -27,77 +27,33 @@ import ( // 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) { - ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) - 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) { - callerAddr := caller.Address() - ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) - return ret, 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) { - callerAddr := caller.Address() - originAddr := env.Origin() - callerValue := caller.Value() - ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue) - 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) { - ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value) - // Here we get an error if we run into maximum stack depth, - // See: https://github.com/ethereum/yellowpaper/pull/131 - // and YP definitions for CREATE instruction - if err != nil { - return nil, address, err - } - return ret, address, err -} - -func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { - evm := env.Vm() // 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 + return nil, 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())) + return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) } - var createAccount bool - if address == nil { - // Create a new account on the state - nonce := env.Db().GetNonce(caller.Address()) - env.Db().SetNonce(caller.Address(), nonce+1) - addr = crypto.CreateAddress(caller.Address(), nonce) - address = &addr - createAccount = true - } - - snapshotPreTransfer := env.MakeSnapshot() + snapshotPreTransfer := env.SnapshotDatabase() var ( from = env.Db().GetAccount(caller.Address()) to vm.Account ) - if createAccount { - to = env.Db().CreateAccount(*address) - } else { - if !env.Db().Exist(*address) { - to = env.Db().CreateAccount(*address) - } else { - to = env.Db().GetAccount(*address) + 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) @@ -105,19 +61,106 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // EVM. The contract is a scoped environment for this execution context // only. contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(codeAddr, code) + contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) defer contract.Finalise() - ret, err = evm.Run(contract, input) + 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 && createAccount { + if err == nil && !maxCodeSizeExceeded { dataGas := big.NewInt(int64(len(ret))) dataGas.Mul(dataGas, params.CreateDataGas) if contract.UseGas(dataGas) { - env.Db().SetCode(*address, ret) + env.Db().SetCode(addr, ret) } else { err = vm.CodeStoreOutOfGasError } @@ -126,46 +169,45 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // 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 && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { + if maxCodeSizeExceeded || + (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) { contract.UseGas(contract.Gas) + env.RevertToSnapshot(snapshotPreTransfer) - env.SetSnapshot(snapshotPreTransfer) + // Nothing should be returned when an error is thrown. + return nil, addr, err } return ret, addr, err } -func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { - evm := env.Vm() +// 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, common.Address{}, vm.DepthError + return nil, vm.DepthError } - snapshot := env.MakeSnapshot() - - var to vm.Account - if !env.Db().Exist(*toAddr) { - to = env.Db().CreateAccount(*toAddr) - } else { - to = env.Db().GetAccount(*toAddr) - } + 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, value, gas, gasPrice).AsDelegate() - contract.SetCallCode(codeAddr, code) + 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 = evm.Run(contract, input) + ret, err = env.Vm().Run(contract, input) if err != nil { contract.UseGas(contract.Gas) - env.SetSnapshot(snapshot) + env.RevertToSnapshot(snapshot) } - return ret, addr, err + return ret, err } // generic transfer method diff --git a/vendor/github.com/ethereum/go-ethereum/core/genesis.go b/vendor/github.com/ethereum/go-ethereum/core/genesis.go index 637b63320..895b24a82 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/genesis.go +++ b/vendor/github.com/ethereum/go-ethereum/core/genesis.go @@ -17,6 +17,7 @@ package core import ( + "compress/bzip2" "compress/gzip" "encoding/base64" "encoding/json" @@ -43,7 +44,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, } var genesis struct { - ChainConfig *ChainConfig `json:"config"` + ChainConfig *params.ChainConfig `json:"config"` Nonce string Timestamp string ParentHash string @@ -73,7 +74,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) } } - root, stateBatch := statedb.CommitBatch() + root, stateBatch := statedb.CommitBatch(false) difficulty := common.String2Big(genesis.Difficulty) block := types.NewBlock(&types.Header{ @@ -128,7 +129,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big statedb, _ := state.New(common.Hash{}, db) obj := statedb.GetOrNewStateObject(addr) obj.SetBalance(balance) - root, err := statedb.Commit() + root, err := statedb.Commit(false) if err != nil { panic(fmt.Sprintf("cannot write state: %v", err)) } @@ -174,7 +175,7 @@ func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { // WriteTestNetGenesisBlock assembles the Morden test network genesis block and // writes it - along with all associated state - into a chain database. func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { - return WriteGenesisBlock(chainDb, strings.NewReader(TestNetGenesisBlock())) + return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock())) } // WriteOlympicGenesisBlock assembles the Olympic genesis block and writes it @@ -197,6 +198,15 @@ func DefaultGenesisBlock() string { return string(blob) } +func DefaultTestnetGenesisBlock() string { + reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock))) + blob, err := ioutil.ReadAll(reader) + if err != nil { + panic(fmt.Sprintf("failed to load default genesis: %v", err)) + } + return string(blob) +} + // OlympicGenesisBlock assembles a JSON string representing the Olympic genesis // block. func OlympicGenesisBlock() string { @@ -220,25 +230,3 @@ func OlympicGenesisBlock() string { } }`, types.EncodeNonce(42), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes()) } - -// TestNetGenesisBlock assembles a JSON string representing the Morden test net -// genenis block. -func TestNetGenesisBlock() string { - return fmt.Sprintf(`{ - "nonce": "0x%x", - "difficulty": "0x20000", - "mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578", - "coinbase": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2FEFD8", - "alloc": { - "0000000000000000000000000000000000000001": { "balance": "1" }, - "0000000000000000000000000000000000000002": { "balance": "1" }, - "0000000000000000000000000000000000000003": { "balance": "1" }, - "0000000000000000000000000000000000000004": { "balance": "1" }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } - } - }`, types.EncodeNonce(0x6d6f7264656e)) -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/headerchain.go b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go index 0f9dd7208..c53694571 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/headerchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go @@ -18,6 +18,7 @@ package core import ( crand "crypto/rand" + "fmt" "math" "math/big" mrand "math/rand" @@ -31,6 +32,7 @@ import ( "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" "github.com/ethereum/go-ethereum/pow" "github.com/hashicorp/golang-lru" ) @@ -47,7 +49,7 @@ const ( // It is not thread safe either, the encapsulating chain structures should do // the necessary mutex locking/unlocking. type HeaderChain struct { - config *ChainConfig + config *params.ChainConfig chainDb ethdb.Database genesisHeader *types.Header @@ -72,7 +74,7 @@ type getHeaderValidatorFn func() HeaderValidator // getValidator should return the parent's validator // procInterrupt points to the parent's interrupt semaphore // wg points to the parent's shutdown wait group -func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { +func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { headerCache, _ := lru.New(headerCacheLimit) tdCache, _ := lru.New(tdCacheLimit) numberCache, _ := lru.New(numberCacheLimit) @@ -321,8 +323,12 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, w } // Report some public statistics so the user has a clue what's going on first, last := chain[0], chain[len(chain)-1] - glog.V(logger.Info).Infof("imported %d header(s) (%d ignored) in %v. #%v [%x… / %x…]", stats.processed, stats.ignored, - time.Since(start), last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) + + ignored := "" + if stats.ignored > 0 { + ignored = fmt.Sprintf(" (%d ignored)", stats.ignored) + } + glog.V(logger.Info).Infof("imported %d headers%s in %9v. #%v [%x… / %x…]", stats.processed, ignored, common.PrettyDuration(time.Since(start)), last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) return 0, nil } @@ -485,13 +491,13 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) { // // headerValidator implements HeaderValidator. type headerValidator struct { - config *ChainConfig + config *params.ChainConfig hc *HeaderChain // Canonical header chain Pow pow.PoW // Proof of work used for validating } // NewBlockValidator returns a new block validator which is safe for re-use -func NewHeaderValidator(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { +func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { return &headerValidator{ config: config, Pow: pow, diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/dump.go b/vendor/github.com/ethereum/go-ethereum/core/state/dump.go index a328b0537..8294d61b9 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/dump.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/dump.go @@ -21,9 +21,10 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" ) -type Account struct { +type DumpAccount struct { Balance string `json:"balance"` Nonce uint64 `json:"nonce"` Root string `json:"root"` @@ -32,40 +33,41 @@ type Account struct { Storage map[string]string `json:"storage"` } -type World struct { - Root string `json:"root"` - Accounts map[string]Account `json:"accounts"` +type Dump struct { + Root string `json:"root"` + Accounts map[string]DumpAccount `json:"accounts"` } -func (self *StateDB) RawDump() World { - world := World{ +func (self *StateDB) RawDump() Dump { + dump := Dump{ Root: common.Bytes2Hex(self.trie.Root()), - Accounts: make(map[string]Account), + Accounts: make(map[string]DumpAccount), } it := self.trie.Iterator() for it.Next() { addr := self.trie.GetKey(it.Key) - stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value) - if err != nil { + var data Account + if err := rlp.DecodeBytes(it.Value, &data); err != nil { panic(err) } - account := Account{ - Balance: stateObject.balance.String(), - Nonce: stateObject.nonce, - Root: common.Bytes2Hex(stateObject.Root()), - CodeHash: common.Bytes2Hex(stateObject.codeHash), - Code: common.Bytes2Hex(stateObject.Code()), + obj := newObject(nil, common.BytesToAddress(addr), data, nil) + account := DumpAccount{ + Balance: data.Balance.String(), + Nonce: data.Nonce, + Root: common.Bytes2Hex(data.Root[:]), + CodeHash: common.Bytes2Hex(data.CodeHash), + Code: common.Bytes2Hex(obj.Code(self.db)), Storage: make(map[string]string), } - storageIt := stateObject.trie.Iterator() + storageIt := obj.getTrie(self.db).Iterator() for storageIt.Next() { account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) } - world.Accounts[common.Bytes2Hex(addr)] = account + dump.Accounts[common.Bytes2Hex(addr)] = account } - return world + return dump } func (self *StateDB) Dump() []byte { @@ -76,12 +78,3 @@ func (self *StateDB) Dump() []byte { return json } - -// Debug stuff -func (self *StateObject) CreateOutputForDiff() { - fmt.Printf("%x %x %x %x\n", self.Address(), self.Root(), self.balance.Bytes(), self.nonce) - it := self.trie.Iterator() - for it.Next() { - fmt.Printf("%x %x\n", it.Key, it.Value) - } -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go b/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go index 9d8a69b7c..14265b277 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go @@ -76,7 +76,7 @@ func (it *NodeIterator) step() error { } // Initialize the iterator if we've just started if it.stateIt == nil { - it.stateIt = trie.NewNodeIterator(it.state.trie.Trie) + it.stateIt = it.state.trie.NodeIterator() } // If we had data nodes previously, we surely have at least state nodes if it.dataIt != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/journal.go b/vendor/github.com/ethereum/go-ethereum/core/state/journal.go new file mode 100644 index 000000000..d1e73e7d0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/journal.go @@ -0,0 +1,129 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +type journalEntry interface { + undo(*StateDB) +} + +type journal []journalEntry + +type ( + // Changes to the account trie. + createObjectChange struct { + account *common.Address + } + resetObjectChange struct { + prev *StateObject + } + suicideChange struct { + account *common.Address + prev bool // whether account had already suicided + prevbalance *big.Int + } + + // Changes to individual accounts. + balanceChange struct { + account *common.Address + prev *big.Int + } + nonceChange struct { + account *common.Address + prev uint64 + } + storageChange struct { + account *common.Address + key, prevalue common.Hash + } + codeChange struct { + account *common.Address + prevcode, prevhash []byte + } + + // Changes to other state values. + refundChange struct { + prev *big.Int + } + addLogChange struct { + txhash common.Hash + } + touchChange struct { + account *common.Address + prev bool + } +) + +func (ch createObjectChange) undo(s *StateDB) { + delete(s.stateObjects, *ch.account) + delete(s.stateObjectsDirty, *ch.account) +} + +func (ch resetObjectChange) undo(s *StateDB) { + s.setStateObject(ch.prev) +} + +func (ch suicideChange) undo(s *StateDB) { + obj := s.GetStateObject(*ch.account) + if obj != nil { + obj.suicided = ch.prev + obj.setBalance(ch.prevbalance) + } +} + +var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") + +func (ch touchChange) undo(s *StateDB) { + if !ch.prev && *ch.account != ripemd { + delete(s.stateObjects, *ch.account) + delete(s.stateObjectsDirty, *ch.account) + } +} + +func (ch balanceChange) undo(s *StateDB) { + s.GetStateObject(*ch.account).setBalance(ch.prev) +} + +func (ch nonceChange) undo(s *StateDB) { + s.GetStateObject(*ch.account).setNonce(ch.prev) +} + +func (ch codeChange) undo(s *StateDB) { + s.GetStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) +} + +func (ch storageChange) undo(s *StateDB) { + s.GetStateObject(*ch.account).setState(ch.key, ch.prevalue) +} + +func (ch refundChange) undo(s *StateDB) { + s.refund = ch.prev +} + +func (ch addLogChange) undo(s *StateDB) { + logs := s.logs[ch.txhash] + if len(logs) == 1 { + delete(s.logs, ch.txhash) + } else { + s.logs[ch.txhash] = logs[:len(logs)-1] + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go index f8e2f2b87..ad73dc0dc 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go @@ -33,14 +33,14 @@ type ManagedState struct { mu sync.RWMutex - accounts map[string]*account + accounts map[common.Address]*account } // ManagedState returns a new managed state with the statedb as it's backing layer func ManageState(statedb *StateDB) *ManagedState { return &ManagedState{ StateDB: statedb.Copy(), - accounts: make(map[string]*account), + accounts: make(map[common.Address]*account), } } @@ -103,7 +103,7 @@ func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) { so := ms.GetOrNewStateObject(addr) so.SetNonce(nonce) - ms.accounts[addr.Str()] = newAccount(so) + ms.accounts[addr] = newAccount(so) } // HasAccount returns whether the given address is managed or not @@ -114,29 +114,28 @@ func (ms *ManagedState) HasAccount(addr common.Address) bool { } func (ms *ManagedState) hasAccount(addr common.Address) bool { - _, ok := ms.accounts[addr.Str()] + _, ok := ms.accounts[addr] return ok } // populate the managed state func (ms *ManagedState) getAccount(addr common.Address) *account { - straddr := addr.Str() - if account, ok := ms.accounts[straddr]; !ok { + if account, ok := ms.accounts[addr]; !ok { so := ms.GetOrNewStateObject(addr) - ms.accounts[straddr] = newAccount(so) + ms.accounts[addr] = newAccount(so) } else { // Always make sure the state account nonce isn't actually higher // than the tracked one. so := ms.StateDB.GetStateObject(addr) - if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce { - ms.accounts[straddr] = newAccount(so) + if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() { + ms.accounts[addr] = newAccount(so) } } - return ms.accounts[straddr] + return ms.accounts[addr] } func newAccount(so *StateObject) *account { - return &account{so, so.nonce, nil} + return &account{so, so.Nonce(), nil} } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go index 769c63d42..d40b42d83 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go @@ -57,143 +57,248 @@ func (self Storage) Copy() Storage { return cpy } +// StateObject represents an Ethereum account which is being modified. +// +// The usage pattern is as follows: +// First you need to obtain a state object. +// Account values can be accessed and modified through the object. +// Finally, call CommitTrie to write the modified storage trie into a database. type StateObject struct { - db trie.Database // State database for storing state changes - trie *trie.SecureTrie + address common.Address // Ethereum address of this account + data Account + db *StateDB - // Address belonging to this account - address common.Address - // The balance of the account - balance *big.Int - // The nonce of the account - nonce uint64 - // The code hash if code is present (i.e. a contract) - codeHash []byte - // The code for this account - code Code - // Temporarily initialisation code - initCode Code - // Cached storage (flushed when updated) - storage Storage + // DB error. + // State objects are used by the consensus core and VM which are + // unable to deal with database-level errors. Any error that occurs + // during a database read is memoized here and will eventually be returned + // by StateDB.Commit. + dbErr error - // Mark for deletion - // When an object is marked for deletion it will be delete from the trie - // during the "update" phase of the state transition - remove bool - deleted bool - dirty bool + // Write caches. + trie *trie.SecureTrie // storage trie, which becomes non-nil on first access + code Code // contract bytecode, which gets set when code is loaded + + cachedStorage Storage // Storage entry cache to avoid duplicate reads + dirtyStorage Storage // Storage entries that need to be flushed to disk + + // Cache flags. + // When an object is marked suicided it will be delete from the trie + // during the "update" phase of the state transition. + dirtyCode bool // true if the code was updated + suicided bool + touched bool + deleted bool + onDirty func(addr common.Address) // Callback method to mark a state object newly dirty } -func NewStateObject(address common.Address, db trie.Database) *StateObject { - object := &StateObject{ - db: db, - address: address, - balance: new(big.Int), - dirty: true, - codeHash: emptyCodeHash, - storage: make(Storage), +// empty returns whether the account is considered empty. +func (s *StateObject) empty() bool { + return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) +} + +// Account is the Ethereum consensus representation of accounts. +// These objects are stored in the main account trie. +type Account struct { + Nonce uint64 + Balance *big.Int + Root common.Hash // merkle root of the storage trie + CodeHash []byte +} + +// newObject creates a state object. +func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *StateObject { + if data.Balance == nil { + data.Balance = new(big.Int) } - object.trie, _ = trie.NewSecure(common.Hash{}, db) - return object + if data.CodeHash == nil { + data.CodeHash = emptyCodeHash + } + return &StateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty} } -func (self *StateObject) MarkForDeletion() { - self.remove = true - self.dirty = true +// EncodeRLP implements rlp.Encoder. +func (c *StateObject) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, c.data) +} +// setError remembers the first non-nil error it is called with. +func (self *StateObject) setError(err error) { + if self.dbErr == nil { + self.dbErr = err + } +} + +func (self *StateObject) markSuicided() { + self.suicided = true + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } if glog.V(logger.Core) { - glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance) + glog.Infof("%x: #%d %v X\n", self.Address(), self.Nonce(), self.Balance()) } } -func (c *StateObject) getAddr(addr common.Hash) common.Hash { - var ret []byte - rlp.DecodeBytes(c.trie.Get(addr[:]), &ret) - return common.BytesToHash(ret) -} - -func (c *StateObject) setAddr(addr, value common.Hash) { - v, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) - if err != nil { - // if RLPing failed we better panic and not fail silently. This would be considered a consensus issue - panic(err) +func (c *StateObject) touch() { + c.db.journal = append(c.db.journal, touchChange{ + account: &c.address, + prev: c.touched, + }) + if c.onDirty != nil { + c.onDirty(c.Address()) + c.onDirty = nil } - c.trie.Update(addr[:], v) + c.touched = true } -func (self *StateObject) Storage() Storage { - return self.storage -} - -func (self *StateObject) GetState(key common.Hash) common.Hash { - value, exists := self.storage[key] - if !exists { - value = self.getAddr(key) - if (value != common.Hash{}) { - self.storage[key] = value +func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie { + if c.trie == nil { + var err error + c.trie, err = trie.NewSecure(c.data.Root, db, 0) + if err != nil { + c.trie, _ = trie.NewSecure(common.Hash{}, db, 0) + c.setError(fmt.Errorf("can't create storage trie: %v", err)) } } + return c.trie +} +// GetState returns a value in account storage. +func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash { + value, exists := self.cachedStorage[key] + if exists { + return value + } + // Load from DB in case it is missing. + if enc := self.getTrie(db).Get(key[:]); len(enc) > 0 { + _, content, _, err := rlp.Split(enc) + if err != nil { + self.setError(err) + } + value.SetBytes(content) + } + if (value != common.Hash{}) { + self.cachedStorage[key] = value + } return value } -func (self *StateObject) SetState(key, value common.Hash) { - self.storage[key] = value - self.dirty = true +// SetState updates a value in account storage. +func (self *StateObject) SetState(db trie.Database, key, value common.Hash) { + self.db.journal = append(self.db.journal, storageChange{ + account: &self.address, + key: key, + prevalue: self.GetState(db, key), + }) + self.setState(key, value) } -// Update updates the current cached storage to the trie -func (self *StateObject) Update() { - for key, value := range self.storage { +func (self *StateObject) setState(key, value common.Hash) { + self.cachedStorage[key] = value + self.dirtyStorage[key] = value + + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } +} + +// updateTrie writes cached storage modifications into the object's storage trie. +func (self *StateObject) updateTrie(db trie.Database) { + tr := self.getTrie(db) + for key, value := range self.dirtyStorage { + delete(self.dirtyStorage, key) if (value == common.Hash{}) { - self.trie.Delete(key[:]) + tr.Delete(key[:]) continue } - self.setAddr(key, value) + // Encoding []byte cannot fail, ok to ignore the error. + v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) + tr.Update(key[:], v) } } +// UpdateRoot sets the trie root to the current root hash of +func (self *StateObject) updateRoot(db trie.Database) { + self.updateTrie(db) + self.data.Root = self.trie.Hash() +} + +// CommitTrie the storage trie of the object to dwb. +// This updates the trie root. +func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error { + self.updateTrie(db) + if self.dbErr != nil { + return self.dbErr + } + root, err := self.trie.CommitTo(dbw) + if err == nil { + self.data.Root = root + } + return err +} + +// AddBalance removes amount from c's balance. +// It is used to add funds to the destination account of a transfer. func (c *StateObject) AddBalance(amount *big.Int) { - c.SetBalance(new(big.Int).Add(c.balance, amount)) + // EIP158: We must check emptiness for the objects such that the account + // clearing (0,0,0 objects) can take effect. + if amount.Cmp(common.Big0) == 0 { + if c.empty() { + c.touch() + } + + return + } + c.SetBalance(new(big.Int).Add(c.Balance(), amount)) if glog.V(logger.Core) { - glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount) + glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce(), c.Balance(), amount) } } +// SubBalance removes amount from c's balance. +// It is used to remove funds from the origin account of a transfer. func (c *StateObject) SubBalance(amount *big.Int) { - c.SetBalance(new(big.Int).Sub(c.balance, amount)) + if amount.Cmp(common.Big0) == 0 { + return + } + c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) if glog.V(logger.Core) { - glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) + glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.Nonce(), c.Balance(), amount) } } -func (c *StateObject) SetBalance(amount *big.Int) { - c.balance = amount - c.dirty = true +func (self *StateObject) SetBalance(amount *big.Int) { + self.db.journal = append(self.db.journal, balanceChange{ + account: &self.address, + prev: new(big.Int).Set(self.data.Balance), + }) + self.setBalance(amount) } -func (c *StateObject) St() Storage { - return c.storage +func (self *StateObject) setBalance(amount *big.Int) { + self.data.Balance = amount + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } } // Return the gas back to the origin. Used by the Virtual machine or Closures func (c *StateObject) ReturnGas(gas, price *big.Int) {} -func (self *StateObject) Copy() *StateObject { - stateObject := NewStateObject(self.Address(), self.db) - stateObject.balance.Set(self.balance) - stateObject.codeHash = common.CopyBytes(self.codeHash) - stateObject.nonce = self.nonce +func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject { + stateObject := newObject(db, self.address, self.data, onDirty) stateObject.trie = self.trie - stateObject.code = common.CopyBytes(self.code) - stateObject.initCode = common.CopyBytes(self.initCode) - stateObject.storage = self.storage.Copy() - stateObject.remove = self.remove - stateObject.dirty = self.dirty + stateObject.code = self.code + stateObject.dirtyStorage = self.dirtyStorage.Copy() + stateObject.cachedStorage = self.dirtyStorage.Copy() + stateObject.suicided = self.suicided + stateObject.dirtyCode = self.dirtyCode stateObject.deleted = self.deleted - return stateObject } @@ -201,40 +306,73 @@ func (self *StateObject) Copy() *StateObject { // Attribute accessors // -func (self *StateObject) Balance() *big.Int { - return self.balance -} - // Returns the address of the contract/account func (c *StateObject) Address() common.Address { return c.address } -func (self *StateObject) Trie() *trie.SecureTrie { - return self.trie -} - -func (self *StateObject) Root() []byte { - return self.trie.Root() -} - -func (self *StateObject) Code() []byte { - return self.code -} - -func (self *StateObject) SetCode(code []byte) { +// Code returns the contract code associated with this object, if any. +func (self *StateObject) Code(db trie.Database) []byte { + if self.code != nil { + return self.code + } + if bytes.Equal(self.CodeHash(), emptyCodeHash) { + return nil + } + code, err := db.Get(self.CodeHash()) + if err != nil { + self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err)) + } self.code = code - self.codeHash = crypto.Keccak256(code) - self.dirty = true + return code +} + +func (self *StateObject) SetCode(codeHash common.Hash, code []byte) { + prevcode := self.Code(self.db.db) + self.db.journal = append(self.db.journal, codeChange{ + account: &self.address, + prevhash: self.CodeHash(), + prevcode: prevcode, + }) + self.setCode(codeHash, code) +} + +func (self *StateObject) setCode(codeHash common.Hash, code []byte) { + self.code = code + self.data.CodeHash = codeHash[:] + self.dirtyCode = true + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } } func (self *StateObject) SetNonce(nonce uint64) { - self.nonce = nonce - self.dirty = true + self.db.journal = append(self.db.journal, nonceChange{ + account: &self.address, + prev: self.data.Nonce, + }) + self.setNonce(nonce) +} + +func (self *StateObject) setNonce(nonce uint64) { + self.data.Nonce = nonce + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } +} + +func (self *StateObject) CodeHash() []byte { + return self.data.CodeHash +} + +func (self *StateObject) Balance() *big.Int { + return self.data.Balance } func (self *StateObject) Nonce() uint64 { - return self.nonce + return self.data.Nonce } // Never called, but must be present to allow StateObject to be used @@ -246,52 +384,16 @@ func (self *StateObject) Value() *big.Int { func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) { // When iterating over the storage check the cache first - for h, value := range self.storage { + for h, value := range self.cachedStorage { cb(h, value) } - it := self.trie.Iterator() + it := self.getTrie(self.db.db).Iterator() for it.Next() { // ignore cached values key := common.BytesToHash(self.trie.GetKey(it.Key)) - if _, ok := self.storage[key]; !ok { + if _, ok := self.cachedStorage[key]; !ok { cb(key, common.BytesToHash(it.Value)) } } } - -type extStateObject struct { - Nonce uint64 - Balance *big.Int - Root common.Hash - CodeHash []byte -} - -// EncodeRLP implements rlp.Encoder. -func (c *StateObject) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{c.nonce, c.balance, c.Root(), c.codeHash}) -} - -// DecodeObject decodes an RLP-encoded state object. -func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) { - var ( - obj = &StateObject{address: address, db: db, storage: make(Storage)} - ext extStateObject - err error - ) - if err = rlp.DecodeBytes(data, &ext); err != nil { - return nil, err - } - if obj.trie, err = trie.NewSecure(ext.Root, db); err != nil { - return nil, err - } - if !bytes.Equal(ext.CodeHash, emptyCodeHash) { - if obj.code, err = db.Get(ext.CodeHash); err != nil { - return nil, fmt.Errorf("can't get code for hash %x: %v", ext.CodeHash, err) - } - } - obj.nonce = ext.Nonce - obj.balance = ext.Balance - obj.codeHash = ext.CodeHash - return obj, nil -} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go index 3e25e0c16..3742c178b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go @@ -20,19 +20,36 @@ package state import ( "fmt" "math/big" + "sort" + "sync" "github.com/ethereum/go-ethereum/common" "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/rlp" "github.com/ethereum/go-ethereum/trie" + lru "github.com/hashicorp/golang-lru" ) -// The starting nonce determines the default nonce when new accounts are being -// created. -var StartingNonce uint64 +// Trie cache generation limit after which to evic trie nodes from memory. +var MaxTrieCacheGen = uint16(120) + +const ( + // Number of past tries to keep. This value is chosen such that + // reasonable chain reorg depths will hit an existing trie. + maxPastTries = 12 + + // Number of codehash->size associations to keep. + codeSizeCacheSize = 100000 +) + +type revision struct { + id int + journalIndex int +} // StateDBs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing @@ -40,54 +57,116 @@ var StartingNonce uint64 // * Contracts // * Accounts type StateDB struct { - db ethdb.Database - trie *trie.SecureTrie + db ethdb.Database + trie *trie.SecureTrie + pastTries []*trie.SecureTrie + codeSizeCache *lru.Cache - stateObjects map[string]*StateObject + // This map holds 'live' objects, which will get modified while processing a state transition. + stateObjects map[common.Address]*StateObject + stateObjectsDirty map[common.Address]struct{} + // The refund counter, also used by state transitioning. refund *big.Int thash, bhash common.Hash txIndex int logs map[common.Hash]vm.Logs logSize uint + + // Journal of state modifications. This is the backbone of + // Snapshot and RevertToSnapshot. + journal journal + validRevisions []revision + nextRevisionId int + + lock sync.Mutex } // Create a new state from a given trie func New(root common.Hash, db ethdb.Database) (*StateDB, error) { - tr, err := trie.NewSecure(root, db) + tr, err := trie.NewSecure(root, db, MaxTrieCacheGen) + if err != nil { + return nil, err + } + csc, _ := lru.New(codeSizeCacheSize) + return &StateDB{ + db: db, + trie: tr, + codeSizeCache: csc, + stateObjects: make(map[common.Address]*StateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash]vm.Logs), + }, nil +} + +// New creates a new statedb by reusing any journalled tries to avoid costly +// disk io. +func (self *StateDB) New(root common.Hash) (*StateDB, error) { + self.lock.Lock() + defer self.lock.Unlock() + + tr, err := self.openTrie(root) if err != nil { return nil, err } return &StateDB{ - db: db, - trie: tr, - stateObjects: make(map[string]*StateObject), - refund: new(big.Int), - logs: make(map[common.Hash]vm.Logs), + db: self.db, + trie: tr, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*StateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash]vm.Logs), }, nil } // Reset clears out all emphemeral state objects from the state db, but keeps // the underlying state trie to avoid reloading data for the next operations. func (self *StateDB) Reset(root common.Hash) error { - var ( - err error - tr = self.trie - ) - if self.trie.Hash() != root { - if tr, err = trie.NewSecure(root, self.db); err != nil { - return err + self.lock.Lock() + defer self.lock.Unlock() + + tr, err := self.openTrie(root) + if err != nil { + return err + } + self.trie = tr + self.stateObjects = make(map[common.Address]*StateObject) + self.stateObjectsDirty = make(map[common.Address]struct{}) + self.thash = common.Hash{} + self.bhash = common.Hash{} + self.txIndex = 0 + self.logs = make(map[common.Hash]vm.Logs) + self.logSize = 0 + self.clearJournalAndRefund() + + return nil +} + +// openTrie creates a trie. It uses an existing trie if one is available +// from the journal if available. +func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) { + for i := len(self.pastTries) - 1; i >= 0; i-- { + if self.pastTries[i].Hash() == root { + tr := *self.pastTries[i] + return &tr, nil } } - *self = StateDB{ - db: self.db, - trie: tr, - stateObjects: make(map[string]*StateObject), - refund: new(big.Int), - logs: make(map[common.Hash]vm.Logs), + return trie.NewSecure(root, self.db, MaxTrieCacheGen) +} + +func (self *StateDB) pushTrie(t *trie.SecureTrie) { + self.lock.Lock() + defer self.lock.Unlock() + + if len(self.pastTries) >= maxPastTries { + copy(self.pastTries, self.pastTries[1:]) + self.pastTries[len(self.pastTries)-1] = t + } else { + self.pastTries = append(self.pastTries, t) } - return nil } func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { @@ -97,6 +176,8 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { } func (self *StateDB) AddLog(log *vm.Log) { + self.journal = append(self.journal, addLogChange{txhash: self.thash}) + log.TxHash = self.thash log.BlockHash = self.bhash log.TxIndex = uint(self.txIndex) @@ -118,15 +199,21 @@ func (self *StateDB) Logs() vm.Logs { } func (self *StateDB) AddRefund(gas *big.Int) { + self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)}) self.refund.Add(self.refund, gas) } -func (self *StateDB) HasAccount(addr common.Address) bool { +// Exist reports whether the given account address exists in the state. +// Notably this also returns true for suicided accounts. +func (self *StateDB) Exist(addr common.Address) bool { return self.GetStateObject(addr) != nil } -func (self *StateDB) Exist(addr common.Address) bool { - return self.GetStateObject(addr) != nil +// Empty returns whether the state object is either non-existant +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (self *StateDB) Empty(addr common.Address) bool { + so := self.GetStateObject(addr) + return so == nil || so.empty() } func (self *StateDB) GetAccount(addr common.Address) vm.Account { @@ -137,43 +224,67 @@ func (self *StateDB) GetAccount(addr common.Address) vm.Account { func (self *StateDB) GetBalance(addr common.Address) *big.Int { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.balance + return stateObject.Balance() } - return common.Big0 } func (self *StateDB) GetNonce(addr common.Address) uint64 { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.nonce + return stateObject.Nonce() } - return StartingNonce + return 0 } func (self *StateDB) GetCode(addr common.Address) []byte { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.code + code := stateObject.Code(self.db) + key := common.BytesToHash(stateObject.CodeHash()) + self.codeSizeCache.Add(key, len(code)) + return code } - return nil } +func (self *StateDB) GetCodeSize(addr common.Address) int { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + return 0 + } + key := common.BytesToHash(stateObject.CodeHash()) + if cached, ok := self.codeSizeCache.Get(key); ok { + return cached.(int) + } + size := len(stateObject.Code(self.db)) + if stateObject.dbErr == nil { + self.codeSizeCache.Add(key, size) + } + return size +} + +func (self *StateDB) GetCodeHash(addr common.Address) common.Hash { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + return common.Hash{} + } + return common.BytesToHash(stateObject.CodeHash()) +} + func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash { stateObject := self.GetStateObject(a) if stateObject != nil { - return stateObject.GetState(b) + return stateObject.GetState(self.db, b) } - return common.Hash{} } -func (self *StateDB) IsDeleted(addr common.Address) bool { +func (self *StateDB) HasSuicided(addr common.Address) bool { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.remove + return stateObject.suicided } return false } @@ -189,6 +300,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { } } +func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetBalance(amount) + } +} + func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { @@ -199,35 +317,43 @@ func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { func (self *StateDB) SetCode(addr common.Address, code []byte) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetCode(code) + stateObject.SetCode(crypto.Keccak256Hash(code), code) } } func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetState(key, value) + stateObject.SetState(self.db, key, value) } } -func (self *StateDB) Delete(addr common.Address) bool { +// Suicide marks the given account as suicided. +// This clears the account balance. +// +// The account's state object is still available until the state is committed, +// GetStateObject will return a non-nil account after Suicide. +func (self *StateDB) Suicide(addr common.Address) bool { stateObject := self.GetStateObject(addr) - if stateObject != nil { - stateObject.MarkForDeletion() - stateObject.balance = new(big.Int) - - return true + if stateObject == nil { + return false } - - return false + self.journal = append(self.journal, suicideChange{ + account: &addr, + prev: stateObject.suicided, + prevbalance: new(big.Int).Set(stateObject.Balance()), + }) + stateObject.markSuicided() + stateObject.data.Balance = new(big.Int) + return true } // // Setting, updating & deleting state object methods // -// Update the given state object and apply it to state trie -func (self *StateDB) UpdateStateObject(stateObject *StateObject) { +// updateStateObject writes the given object to the trie. +func (self *StateDB) updateStateObject(stateObject *StateObject) { addr := stateObject.Address() data, err := rlp.EncodeToBytes(stateObject) if err != nil { @@ -236,117 +362,156 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) { self.trie.Update(addr[:], data) } -// Delete the given state object and delete it from the state trie -func (self *StateDB) DeleteStateObject(stateObject *StateObject) { +// deleteStateObject removes the given object from the state trie. +func (self *StateDB) deleteStateObject(stateObject *StateObject) { stateObject.deleted = true - addr := stateObject.Address() self.trie.Delete(addr[:]) - //delete(self.stateObjects, addr.Str()) } -// Retrieve a state object given my the address. Nil if not found +// Retrieve a state object given my the address. Returns nil if not found. func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) { - stateObject = self.stateObjects[addr.Str()] - if stateObject != nil { - if stateObject.deleted { - stateObject = nil + // Prefer 'live' objects. + if obj := self.stateObjects[addr]; obj != nil { + if obj.deleted { + return nil } - - return stateObject + return obj } - data := self.trie.Get(addr[:]) - if len(data) == 0 { + // Load the object from the database. + enc := self.trie.Get(addr[:]) + if len(enc) == 0 { return nil } - stateObject, err := DecodeObject(addr, self.db, data) - if err != nil { + var data Account + if err := rlp.DecodeBytes(enc, &data); err != nil { glog.Errorf("can't decode object at %x: %v", addr[:], err) return nil } - self.SetStateObject(stateObject) - return stateObject + // Insert into the live set. + obj := newObject(self, addr, data, self.MarkStateObjectDirty) + self.setStateObject(obj) + return obj } -func (self *StateDB) SetStateObject(object *StateObject) { - self.stateObjects[object.Address().Str()] = object +func (self *StateDB) setStateObject(object *StateObject) { + self.stateObjects[object.Address()] = object } // Retrieve a state object or create a new state object if nil func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { stateObject := self.GetStateObject(addr) if stateObject == nil || stateObject.deleted { - stateObject = self.CreateStateObject(addr) + stateObject, _ = self.createObject(addr) } - return stateObject } -// NewStateObject create a state object whether it exist in the trie or not -func (self *StateDB) newStateObject(addr common.Address) *StateObject { - if glog.V(logger.Core) { - glog.Infof("(+) %x\n", addr) - } - - stateObject := NewStateObject(addr, self.db) - stateObject.SetNonce(StartingNonce) - self.stateObjects[addr.Str()] = stateObject - - return stateObject +// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly +// state object cache iteration to find a handful of modified ones. +func (self *StateDB) MarkStateObjectDirty(addr common.Address) { + self.stateObjectsDirty[addr] = struct{}{} } -// Creates creates a new state object and takes ownership. This is different from "NewStateObject" -func (self *StateDB) CreateStateObject(addr common.Address) *StateObject { - // Get previous (if any) - so := self.GetStateObject(addr) - // Create a new one - newSo := self.newStateObject(addr) - - // If it existed set the balance to the new account - if so != nil { - newSo.balance = so.balance +// createObject creates a new state object. If there is an existing account with +// the given address, it is overwritten and returned as the second return value. +func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) { + prev = self.GetStateObject(addr) + newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty) + newobj.setNonce(0) // sets the object to dirty + if prev == nil { + if glog.V(logger.Core) { + glog.Infof("(+) %x\n", addr) + } + self.journal = append(self.journal, createObjectChange{account: &addr}) + } else { + self.journal = append(self.journal, resetObjectChange{prev: prev}) } - - return newSo + self.setStateObject(newobj) + return newobj, prev } +// CreateAccount explicitly creates a state object. If a state object with the address +// already exists the balance is carried over to the new account. +// +// CreateAccount is called during the EVM CREATE operation. The situation might arise that +// a contract does the following: +// +// 1. sends funds to sha(account ++ (nonce + 1)) +// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) +// +// Carrying over the balance ensures that Ether doesn't disappear. func (self *StateDB) CreateAccount(addr common.Address) vm.Account { - return self.CreateStateObject(addr) + new, prev := self.createObject(addr) + if prev != nil { + new.setBalance(prev.data.Balance) + } + return new } -// -// Setting, copying of the state methods -// - +// Copy creates a deep, independent copy of the state. +// Snapshots of the copied state cannot be applied to the copy. func (self *StateDB) Copy() *StateDB { - // ignore error - we assume state-to-be-copied always exists - state, _ := New(common.Hash{}, self.db) - state.trie = self.trie - for k, stateObject := range self.stateObjects { - state.stateObjects[k] = stateObject.Copy() + self.lock.Lock() + defer self.lock.Unlock() + + // Copy all the basic fields, initialize the memory ones + state := &StateDB{ + db: self.db, + trie: self.trie, + pastTries: self.pastTries, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), + refund: new(big.Int).Set(self.refund), + logs: make(map[common.Hash]vm.Logs, len(self.logs)), + logSize: self.logSize, + } + // Copy the dirty states and logs + for addr, _ := range self.stateObjectsDirty { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) + state.stateObjectsDirty[addr] = struct{}{} } - - state.refund.Set(self.refund) - for hash, logs := range self.logs { state.logs[hash] = make(vm.Logs, len(logs)) copy(state.logs[hash], logs) } - state.logSize = self.logSize - return state } -func (self *StateDB) Set(state *StateDB) { - self.trie = state.trie - self.stateObjects = state.stateObjects - - self.refund = state.refund - self.logs = state.logs - self.logSize = state.logSize +// Snapshot returns an identifier for the current revision of the state. +func (self *StateDB) Snapshot() int { + id := self.nextRevisionId + self.nextRevisionId++ + self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)}) + return id } +// RevertToSnapshot reverts all state changes made since the given revision. +func (self *StateDB) RevertToSnapshot(revid int) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(self.validRevisions), func(i int) bool { + return self.validRevisions[i].id >= revid + }) + if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid { + panic(fmt.Errorf("revision id %v cannot be reverted", revid)) + } + snapshot := self.validRevisions[idx].journalIndex + + // Replay the journal to undo changes. + for i := len(self.journal) - 1; i >= snapshot; i-- { + self.journal[i].undo(self) + } + self.journal = self.journal[:snapshot] + + // Remove invalidated snapshots from the stack. + self.validRevisions = self.validRevisions[:idx] +} + +// GetRefund returns the current value of the refund counter. +// The return value must not be modified by the caller and will become +// invalid at the next call to AddRefund. func (self *StateDB) GetRefund() *big.Int { return self.refund } @@ -354,19 +519,18 @@ func (self *StateDB) GetRefund() *big.Int { // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. -func (s *StateDB) IntermediateRoot() common.Hash { - s.refund = new(big.Int) - for _, stateObject := range s.stateObjects { - if stateObject.dirty { - if stateObject.remove { - s.DeleteStateObject(stateObject) - } else { - stateObject.Update() - s.UpdateStateObject(stateObject) - } - stateObject.dirty = false +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { + for addr, _ := range s.stateObjectsDirty { + stateObject := s.stateObjects[addr] + if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { + s.deleteStateObject(stateObject) + } else { + stateObject.updateRoot(s.db) + s.updateStateObject(stateObject) } } + // Invalidate journal because reverting across transactions is not allowed. + s.clearJournalAndRefund() return s.trie.Hash() } @@ -376,76 +540,76 @@ func (s *StateDB) IntermediateRoot() common.Hash { // DeleteSuicides should not be used for consensus related updates // under any circumstances. func (s *StateDB) DeleteSuicides() { - // Reset refund so that any used-gas calculations can use - // this method. - s.refund = new(big.Int) - for _, stateObject := range s.stateObjects { - if stateObject.dirty { - // If the object has been removed by a suicide - // flag the object as deleted. - if stateObject.remove { - stateObject.deleted = true - } - stateObject.dirty = false + // Reset refund so that any used-gas calculations can use this method. + s.clearJournalAndRefund() + + for addr, _ := range s.stateObjectsDirty { + stateObject := s.stateObjects[addr] + + // If the object has been removed by a suicide + // flag the object as deleted. + if stateObject.suicided { + stateObject.deleted = true } + delete(s.stateObjectsDirty, addr) } } // Commit commits all state changes to the database. -func (s *StateDB) Commit() (root common.Hash, err error) { - root, batch := s.CommitBatch() +func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { + root, batch := s.CommitBatch(deleteEmptyObjects) return root, batch.Write() } // CommitBatch commits all state changes to a write batch but does not // execute the batch. It is used to validate state changes against // the root hash stored in a block. -func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) { +func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) { batch = s.db.NewBatch() - root, _ = s.commit(batch) + root, _ = s.commit(batch, deleteEmptyObjects) + + glog.V(logger.Debug).Infof("Trie cache stats: %d misses, %d unloads", trie.CacheMisses(), trie.CacheUnloads()) return root, batch } -func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) { +func (s *StateDB) clearJournalAndRefund() { + s.journal = nil + s.validRevisions = s.validRevisions[:0] s.refund = new(big.Int) +} - for _, stateObject := range s.stateObjects { - if stateObject.remove { +func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { + defer s.clearJournalAndRefund() + + // Commit objects to the trie. + for addr, stateObject := range s.stateObjects { + _, isDirty := s.stateObjectsDirty[addr] + switch { + case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): // If the object has been removed, don't bother syncing it // and just mark it for deletion in the trie. - s.DeleteStateObject(stateObject) - } else { + s.deleteStateObject(stateObject) + case isDirty: // Write any contract code associated with the state object - if len(stateObject.code) > 0 { - if err := db.Put(stateObject.codeHash, stateObject.code); err != nil { + if stateObject.code != nil && stateObject.dirtyCode { + if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil { return common.Hash{}, err } + stateObject.dirtyCode = false } - // Write any storage changes in the state object to its trie. - stateObject.Update() - - // Commit the trie of the object to the batch. - // This updates the trie root internally, so - // getting the root hash of the storage trie - // through UpdateStateObject is fast. - if _, err := stateObject.trie.CommitTo(db); err != nil { + // Write any storage changes in the state object to its storage trie. + if err := stateObject.CommitTrie(s.db, dbw); err != nil { return common.Hash{}, err } - // Update the object in the account trie. - s.UpdateStateObject(stateObject) + // Update the object in the main account trie. + s.updateStateObject(stateObject) } - stateObject.dirty = false + delete(s.stateObjectsDirty, addr) } - return s.trie.CommitTo(db) -} - -func (self *StateDB) Refunds() *big.Int { - return self.refund -} - -// Debug stuff -func (self *StateDB) CreateOutputForDiff() { - for _, stateObject := range self.stateObjects { - stateObject.CreateOutputForDiff() + // Write trie changes. + root, err = s.trie.CommitTo(dbw) + if err == nil { + s.pushTrie(s.trie) } + return root, err } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/sync.go b/vendor/github.com/ethereum/go-ethereum/core/state/sync.go index ef2b4b84c..bab9c8e7e 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/sync.go @@ -59,8 +59,10 @@ func (s *StateSync) Missing(max int) []common.Hash { return (*trie.TrieSync)(s).Missing(max) } -// Process injects a batch of retrieved trie nodes data. -func (s *StateSync) Process(list []trie.SyncResult) (int, error) { +// Process injects a batch of retrieved trie nodes data, returning if something +// was committed to the database and also the index of an entry if processing of +// it failed. +func (s *StateSync) Process(list []trie.SyncResult) (bool, int, error) { return (*trie.TrieSync)(s).Process(list) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go index fd8e9762e..e346917c3 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" ) var ( @@ -37,12 +38,12 @@ var ( // // StateProcessor implements Processor. type StateProcessor struct { - config *ChainConfig + config *params.ChainConfig bc *BlockChain } // NewStateProcessor initialises a new StateProcessor. -func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor { +func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor { return &StateProcessor{ config: config, bc: bc, @@ -71,10 +72,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { + //fmt.Println("tx:", i) statedb.StartRecord(tx.Hash(), block.Hash(), i) receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) if err != nil { - return nil, nil, totalUsedGas, err + return nil, nil, nil, err } receipts = append(receipts, receipt) allLogs = append(allLogs, logs...) @@ -89,20 +91,24 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // // ApplyTransactions returns the generated receipts and vm logs during the // execution of the state transition phase. -func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { - _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) +func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) + if err != nil { + return nil, nil, nil, err + } + + _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp) if err != nil { return nil, nil, nil, err } // Update the state with pending changes usedGas.Add(usedGas, gas) - receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) + receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) - if MessageCreatesContract(tx) { - from, _ := tx.From() - receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) + if MessageCreatesContract(msg) { + receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce()) } logs := statedb.GetLogs(tx.Hash()) diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go index 9e6b2f567..8abe17b0a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go @@ -62,8 +62,8 @@ type StateTransition struct { // Message represents a message sent to a contract. type Message interface { - From() (common.Address, error) - FromFrontier() (common.Address, error) + From() common.Address + //FromFrontier() (common.Address, error) To() *common.Address GasPrice() *big.Int @@ -134,23 +134,12 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In return ret, gasUsed, err } -func (self *StateTransition) from() (vm.Account, error) { - var ( - f common.Address - err error - ) - if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) { - f, err = self.msg.From() - } else { - f, err = self.msg.FromFrontier() - } - if err != nil { - return nil, err - } +func (self *StateTransition) from() vm.Account { + f := self.msg.From() if !self.state.Exist(f) { - return self.state.CreateAccount(f), nil + return self.state.CreateAccount(f) } - return self.state.GetAccount(f), nil + return self.state.GetAccount(f) } func (self *StateTransition) to() vm.Account { @@ -185,14 +174,11 @@ func (self *StateTransition) buyGas() error { mgas := self.msg.Gas() mgval := new(big.Int).Mul(mgas, self.gasPrice) - sender, err := self.from() - if err != nil { - return err - } + sender := self.from() if sender.Balance().Cmp(mgval) < 0 { return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance()) } - if err = self.gp.SubGas(mgas); err != nil { + if err := self.gp.SubGas(mgas); err != nil { return err } self.addGas(mgas) @@ -203,10 +189,7 @@ func (self *StateTransition) buyGas() error { func (self *StateTransition) preCheck() (err error) { msg := self.msg - sender, err := self.from() - if err != nil { - return err - } + sender := self.from() // Make sure this transaction's nonce is correct if msg.CheckNonce() { @@ -232,9 +215,9 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b return } msg := self.msg - sender, _ := self.from() // err checked in preCheck + sender := self.from() // err checked in preCheck - homestead := self.env.RuleSet().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 { @@ -282,7 +265,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b func (self *StateTransition) refundGas() { // Return eth for remaining gas to the sender account, // exchanged at the original rate. - sender, _ := self.from() // err already checked + sender := self.from() // err already checked remaining := new(big.Int).Mul(self.gas, self.gasPrice) sender.AddBalance(remaining) diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_list.go b/vendor/github.com/ethereum/go-ethereum/core/tx_list.go new file mode 100644 index 000000000..c3ddf3148 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_list.go @@ -0,0 +1,342 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "container/heap" + "math" + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/core/types" +) + +// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for +// retrieving sorted transactions from the possibly gapped future queue. +type nonceHeap []uint64 + +func (h nonceHeap) Len() int { return len(h) } +func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } +func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *nonceHeap) Push(x interface{}) { + *h = append(*h, x.(uint64)) +} + +func (h *nonceHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +// txSortedMap is a nonce->transaction hash map with a heap based index to allow +// iterating over the contents in a nonce-incrementing way. +type txSortedMap struct { + items map[uint64]*types.Transaction // Hash map storing the transaction data + index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) + cache types.Transactions // Cache of the transactions already sorted +} + +// newTxSortedMap creates a new sorted transaction map. +func newTxSortedMap() *txSortedMap { + return &txSortedMap{ + items: make(map[uint64]*types.Transaction), + index: &nonceHeap{}, + } +} + +// Get retrieves the current transactions associated with the given nonce. +func (m *txSortedMap) Get(nonce uint64) *types.Transaction { + return m.items[nonce] +} + +// Put inserts a new transaction into the map, also updating the map's nonce +// index. If a transaction already exists with the same nonce, it's overwritten. +func (m *txSortedMap) Put(tx *types.Transaction) { + nonce := tx.Nonce() + if m.items[nonce] == nil { + heap.Push(m.index, nonce) + } + m.items[nonce], m.cache = tx, nil +} + +// Forward removes all transactions from the map with a nonce lower than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. +func (m *txSortedMap) Forward(threshold uint64) types.Transactions { + var removed types.Transactions + + // Pop off heap items until the threshold is reached + for m.index.Len() > 0 && (*m.index)[0] < threshold { + nonce := heap.Pop(m.index).(uint64) + removed = append(removed, m.items[nonce]) + delete(m.items, nonce) + } + // If we had a cached order, shift the front + if m.cache != nil { + m.cache = m.cache[len(removed):] + } + return removed +} + +// Filter iterates over the list of transactions and removes all of them for which +// the specified function evaluates to true. +func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { + var removed types.Transactions + + // Collect all the transactions to filter out + for nonce, tx := range m.items { + if filter(tx) { + removed = append(removed, tx) + delete(m.items, nonce) + } + } + // If transactions were removed, the heap and cache are ruined + if len(removed) > 0 { + *m.index = make([]uint64, 0, len(m.items)) + for nonce, _ := range m.items { + *m.index = append(*m.index, nonce) + } + heap.Init(m.index) + + m.cache = nil + } + return removed +} + +// Cap places a hard limit on the number of items, returning all transactions +// exceeding that limit. +func (m *txSortedMap) Cap(threshold int) types.Transactions { + // Short circuit if the number of items is under the limit + if len(m.items) <= threshold { + return nil + } + // Otherwise gather and drop the highest nonce'd transactions + var drops types.Transactions + + sort.Sort(*m.index) + for size := len(m.items); size > threshold; size-- { + drops = append(drops, m.items[(*m.index)[size-1]]) + delete(m.items, (*m.index)[size-1]) + } + *m.index = (*m.index)[:threshold] + heap.Init(m.index) + + // If we had a cache, shift the back + if m.cache != nil { + m.cache = m.cache[:len(m.cache)-len(drops)] + } + return drops +} + +// Remove deletes a transaction from the maintained map, returning whether the +// transaction was found. +func (m *txSortedMap) Remove(nonce uint64) bool { + // Short circuit if no transaction is present + _, ok := m.items[nonce] + if !ok { + return false + } + // Otherwise delete the transaction and fix the heap index + for i := 0; i < m.index.Len(); i++ { + if (*m.index)[i] == nonce { + heap.Remove(m.index, i) + break + } + } + delete(m.items, nonce) + m.cache = nil + + return true +} + +// Ready retrieves a sequentially increasing list of transactions starting at the +// provided nonce that is ready for processing. The returned transactions will be +// removed from the list. +// +// Note, all transactions with nonces lower than start will also be returned to +// prevent getting into and invalid state. This is not something that should ever +// happen but better to be self correcting than failing! +func (m *txSortedMap) Ready(start uint64) types.Transactions { + // Short circuit if no transactions are available + if m.index.Len() == 0 || (*m.index)[0] > start { + return nil + } + // Otherwise start accumulating incremental transactions + var ready types.Transactions + for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { + ready = append(ready, m.items[next]) + delete(m.items, next) + heap.Pop(m.index) + } + m.cache = nil + + return ready +} + +// Len returns the length of the transaction map. +func (m *txSortedMap) Len() int { + return len(m.items) +} + +// Flatten creates a nonce-sorted slice of transactions based on the loosely +// sorted internal representation. The result of the sorting is cached in case +// it's requested again before any modifications are made to the contents. +func (m *txSortedMap) Flatten() types.Transactions { + // If the sorting was not cached yet, create and cache it + if m.cache == nil { + m.cache = make(types.Transactions, 0, len(m.items)) + for _, tx := range m.items { + m.cache = append(m.cache, tx) + } + sort.Sort(types.TxByNonce(m.cache)) + } + // Copy the cache to prevent accidental modifications + txs := make(types.Transactions, len(m.cache)) + copy(txs, m.cache) + return txs +} + +// txList is a "list" of transactions belonging to an account, sorted by account +// nonce. The same type can be used both for storing contiguous transactions for +// the executable/pending queue; and for storing gapped transactions for the non- +// executable/future queue, with minor behavoiral changes. +type txList struct { + strict bool // Whether nonces are strictly continuous or not + txs *txSortedMap // Heap indexed sorted hash map of the transactions + costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) +} + +// newTxList create a new transaction list for maintaining nonce-indexable fast, +// gapped, sortable transaction lists. +func newTxList(strict bool) *txList { + return &txList{ + strict: strict, + txs: newTxSortedMap(), + costcap: new(big.Int), + } +} + +// Add tries to insert a new transaction into the list, returning whether the +// transaction was accepted, and if yes, any previous transaction it replaced. +// +// If the new transaction is accepted into the list, the lists' cost threshold +// is also potentially updated. +func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) { + // If there's an older better transaction, abort + old := l.txs.Get(tx.Nonce()) + if old != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 { + return false, nil + } + // Otherwise overwrite the old transaction with the current one + l.txs.Put(tx) + if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { + l.costcap = cost + } + return true, old +} + +// Forward removes all transactions from the list with a nonce lower than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. +func (l *txList) Forward(threshold uint64) types.Transactions { + return l.txs.Forward(threshold) +} + +// Filter removes all transactions from the list with a cost higher than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. Strict-mode invalidated transactions are also returned. +// +// This method uses the cached costcap to quickly decide if there's even a point +// in calculating all the costs or if the balance covers all. If the threshold is +// lower than the costcap, the costcap will be reset to a new high after removing +// expensive the too transactions. +func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transactions) { + // If all transactions are below the threshold, short circuit + if l.costcap.Cmp(threshold) <= 0 { + return nil, nil + } + l.costcap = new(big.Int).Set(threshold) // Lower the cap to the threshold + + // Filter out all the transactions above the account's funds + removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(threshold) > 0 }) + + // If the list was strict, filter anything above the lowest nonce + var invalids types.Transactions + if l.strict && len(removed) > 0 { + lowest := uint64(math.MaxUint64) + for _, tx := range removed { + if nonce := tx.Nonce(); lowest > nonce { + lowest = nonce + } + } + invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) + } + return removed, invalids +} + +// Cap places a hard limit on the number of items, returning all transactions +// exceeding that limit. +func (l *txList) Cap(threshold int) types.Transactions { + return l.txs.Cap(threshold) +} + +// Remove deletes a transaction from the maintained list, returning whether the +// transaction was found, and also returning any transaction invalidated due to +// the deletion (strict mode only). +func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { + // Remove the transaction from the set + nonce := tx.Nonce() + if removed := l.txs.Remove(nonce); !removed { + return false, nil + } + // In strict mode, filter out non-executable transactions + if l.strict { + return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce }) + } + return true, nil +} + +// Ready retrieves a sequentially increasing list of transactions starting at the +// provided nonce that is ready for processing. The returned transactions will be +// removed from the list. +// +// Note, all transactions with nonces lower than start will also be returned to +// prevent getting into and invalid state. This is not something that should ever +// happen but better to be self correcting than failing! +func (l *txList) Ready(start uint64) types.Transactions { + return l.txs.Ready(start) +} + +// Len returns the length of the transaction list. +func (l *txList) Len() int { + return l.txs.Len() +} + +// Empty returns whether the list of transactions is empty or not. +func (l *txList) Empty() bool { + return l.Len() == 0 +} + +// Flatten creates a nonce-sorted slice of transactions based on the loosely +// sorted internal representation. The result of the sorting is cached in case +// it's requested again before any modifications are made to the contents. +func (l *txList) Flatten() types.Transactions { + return l.txs.Flatten() +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go index 596356377..edcbc21eb 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go @@ -30,6 +30,9 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" + "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) var ( @@ -45,8 +48,30 @@ var ( ErrNegativeValue = errors.New("Negative value") ) -const ( - maxQueued = 64 // max limit of queued txs per address +var ( + minPendingPerAccount = uint64(16) // Min number of guaranteed transaction slots per address + maxPendingTotal = uint64(4096) // Max limit of pending transactions from all accounts (soft) + maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address + maxQueuedInTotal = uint64(1024) // Max limit of queued transactions from all accounts + maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued + evictionInterval = time.Minute // Time interval to check for evictable transactions +) + +var ( + // Metrics for the pending pool + pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard") + pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace") + pendingRLCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting + pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds + + // Metrics for the queued pool + queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard") + queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace") + queuedRLCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting + queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds + + // General tx metrics + invalidTxCounter = metrics.NewCounter("txpool/invalid") ) type stateFn func() (*state.StateDB, error) @@ -59,7 +84,7 @@ type stateFn func() (*state.StateDB, error) // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { - config *ChainConfig + config *params.ChainConfig currentState stateFn // The state function which will allow us to do some pre checks pendingState *state.ManagedState gasLimit func() *big.Int // The current gas limit function callback @@ -67,20 +92,28 @@ type TxPool struct { eventMux *event.TypeMux events event.Subscription localTx *txSet + signer types.Signer mu sync.RWMutex - pending map[common.Hash]*types.Transaction // processable transactions - queue map[common.Address]map[common.Hash]*types.Transaction - wg sync.WaitGroup // for shutdown sync + pending map[common.Address]*txList // All currently processable transactions + queue map[common.Address]*txList // Queued but non-processable transactions + all map[common.Hash]*types.Transaction // All transactions to allow lookups + beats map[common.Address]time.Time // Last heartbeat from each known account + + wg sync.WaitGroup // for shutdown sync + quit chan struct{} homestead bool } -func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { pool := &TxPool{ config: config, - pending: make(map[common.Hash]*types.Transaction), - queue: make(map[common.Address]map[common.Hash]*types.Transaction), + signer: types.NewEIP155Signer(config.ChainId), + pending: make(map[common.Address]*txList), + queue: make(map[common.Address]*txList), + all: make(map[common.Hash]*types.Transaction), + beats: make(map[common.Address]time.Time), eventMux: eventMux, currentState: currentStateFn, gasLimit: gasLimitFn, @@ -88,10 +121,12 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat pendingState: nil, localTx: newTxSet(), events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}), + quit: make(chan struct{}), } - pool.wg.Add(1) + pool.wg.Add(2) go pool.eventLoop() + go pool.expirationLoop() return pool } @@ -106,8 +141,10 @@ func (pool *TxPool) eventLoop() { switch ev := ev.Data.(type) { case ChainHeadEvent: pool.mu.Lock() - if ev.Block != nil && pool.config.IsHomestead(ev.Block.Number()) { - pool.homestead = true + if ev.Block != nil { + if pool.config.IsHomestead(ev.Block.Number()) { + pool.homestead = true + } } pool.resetState() @@ -117,7 +154,7 @@ func (pool *TxPool) eventLoop() { pool.minGasPrice = ev.Price pool.mu.Unlock() case RemovedTransactionEvent: - pool.AddTransactions(ev.Txs) + pool.AddBatch(ev.Txs) } } } @@ -125,12 +162,12 @@ func (pool *TxPool) eventLoop() { func (pool *TxPool) resetState() { currentState, err := pool.currentState() if err != nil { - glog.V(logger.Info).Infoln("failed to get current state: %v", err) + glog.V(logger.Error).Infof("Failed to get current state: %v", err) return } managedState := state.ManageState(currentState) if err != nil { - glog.V(logger.Info).Infoln("failed to get managed state: %v", err) + glog.V(logger.Error).Infof("Failed to get managed state: %v", err) return } pool.pendingState = managedState @@ -139,26 +176,21 @@ func (pool *TxPool) resetState() { // any transactions that have been included in the block or // have been invalidated because of another transaction (e.g. // higher gas price) - pool.validatePool() + pool.demoteUnexecutables() - // Loop over the pending transactions and base the nonce of the new - // pending transaction set. - for _, tx := range pool.pending { - if addr, err := tx.From(); err == nil { - // Set the nonce. Transaction nonce can never be lower - // than the state nonce; validatePool took care of that. - if pool.pendingState.GetNonce(addr) <= tx.Nonce() { - pool.pendingState.SetNonce(addr, tx.Nonce()+1) - } - } + // Update all accounts to the latest known pending nonce + for addr, list := range pool.pending { + txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway + pool.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1) } // Check the queue and move transactions over to the pending if possible // or remove those that have become invalid - pool.checkQueue() + pool.promoteExecutables() } func (pool *TxPool) Stop() { pool.events.Unsubscribe() + close(pool.quit) pool.wg.Wait() glog.V(logger.Info).Infoln("Transaction pool stopped") } @@ -170,47 +202,58 @@ func (pool *TxPool) State() *state.ManagedState { return pool.pendingState } +// Stats retrieves the current pool stats, namely the number of pending and the +// number of queued (non-executable) transactions. func (pool *TxPool) Stats() (pending int, queued int) { pool.mu.RLock() defer pool.mu.RUnlock() - pending = len(pool.pending) - for _, txs := range pool.queue { - queued += len(txs) + for _, list := range pool.pending { + pending += list.Len() + } + for _, list := range pool.queue { + queued += list.Len() } return } // Content retrieves the data content of the transaction pool, returning all the -// pending as well as queued transactions, grouped by account and nonce. -func (pool *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +// pending as well as queued transactions, grouped by account and sorted by nonce. +func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { pool.mu.RLock() defer pool.mu.RUnlock() - // Retrieve all the pending transactions and sort by account and by nonce - pending := make(map[common.Address]map[uint64][]*types.Transaction) - for _, tx := range pool.pending { - account, _ := tx.From() - - owned, ok := pending[account] - if !ok { - owned = make(map[uint64][]*types.Transaction) - pending[account] = owned - } - owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + pending := make(map[common.Address]types.Transactions) + for addr, list := range pool.pending { + pending[addr] = list.Flatten() } - // Retrieve all the queued transactions and sort by account and by nonce - queued := make(map[common.Address]map[uint64][]*types.Transaction) - for account, txs := range pool.queue { - owned := make(map[uint64][]*types.Transaction) - for _, tx := range txs { - owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) - } - queued[account] = owned + queued := make(map[common.Address]types.Transactions) + for addr, list := range pool.queue { + queued[addr] = list.Flatten() } return pending, queued } +// Pending retrieves all currently processable transactions, groupped by origin +// account and sorted by nonce. The returned transaction set is a copy and can be +// freely modified by calling code. +func (pool *TxPool) Pending() map[common.Address]types.Transactions { + pool.mu.Lock() + defer pool.mu.Unlock() + + // check queue first + pool.promoteExecutables() + + // invalidate any txs + pool.demoteUnexecutables() + + pending := make(map[common.Address]types.Transactions) + for addr, list := range pool.pending { + pending[addr] = list.Flatten() + } + return pending +} + // SetLocal marks a transaction as local, skipping gas price // check against local miner minimum in the future func (pool *TxPool) SetLocal(tx *types.Transaction) { @@ -233,14 +276,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return err } - from, err := tx.From() + from, err := types.Sender(pool.signer, tx) if err != nil { return ErrInvalidSender } // Make sure the account exist. Non existent accounts // haven't got funds and well therefor never pass. - if !currentState.HasAccount(from) { + if !currentState.Exist(from) { return ErrNonExistentAccount } @@ -268,7 +311,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return ErrInsufficientFunds } - intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead) + intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead) if tx.Gas().Cmp(intrGas) < 0 { return ErrIntrinsicGas } @@ -276,312 +319,412 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return nil } -// validate and queue transactions. -func (self *TxPool) add(tx *types.Transaction) error { +// add validates a transaction and inserts it into the non-executable queue for +// later pending promotion and execution. +func (pool *TxPool) add(tx *types.Transaction) error { + // If the transaction is alreayd known, discard it hash := tx.Hash() - - if self.pending[hash] != nil { - return fmt.Errorf("Known transaction (%x)", hash[:4]) + if pool.all[hash] != nil { + return fmt.Errorf("Known transaction: %x", hash[:4]) } - err := self.validateTx(tx) - if err != nil { + // Otherwise ensure basic validation passes and queue it up + if err := pool.validateTx(tx); err != nil { + invalidTxCounter.Inc(1) return err } - self.queueTx(hash, tx) + pool.enqueueTx(hash, tx) + // Print a log message if low enough level is set if glog.V(logger.Debug) { - var toname string + rcpt := "[NEW_CONTRACT]" if to := tx.To(); to != nil { - toname = common.Bytes2Hex(to[:4]) - } else { - toname = "[NEW_CONTRACT]" + rcpt = common.Bytes2Hex(to[:4]) } - // we can ignore the error here because From is - // verified in ValidateTransaction. - f, _ := tx.From() - from := common.Bytes2Hex(f[:4]) - glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) + from, _ := types.Sender(pool.signer, tx) // from already verified during tx validation + glog.Infof("(t) 0x%x => %s (%v) %x\n", from[:4], rcpt, tx.Value, hash) } - return nil } -// queueTx will queue an unknown transaction -func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { - from, _ := tx.From() // already validated - if self.queue[from] == nil { - self.queue[from] = make(map[common.Hash]*types.Transaction) +// enqueueTx inserts a new transaction into the non-executable transaction queue. +// +// Note, this method assumes the pool lock is held! +func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) { + // Try to insert the transaction into the future queue + from, _ := types.Sender(pool.signer, tx) // already validated + if pool.queue[from] == nil { + pool.queue[from] = newTxList(false) } - self.queue[from][hash] = tx + inserted, old := pool.queue[from].Add(tx) + if !inserted { + queuedDiscardCounter.Inc(1) + return // An older transaction was better, discard this + } + // Discard any previous transaction and mark this + if old != nil { + delete(pool.all, old.Hash()) + queuedReplaceCounter.Inc(1) + } + pool.all[hash] = tx } -// addTx will add a transaction to the pending (processable queue) list of transactions -func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { - // init delayed since tx pool could have been started before any state sync +// promoteTx adds a transaction to the pending (processable) list of transactions. +// +// Note, this method assumes the pool lock is held! +func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) { + // Init delayed since tx pool could have been started before any state sync if pool.pendingState == nil { pool.resetState() } - - if _, ok := pool.pending[hash]; !ok { - pool.pending[hash] = tx - - // Increment the nonce on the pending state. This can only happen if - // the nonce is +1 to the previous one. - pool.pendingState.SetNonce(addr, tx.Nonce()+1) - // Notify the subscribers. This event is posted in a goroutine - // because it's possible that somewhere during the post "Remove transaction" - // gets called which will then wait for the global tx pool lock and deadlock. - go pool.eventMux.Post(TxPreEvent{tx}) + // Try to insert the transaction into the pending queue + if pool.pending[addr] == nil { + pool.pending[addr] = newTxList(true) } + list := pool.pending[addr] + + inserted, old := list.Add(tx) + if !inserted { + // An older transaction was better, discard this + delete(pool.all, hash) + pendingDiscardCounter.Inc(1) + return + } + // Otherwise discard any previous transaction and mark this + if old != nil { + delete(pool.all, old.Hash()) + pendingReplaceCounter.Inc(1) + } + pool.all[hash] = tx // Failsafe to work around direct pending inserts (tests) + + // Set the potentially new pending nonce and notify any subsystems of the new tx + pool.beats[addr] = time.Now() + pool.pendingState.SetNonce(addr, tx.Nonce()+1) + go pool.eventMux.Post(TxPreEvent{tx}) } // Add queues a single transaction in the pool if it is valid. -func (self *TxPool) Add(tx *types.Transaction) error { - self.mu.Lock() - defer self.mu.Unlock() - - if err := self.add(tx); err != nil { - return err - } - self.checkQueue() - return nil -} - -// AddTransactions attempts to queue all valid transactions in txs. -func (self *TxPool) AddTransactions(txs []*types.Transaction) { - self.mu.Lock() - defer self.mu.Unlock() - - for _, tx := range txs { - if err := self.add(tx); err != nil { - glog.V(logger.Debug).Infoln("tx error:", err) - } else { - h := tx.Hash() - glog.V(logger.Debug).Infof("tx %x\n", h[:4]) - } - } - - // check and validate the queue - self.checkQueue() -} - -// GetTransaction returns a transaction if it is contained in the pool -// and nil otherwise. -func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { - tp.mu.RLock() - defer tp.mu.RUnlock() - - // check the txs first - if tx, ok := tp.pending[hash]; ok { - return tx - } - // check queue - for _, txs := range tp.queue { - if tx, ok := txs[hash]; ok { - return tx - } - } - return nil -} - -// GetTransactions returns all currently processable transactions. -// The returned slice may be modified by the caller. -func (self *TxPool) GetTransactions() (txs types.Transactions) { - self.mu.Lock() - defer self.mu.Unlock() - - // check queue first - self.checkQueue() - // invalidate any txs - self.validatePool() - - txs = make(types.Transactions, len(self.pending)) - i := 0 - for _, tx := range self.pending { - txs[i] = tx - i++ - } - return txs -} - -// GetQueuedTransactions returns all non-processable transactions. -func (self *TxPool) GetQueuedTransactions() types.Transactions { - self.mu.RLock() - defer self.mu.RUnlock() - - var ret types.Transactions - for _, txs := range self.queue { - for _, tx := range txs { - ret = append(ret, tx) - } - } - sort.Sort(types.TxByNonce(ret)) - return ret -} - -// RemoveTransactions removes all given transactions from the pool. -func (self *TxPool) RemoveTransactions(txs types.Transactions) { - self.mu.Lock() - defer self.mu.Unlock() - for _, tx := range txs { - self.removeTx(tx.Hash()) - } -} - -// RemoveTx removes the transaction with the given hash from the pool. -func (pool *TxPool) RemoveTx(hash common.Hash) { +func (pool *TxPool) Add(tx *types.Transaction) error { pool.mu.Lock() defer pool.mu.Unlock() + + if err := pool.add(tx); err != nil { + return err + } + pool.promoteExecutables() + + return nil +} + +// AddBatch attempts to queue a batch of transactions. +func (pool *TxPool) AddBatch(txs []*types.Transaction) { + pool.mu.Lock() + defer pool.mu.Unlock() + + for _, tx := range txs { + if err := pool.add(tx); err != nil { + glog.V(logger.Debug).Infoln("tx error:", err) + } + } + pool.promoteExecutables() +} + +// Get returns a transaction if it is contained in the pool +// and nil otherwise. +func (pool *TxPool) Get(hash common.Hash) *types.Transaction { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return pool.all[hash] +} + +// Remove removes the transaction with the given hash from the pool. +func (pool *TxPool) Remove(hash common.Hash) { + pool.mu.Lock() + defer pool.mu.Unlock() + pool.removeTx(hash) } +// RemoveBatch removes all given transactions from the pool. +func (pool *TxPool) RemoveBatch(txs types.Transactions) { + pool.mu.Lock() + defer pool.mu.Unlock() + + for _, tx := range txs { + pool.removeTx(tx.Hash()) + } +} + +// removeTx removes a single transaction from the queue, moving all subsequent +// transactions back to the future queue. func (pool *TxPool) removeTx(hash common.Hash) { - // delete from pending pool - delete(pool.pending, hash) - // delete from queue - for address, txs := range pool.queue { - if _, ok := txs[hash]; ok { - if len(txs) == 1 { - // if only one tx, remove entire address entry. - delete(pool.queue, address) + // Fetch the transaction we wish to delete + tx, ok := pool.all[hash] + if !ok { + return + } + addr, _ := types.Sender(pool.signer, tx) // already validated during insertion + + // Remove it from the list of known transactions + delete(pool.all, hash) + + // Remove the transaction from the pending lists and reset the account nonce + if pending := pool.pending[addr]; pending != nil { + if removed, invalids := pending.Remove(tx); removed { + // If no more transactions are left, remove the list + if pending.Empty() { + delete(pool.pending, addr) + delete(pool.beats, addr) } else { - delete(txs, hash) + // Otherwise postpone any invalidated transactions + for _, tx := range invalids { + pool.enqueueTx(tx.Hash(), tx) + } } - break + // Update the account nonce if needed + if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce { + pool.pendingState.SetNonce(addr, tx.Nonce()) + } + } + } + // Transaction is in the future queue + if future := pool.queue[addr]; future != nil { + future.Remove(tx) + if future.Empty() { + delete(pool.queue, addr) } } } -// checkQueue moves transactions that have become processable to main pool. -func (pool *TxPool) checkQueue() { - // init delayed since tx pool could have been started before any state sync +// promoteExecutables moves transactions that have become processable from the +// future queue to the set of pending transactions. During this process, all +// invalidated transactions (low nonce, low balance) are deleted. +func (pool *TxPool) promoteExecutables() { + // Init delayed since tx pool could have been started before any state sync if pool.pendingState == nil { pool.resetState() } - - var promote txQueue - for address, txs := range pool.queue { - currentState, err := pool.currentState() - if err != nil { - glog.Errorf("could not get current state: %v", err) - return + // Retrieve the current state to allow nonce and balance checking + state, err := pool.currentState() + if err != nil { + glog.Errorf("Could not get current state: %v", err) + return + } + // Iterate over all accounts and promote any executable transactions + queued := uint64(0) + for addr, list := range pool.queue { + // Drop all transactions that are deemed too old (low nonce) + for _, tx := range list.Forward(state.GetNonce(addr)) { + if glog.V(logger.Core) { + glog.Infof("Removed old queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) } - balance := currentState.GetBalance(address) + // Drop all transactions that are too costly (low balance) + drops, _ := list.Filter(state.GetBalance(addr)) + for _, tx := range drops { + if glog.V(logger.Core) { + glog.Infof("Removed unpayable queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + queuedNofundsCounter.Inc(1) + } + // Gather all executable transactions and promote them + for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) { + if glog.V(logger.Core) { + glog.Infof("Promoting queued transaction: %v", tx) + } + pool.promoteTx(addr, tx.Hash(), tx) + } + // Drop all transactions over the allowed limit + for _, tx := range list.Cap(int(maxQueuedPerAccount)) { + if glog.V(logger.Core) { + glog.Infof("Removed cap-exceeding queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + queuedRLCounter.Inc(1) + } + queued += uint64(list.Len()) - var ( - guessedNonce = pool.pendingState.GetNonce(address) // nonce currently kept by the tx pool (pending state) - trueNonce = currentState.GetNonce(address) // nonce known by the last state - ) - promote = promote[:0] - for hash, tx := range txs { - // Drop processed or out of fund transactions - if tx.Nonce() < trueNonce || balance.Cmp(tx.Cost()) < 0 { - if glog.V(logger.Core) { - glog.Infof("removed tx (%v) from pool queue: low tx nonce or out of funds\n", tx) + // Delete the entire queue entry if it became empty. + if list.Empty() { + delete(pool.queue, addr) + } + } + // If the pending limit is overflown, start equalizing allowances + pending := uint64(0) + for _, list := range pool.pending { + pending += uint64(list.Len()) + } + if pending > maxPendingTotal { + pendingBeforeCap := pending + // Assemble a spam order to penalize large transactors first + spammers := prque.New() + for addr, list := range pool.pending { + // Only evict transactions from high rollers + if uint64(list.Len()) > minPendingPerAccount { + // Skip local accounts as pools should maintain backlogs for themselves + for _, tx := range list.txs.items { + if !pool.localTx.contains(tx.Hash()) { + spammers.Push(addr, float32(list.Len())) + } + break // Checking on transaction for locality is enough } - delete(txs, hash) + } + } + // Gradually drop transactions from offenders + offenders := []common.Address{} + for pending > maxPendingTotal && !spammers.Empty() { + // Retrieve the next offender if not local address + offender, _ := spammers.Pop() + offenders = append(offenders, offender.(common.Address)) + + // Equalize balances until all the same or below threshold + if len(offenders) > 1 { + // Calculate the equalization threshold for all current offenders + threshold := pool.pending[offender.(common.Address)].Len() + + // Iteratively reduce all offenders until below limit or threshold reached + for pending > maxPendingTotal && pool.pending[offenders[len(offenders)-2]].Len() > threshold { + for i := 0; i < len(offenders)-1; i++ { + list := pool.pending[offenders[i]] + list.Cap(list.Len() - 1) + pending-- + } + } + } + } + // If still above threshold, reduce to limit or min allowance + if pending > maxPendingTotal && len(offenders) > 0 { + for pending > maxPendingTotal && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > minPendingPerAccount { + for _, addr := range offenders { + list := pool.pending[addr] + list.Cap(list.Len() - 1) + pending-- + } + } + } + pendingRLCounter.Inc(int64(pendingBeforeCap - pending)) + } + // If we've queued more transactions than the hard limit, drop oldest ones + if queued > maxQueuedInTotal { + // Sort all accounts with queued transactions by heartbeat + addresses := make(addresssByHeartbeat, 0, len(pool.queue)) + for addr, _ := range pool.queue { + addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]}) + } + sort.Sort(addresses) + + // Drop transactions until the total is below the limit + for drop := queued - maxQueuedInTotal; drop > 0; { + addr := addresses[len(addresses)-1] + list := pool.queue[addr.address] + + addresses = addresses[:len(addresses)-1] + + // Drop all transactions if they are less than the overflow + if size := uint64(list.Len()); size <= drop { + for _, tx := range list.Flatten() { + pool.removeTx(tx.Hash()) + } + drop -= size + queuedRLCounter.Inc(int64(size)) continue } - // Collect the remaining transactions for the next pass. - promote = append(promote, txQueueEntry{hash, address, tx}) - } - // Find the next consecutive nonce range starting at the current account nonce, - // pushing the guessed nonce forward if we add consecutive transactions. - sort.Sort(promote) - for i, entry := range promote { - // If we reached a gap in the nonces, enforce transaction limit and stop - if entry.Nonce() > guessedNonce { - if len(promote)-i > maxQueued { - if glog.V(logger.Debug) { - glog.Infof("Queued tx limit exceeded for %s. Tx %s removed\n", common.PP(address[:]), common.PP(entry.hash[:])) - } - for _, drop := range promote[i+maxQueued:] { - delete(txs, drop.hash) - } - } - break + // Otherwise drop only last few transactions + txs := list.Flatten() + for i := len(txs) - 1; i >= 0 && drop > 0; i-- { + pool.removeTx(txs[i].Hash()) + drop-- + queuedRLCounter.Inc(1) } - // Otherwise promote the transaction and move the guess nonce if needed - pool.addTx(entry.hash, address, entry.Transaction) - delete(txs, entry.hash) - - if entry.Nonce() == guessedNonce { - guessedNonce++ - } - } - // Delete the entire queue entry if it became empty. - if len(txs) == 0 { - delete(pool.queue, address) } } } -// validatePool removes invalid and processed transactions from the main pool. -// If a transaction is removed for being invalid (e.g. out of funds), all sub- -// sequent (Still valid) transactions are moved back into the future queue. This -// is important to prevent a drained account from DOSing the network with non -// executable transactions. -func (pool *TxPool) validatePool() { +// demoteUnexecutables removes invalid and processed transactions from the pools +// executable/pending queue and any subsequent transactions that become unexecutable +// are moved back into the future queue. +func (pool *TxPool) demoteUnexecutables() { + // Retrieve the current state to allow nonce and balance checking state, err := pool.currentState() if err != nil { glog.V(logger.Info).Infoln("failed to get current state: %v", err) return } - balanceCache := make(map[common.Address]*big.Int) + // Iterate over all accounts and demote any non-executable transactions + for addr, list := range pool.pending { + nonce := state.GetNonce(addr) - // Clean up the pending pool, accumulating invalid nonces - gaps := make(map[common.Address]uint64) - - for hash, tx := range pool.pending { - sender, _ := tx.From() // err already checked - - // Perform light nonce and balance validation - balance := balanceCache[sender] - if balance == nil { - balance = state.GetBalance(sender) - balanceCache[sender] = balance - } - if past := state.GetNonce(sender) > tx.Nonce(); past || balance.Cmp(tx.Cost()) < 0 { - // Remove an already past it invalidated transaction + // Drop all transactions that are deemed too old (low nonce) + for _, tx := range list.Forward(nonce) { if glog.V(logger.Core) { - glog.Infof("removed tx (%v) from pool: low tx nonce or out of funds\n", tx) - } - delete(pool.pending, hash) - - // Track the smallest invalid nonce to postpone subsequent transactions - if !past { - if prev, ok := gaps[sender]; !ok || tx.Nonce() < prev { - gaps[sender] = tx.Nonce() - } + glog.Infof("Removed old pending transaction: %v", tx) } + delete(pool.all, tx.Hash()) } - } - // Move all transactions after a gap back to the future queue - if len(gaps) > 0 { - for hash, tx := range pool.pending { - sender, _ := tx.From() - if gap, ok := gaps[sender]; ok && tx.Nonce() >= gap { - if glog.V(logger.Core) { - glog.Infof("postponed tx (%v) due to introduced gap\n", tx) - } - pool.queueTx(hash, tx) - delete(pool.pending, hash) + // Drop all transactions that are too costly (low balance), and queue any invalids back for later + drops, invalids := list.Filter(state.GetBalance(addr)) + for _, tx := range drops { + if glog.V(logger.Core) { + glog.Infof("Removed unpayable pending transaction: %v", tx) } + delete(pool.all, tx.Hash()) + pendingNofundsCounter.Inc(1) + } + for _, tx := range invalids { + if glog.V(logger.Core) { + glog.Infof("Demoting pending transaction: %v", tx) + } + pool.enqueueTx(tx.Hash(), tx) + } + // Delete the entire queue entry if it became empty. + if list.Empty() { + delete(pool.pending, addr) + delete(pool.beats, addr) } } } -type txQueue []txQueueEntry +// expirationLoop is a loop that periodically iterates over all accounts with +// queued transactions and drop all that have been inactive for a prolonged amount +// of time. +func (pool *TxPool) expirationLoop() { + defer pool.wg.Done() -type txQueueEntry struct { - hash common.Hash - addr common.Address - *types.Transaction + evict := time.NewTicker(evictionInterval) + defer evict.Stop() + + for { + select { + case <-evict.C: + pool.mu.Lock() + for addr := range pool.queue { + if time.Since(pool.beats[addr]) > maxQueuedLifetime { + for _, tx := range pool.queue[addr].Flatten() { + pool.removeTx(tx.Hash()) + } + } + } + pool.mu.Unlock() + + case <-pool.quit: + return + } + } } -func (q txQueue) Len() int { return len(q) } -func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } -func (q txQueue) Less(i, j int) bool { return q[i].Nonce() < q[j].Nonce() } +// addressByHeartbeat is an account address tagged with its last activity timestamp. +type addressByHeartbeat struct { + address common.Address + heartbeat time.Time +} + +type addresssByHeartbeat []addressByHeartbeat + +func (a addresssByHeartbeat) Len() int { return len(a) } +func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) } +func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // txSet represents a set of transaction hashes in which entries // are automatically dropped after txSetDuration time diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/block.go b/vendor/github.com/ethereum/go-ethereum/core/types/block.go index 12fcb6cdc..68504ffcc 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/block.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/block.go @@ -18,9 +18,9 @@ package types import ( - "bytes" "encoding/binary" "encoding/json" + "errors" "fmt" "io" "math/big" @@ -33,25 +33,53 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +var ( + EmptyRootHash = DeriveSha(Transactions{}) + EmptyUncleHash = CalcUncleHash(nil) +) + +var ( + errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header") + errMissingHeaderFields = errors.New("missing required JSON block header fields") + errBadNonceSize = errors.New("invalid block nonce size, want 8 bytes") +) + // A BlockNonce is a 64-bit hash which proves (combined with the // mix-hash) that a sufficient amount of computation has been carried // out on a block. type BlockNonce [8]byte +// EncodeNonce converts the given integer to a block nonce. func EncodeNonce(i uint64) BlockNonce { var n BlockNonce binary.BigEndian.PutUint64(n[:], i) return n } +// Uint64 returns the integer value of a block nonce. func (n BlockNonce) Uint64() uint64 { return binary.BigEndian.Uint64(n[:]) } +// MarshalJSON implements json.Marshaler func (n BlockNonce) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"0x%x"`, n)), nil } +// 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 +} + +// Header represents a block header in the Ethereum blockchain. type Header struct { ParentHash common.Hash // Hash to the previous block UncleHash common.Hash // Uncles of this block @@ -70,19 +98,31 @@ type Header struct { Nonce BlockNonce } -func (h *Header) GetNumber() *big.Int { return new(big.Int).Set(h.Number) } -func (h *Header) GetGasLimit() *big.Int { return new(big.Int).Set(h.GasLimit) } -func (h *Header) GetGasUsed() *big.Int { return new(big.Int).Set(h.GasUsed) } -func (h *Header) GetDifficulty() *big.Int { return new(big.Int).Set(h.Difficulty) } -func (h *Header) GetTime() *big.Int { return new(big.Int).Set(h.Time) } -func (h *Header) GetNumberU64() uint64 { return h.Number.Uint64() } -func (h *Header) GetNonce() uint64 { return binary.BigEndian.Uint64(h.Nonce[:]) } -func (h *Header) GetExtra() []byte { return common.CopyBytes(h.Extra) } +type jsonHeader struct { + ParentHash *common.Hash `json:"parentHash"` + UncleHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot"` + 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"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *BlockNonce `json:"nonce"` +} +// Hash returns the block hash of the header, which is simply the keccak256 hash of its +// RLP encoding. func (h *Header) Hash() common.Hash { return rlpHash(h) } +// HashNoNonce returns the hash which is used as input for the proof-of-work search. func (h *Header) HashNoNonce() common.Hash { return rlpHash([]interface{}{ h.ParentHash, @@ -101,48 +141,63 @@ func (h *Header) HashNoNonce() common.Hash { }) } -func (h *Header) UnmarshalJSON(data []byte) error { - var ext struct { - ParentHash string - Coinbase string - Difficulty string - GasLimit string - Time *big.Int - Extra string - } - dec := json.NewDecoder(bytes.NewReader(data)) - if err := dec.Decode(&ext); err != nil { - return err - } - - h.ParentHash = common.HexToHash(ext.ParentHash) - h.Coinbase = common.HexToAddress(ext.Coinbase) - h.Difficulty = common.String2Big(ext.Difficulty) - h.Time = ext.Time - h.Extra = []byte(ext.Extra) - return nil +// MarshalJSON encodes headers into the web3 RPC response block format. +func (h *Header) MarshalJSON() ([]byte, error) { + return json.Marshal(&jsonHeader{ + ParentHash: &h.ParentHash, + UncleHash: &h.UncleHash, + Coinbase: &h.Coinbase, + Root: &h.Root, + 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), + MixDigest: &h.MixDigest, + Nonce: &h.Nonce, + }) } -func (h *Header) MarshalJSON() ([]byte, error) { - fields := map[string]interface{}{ - "hash": h.Hash(), - "parentHash": h.ParentHash, - "number": fmt.Sprintf("%#x", h.Number), - "nonce": h.Nonce, - "receiptRoot": h.ReceiptHash, - "logsBloom": h.Bloom, - "sha3Uncles": h.UncleHash, - "stateRoot": h.Root, - "miner": h.Coinbase, - "difficulty": fmt.Sprintf("%#x", h.Difficulty), - "extraData": fmt.Sprintf("0x%x", h.Extra), - "gasLimit": fmt.Sprintf("%#x", h.GasLimit), - "gasUsed": fmt.Sprintf("%#x", h.GasUsed), - "timestamp": fmt.Sprintf("%#x", h.Time), - "transactionsRoot": h.TxHash, +// UnmarshalJSON decodes headers from the web3 RPC response block format. +func (h *Header) UnmarshalJSON(input []byte) error { + var dec jsonHeader + if err := json.Unmarshal(input, &dec); err != nil { + return err } - - return json.Marshal(fields) + // Ensure that all fields are set. MixDigest is checked separately because + // it is a recent addition to the spec (as of August 2016) and older RPC server + // implementations might not provide it. + if dec.MixDigest == nil { + return errMissingHeaderMixDigest + } + if dec.ParentHash == nil || dec.UncleHash == nil || dec.Coinbase == nil || + dec.Root == nil || dec.TxHash == nil || dec.ReceiptHash == nil || + dec.Bloom == nil || dec.Difficulty == nil || dec.Number == nil || + dec.GasLimit == nil || dec.GasUsed == nil || dec.Time == nil || + dec.Extra == nil || dec.Nonce == nil { + return errMissingHeaderFields + } + // Assign all values. + h.ParentHash = *dec.ParentHash + h.UncleHash = *dec.UncleHash + h.Coinbase = *dec.Coinbase + h.Root = *dec.Root + h.TxHash = *dec.TxHash + h.ReceiptHash = *dec.ReceiptHash + h.Bloom = *dec.Bloom + h.Difficulty = (*big.Int)(dec.Difficulty) + h.Number = (*big.Int)(dec.Number) + h.GasLimit = (*big.Int)(dec.GasLimit) + h.GasUsed = (*big.Int)(dec.GasUsed) + h.Time = (*big.Int)(dec.Time) + h.Extra = *dec.Extra + h.MixDigest = *dec.MixDigest + h.Nonce = *dec.Nonce + return nil } func rlpHash(x interface{}) (h common.Hash) { @@ -159,6 +214,7 @@ type Body struct { Uncles []*Header } +// Block represents an entire block in the Ethereum blockchain. type Block struct { header *Header uncles []*Header @@ -207,11 +263,6 @@ type storageblock struct { TD *big.Int } -var ( - EmptyRootHash = DeriveSha(Transactions{}) - EmptyUncleHash = CalcUncleHash(nil) -) - // NewBlock creates a new block. The input data is copied, // changes to header and to the field values will not affect the // block. @@ -284,23 +335,7 @@ func CopyHeader(h *Header) *Header { return &cpy } -func (b *Block) ValidateFields() error { - if b.header == nil { - return fmt.Errorf("header is nil") - } - for i, transaction := range b.transactions { - if transaction == nil { - return fmt.Errorf("transaction %d is nil", i) - } - } - for i, uncle := range b.uncles { - if uncle == nil { - return fmt.Errorf("uncle %d is nil", i) - } - } - return nil -} - +// DecodeRLP decodes the Ethereum func (b *Block) DecodeRLP(s *rlp.Stream) error { var eb extblock _, size, _ := s.Kind() @@ -312,6 +347,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { return nil } +// EncodeRLP serializes b into the Ethereum RLP block format. func (b *Block) EncodeRLP(w io.Writer) error { return rlp.Encode(w, extblock{ Header: b.header, @@ -331,6 +367,7 @@ func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { } // TODO: copies + func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Transactions() Transactions { return b.transactions } @@ -418,8 +455,8 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { return block } -// Implement pow.Block - +// Hash returns the keccak256 hash of b's header. +// The hash is computed on the first call and cached thereafter. func (b *Block) Hash() common.Hash { if hash := b.hash.Load(); hash != nil { return hash.(common.Hash) diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go index ecf2bffc2..d3945a734 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go @@ -31,28 +31,34 @@ type bytesBacked interface { const bloomLength = 256 +// Bloom represents a 256 bit bloom filter. type Bloom [bloomLength]byte +// BytesToBloom converts a byte slice to a bloom filter. +// It panics if b is not of suitable size. func BytesToBloom(b []byte) Bloom { var bloom Bloom bloom.SetBytes(b) return bloom } +// SetBytes sets the content of b to the given bytes. +// It panics if d is not of suitable size. func (b *Bloom) SetBytes(d []byte) { if len(b) < len(d) { panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d))) } - copy(b[bloomLength-len(d):], d) } +// Add adds d to the filter. Future calls of Test(d) will return true. func (b *Bloom) Add(d *big.Int) { bin := new(big.Int).SetBytes(b[:]) bin.Or(bin, bloom9(d.Bytes())) b.SetBytes(bin.Bytes()) } +// Big converts b to a big integer. func (b Bloom) Big() *big.Int { return common.Bytes2Big(b[:]) } @@ -69,8 +75,22 @@ func (b Bloom) TestBytes(test []byte) bool { return b.Test(common.BytesToBig(test)) } +// MarshalJSON encodes b as a hex string with 0x prefix. func (b Bloom) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%#x"`, b.Bytes())), nil + return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil +} + +// 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 } func CreateBloom(receipts Receipts) Bloom { diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/json.go b/vendor/github.com/ethereum/go-ethereum/core/types/json.go new file mode 100644 index 000000000..d2718a96d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/json.go @@ -0,0 +1,108 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "encoding/hex" + "fmt" + "math/big" +) + +// JSON unmarshaling utilities. + +type hexBytes []byte + +func (b *hexBytes) MarshalJSON() ([]byte, error) { + if b != nil { + return []byte(fmt.Sprintf(`"0x%x"`, []byte(*b))), nil + } + return nil, nil +} + +func (b *hexBytes) UnmarshalJSON(input []byte) error { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + return fmt.Errorf("cannot unmarshal non-string into hexBytes") + } + input = input[1 : len(input)-1] + if len(input) < 2 || input[0] != '0' || input[1] != 'x' { + return fmt.Errorf("missing 0x prefix in hexBytes input %q", input) + } + dec := make(hexBytes, (len(input)-2)/2) + if _, err := hex.Decode(dec, input[2:]); err != nil { + return err + } + *b = dec + return nil +} + +type hexBig big.Int + +func (b *hexBig) MarshalJSON() ([]byte, error) { + if b != nil { + return []byte(fmt.Sprintf(`"0x%x"`, (*big.Int)(b))), nil + } + return nil, nil +} + +func (b *hexBig) UnmarshalJSON(input []byte) error { + raw, err := checkHexNumber(input) + if err != nil { + return err + } + dec, ok := new(big.Int).SetString(string(raw), 16) + if !ok { + return fmt.Errorf("invalid hex number") + } + *b = (hexBig)(*dec) + return nil +} + +type hexUint64 uint64 + +func (b *hexUint64) MarshalJSON() ([]byte, error) { + if b != nil { + return []byte(fmt.Sprintf(`"0x%x"`, *(*uint64)(b))), nil + } + return nil, nil +} + +func (b *hexUint64) UnmarshalJSON(input []byte) error { + raw, err := checkHexNumber(input) + if err != nil { + return err + } + _, err = fmt.Sscanf(string(raw), "%x", b) + return err +} + +func checkHexNumber(input []byte) (raw []byte, err error) { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + return nil, fmt.Errorf("cannot unmarshal non-string into hex number") + } + input = input[1 : len(input)-1] + if len(input) < 2 || input[0] != '0' || input[1] != 'x' { + return nil, fmt.Errorf("missing 0x prefix in hex number input %q", input) + } + if len(input) == 2 { + return nil, fmt.Errorf("empty hex number") + } + raw = input[2:] + if len(raw)%2 != 0 { + raw = append([]byte{'0'}, raw...) + } + return raw, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go index 5f847fc5c..b00fdabff 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go @@ -17,6 +17,8 @@ package types import ( + "encoding/json" + "errors" "fmt" "io" "math/big" @@ -26,6 +28,11 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +var ( + errMissingReceiptPostState = errors.New("missing post state root in JSON receipt") + errMissingReceiptFields = errors.New("missing required JSON receipt fields") +) + // Receipt represents the results of a transaction. type Receipt struct { // Consensus fields @@ -34,12 +41,22 @@ type Receipt struct { Bloom Bloom Logs vm.Logs - // Implementation fields + // Implementation fields (don't reorder!) TxHash common.Hash ContractAddress common.Address GasUsed *big.Int } +type jsonReceipt struct { + PostState *common.Hash `json:"root"` + CumulativeGasUsed *hexBig `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"` +} + // NewReceipt creates a barebone transaction receipt, copying the init fields. func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt { return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)} @@ -67,13 +84,49 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return nil } -// RlpEncode implements common.RlpEncode required for SHA3 derivation. -func (r *Receipt) RlpEncode() []byte { - bytes, err := rlp.EncodeToBytes(r) - if err != nil { - panic(err) +// MarshalJSON encodes receipts into the web3 RPC response block format. +func (r *Receipt) MarshalJSON() ([]byte, error) { + root := common.BytesToHash(r.PostState) + + return json.Marshal(&jsonReceipt{ + PostState: &root, + CumulativeGasUsed: (*hexBig)(r.CumulativeGasUsed), + Bloom: &r.Bloom, + Logs: &r.Logs, + TxHash: &r.TxHash, + ContractAddress: &r.ContractAddress, + GasUsed: (*hexBig)(r.GasUsed), + }) +} + +// UnmarshalJSON decodes the web3 RPC receipt format. +func (r *Receipt) UnmarshalJSON(input []byte) error { + var dec jsonReceipt + if err := json.Unmarshal(input, &dec); err != nil { + return err } - return bytes + // Ensure that all fields are set. PostState is checked separately because it is a + // recent addition to the RPC spec (as of August 2016) and older implementations might + // not provide it. Note that ContractAddress is not checked because it can be null. + if dec.PostState == nil { + return errMissingReceiptPostState + } + if dec.CumulativeGasUsed == nil || dec.Bloom == nil || + dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil { + return errMissingReceiptFields + } + *r = Receipt{ + PostState: (*dec.PostState)[:], + CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed), + Bloom: *dec.Bloom, + Logs: *dec.Logs, + TxHash: *dec.TxHash, + GasUsed: (*big.Int)(dec.GasUsed), + } + if dec.ContractAddress != nil { + r.ContractAddress = *dec.ContractAddress + } + return nil } // String implements the Stringer interface. @@ -122,7 +175,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { return nil } -// Receipts is a wrapper around a Receipt array to implement types.DerivableList. +// Receipts is a wrapper around a Receipt array to implement DerivableList. type Receipts []*Receipt // Len returns the number of receipts in this list. diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go index c71c98aa7..323bfaee6 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go @@ -19,21 +19,35 @@ package types import ( "container/heap" "crypto/ecdsa" + "encoding/json" "errors" "fmt" "io" "math/big" - "sort" "sync/atomic" "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" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) -var ErrInvalidSig = errors.New("invalid v, r, s values") +var ErrInvalidSig = errors.New("invalid transaction v, r, s values") + +var ( + errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields") + errMissingTxFields = errors.New("missing required JSON transaction fields") + errNoSigner = errors.New("missing signing methods") +) + +// deriveSigner makes a *best* guess about which signer to use. +func deriveSigner(V *big.Int) Signer { + if V.BitLen() > 0 && isProtectedV(V) { + return EIP155Signer{chainId: deriveChainId(V)} + } else { + return HomesteadSigner{} + } +} type Transaction struct { data txdata @@ -49,37 +63,43 @@ type txdata struct { Recipient *common.Address `rlp:"nil"` // nil means contract creation Amount *big.Int Payload []byte - V byte // signature + V *big.Int // signature R, S *big.Int // signature } -func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { - if len(data) > 0 { - data = common.CopyBytes(data) - } - return &Transaction{data: txdata{ - AccountNonce: nonce, - Recipient: nil, - Amount: new(big.Int).Set(amount), - GasLimit: new(big.Int).Set(gasLimit), - Price: new(big.Int).Set(gasPrice), - Payload: data, - R: new(big.Int), - S: new(big.Int), - }} +type jsonTransaction struct { + Hash *common.Hash `json:"hash"` + AccountNonce *hexUint64 `json:"nonce"` + Price *hexBig `json:"gasPrice"` + GasLimit *hexBig `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"` } func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) +} + +func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) +} + +func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { if len(data) > 0 { data = common.CopyBytes(data) } d := txdata{ AccountNonce: nonce, - Recipient: &to, + Recipient: to, Payload: data, Amount: new(big.Int), GasLimit: new(big.Int), Price: new(big.Int), + V: new(big.Int), R: new(big.Int), S: new(big.Int), } @@ -92,22 +112,120 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice if gasPrice != nil { d.Price.Set(gasPrice) } + return &Transaction{data: d} } +func pickSigner(rules params.Rules) Signer { + var signer Signer + switch { + case rules.IsEIP155: + signer = NewEIP155Signer(rules.ChainId) + case rules.IsHomestead: + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +// ChainId returns which chain id this transaction was signed for (if at all) +func (tx *Transaction) ChainId() *big.Int { + return deriveChainId(tx.data.V) +} + +// Protected returns whether the transaction is pretected from replay protection +func (tx *Transaction) Protected() bool { + return isProtectedV(tx.data.V) +} + +func isProtectedV(V *big.Int) bool { + if V.BitLen() <= 8 { + v := V.Uint64() + return v != 27 && v != 28 + } + // anything not 27 or 28 are considered unprotected + return true +} + +// DecodeRLP implements rlp.Encoder func (tx *Transaction) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &tx.data) } +// DecodeRLP implements rlp.Decoder func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { _, size, _ := s.Kind() err := s.Decode(&tx.data) if err == nil { tx.size.Store(common.StorageSize(rlp.ListSize(size))) } + return err } +// MarshalJSON encodes transactions into the web3 RPC response block format. +func (tx *Transaction) MarshalJSON() ([]byte, error) { + hash := tx.Hash() + + return json.Marshal(&jsonTransaction{ + Hash: &hash, + AccountNonce: (*hexUint64)(&tx.data.AccountNonce), + Price: (*hexBig)(tx.data.Price), + GasLimit: (*hexBig)(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), + }) +} + +// UnmarshalJSON decodes the web3 RPC transaction format. +func (tx *Transaction) UnmarshalJSON(input []byte) error { + var dec jsonTransaction + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + // Ensure that all fields are set. V, R, S are checked separately because they're a + // recent addition to the RPC spec (as of August 2016) and older implementations might + // not provide them. Note that Recipient is not checked because it can be missing for + // contract creations. + if dec.V == nil || dec.R == nil || dec.S == nil { + return errMissingTxSignatureFields + } + + var V byte + if isProtectedV((*big.Int)(dec.V)) { + V = normaliseV(NewEIP155Signer(deriveChainId((*big.Int)(dec.V))), (*big.Int)(dec.V)) + } else { + V = byte(((*big.Int)(dec.V)).Uint64()) + } + if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) { + return ErrInvalidSig + } + + if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil { + return errMissingTxFields + } + // Assign the fields. This is not atomic but reusing transactions + // for decoding isn't thread safe anyway. + *tx = Transaction{} + tx.data = txdata{ + AccountNonce: uint64(*dec.AccountNonce), + Recipient: dec.Recipient, + Amount: (*big.Int)(dec.Amount), + GasLimit: (*big.Int)(dec.GasLimit), + Price: (*big.Int)(dec.Price), + Payload: *dec.Payload, + V: (*big.Int)(dec.V), + R: (*big.Int)(dec.R), + S: (*big.Int)(dec.S), + } + return nil +} + func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) } func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } @@ -137,15 +255,8 @@ func (tx *Transaction) Hash() common.Hash { // SigHash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. -func (tx *Transaction) SigHash() common.Hash { - return rlpHash([]interface{}{ - tx.data.AccountNonce, - tx.data.Price, - tx.data.GasLimit, - tx.data.Recipient, - tx.data.Amount, - tx.data.Payload, - }) +func (tx *Transaction) SigHash(signer Signer) common.Hash { + return signer.Hash(tx) } func (tx *Transaction) Size() common.StorageSize { @@ -158,6 +269,7 @@ func (tx *Transaction) Size() common.StorageSize { return common.StorageSize(c) } +/* // From returns the address derived from the signature (V, R, S) using secp256k1 // elliptic curve and an error if it failed deriving or upon an incorrect // signature. @@ -173,32 +285,15 @@ func (tx *Transaction) Size() common.StorageSize { // both txs before and after the first homestead block. Signatures // valid in homestead are a subset of valid ones in Frontier) func (tx *Transaction) From() (common.Address, error) { - return doFrom(tx, true) -} + if tx.signer == nil { + return common.Address{}, errNoSigner + } -// FromFrontier returns the address derived from the signature (V, R, S) using -// secp256k1 elliptic curve and an error if it failed deriving or upon an -// incorrect signature. -// -// FromFrantier uses the frontier consensus rules to determine whether the -// signature is valid. -// -// FromFrontier caches the address, allowing it to be used regardless of -// Frontier / Homestead. however, the first time called it runs -// signature validations, so we need two versions. This makes it -// easier to ensure backwards compatibility of things like package rpc -// where eth_getblockbynumber uses tx.From() and needs to work for -// both txs before and after the first homestead block. Signatures -// valid in homestead are a subset of valid ones in Frontier) -func (tx *Transaction) FromFrontier() (common.Address, error) { - return doFrom(tx, false) -} - -func doFrom(tx *Transaction, homestead bool) (common.Address, error) { if from := tx.from.Load(); from != nil { return from.(common.Address), nil } - pubkey, err := tx.publicKey(homestead) + + pubkey, err := tx.signer.PublicKey(tx) if err != nil { return common.Address{}, err } @@ -208,6 +303,52 @@ func doFrom(tx *Transaction, homestead bool) (common.Address, error) { return addr, nil } +// SignatureValues returns the ECDSA signature values contained in the transaction. +func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int, err error) { + if tx.signer == nil { + return 0, nil, nil,errNoSigner + } + + return normaliseV(tx.signer, tx.data.V), new(big.Int).Set(tx.data.R),new(big.Int).Set(tx.data.S), nil +} + +*/ + +// AsMessage returns the transaction as a core.Message. +// +// AsMessage requires a signer to derive the sender. +// +// XXX Rename message to something less arbitrary? +func (tx *Transaction) AsMessage(s Signer) (Message, error) { + msg := Message{ + nonce: tx.data.AccountNonce, + price: new(big.Int).Set(tx.data.Price), + gasLimit: new(big.Int).Set(tx.data.GasLimit), + to: tx.data.Recipient, + amount: tx.data.Amount, + data: tx.data.Payload, + checkNonce: true, + } + + var err error + msg.from, err = Sender(s, tx) + return msg, err +} + +// SignECDSA signs the transaction using the given signer and private key +// +// XXX This only makes for a nice API: NewTx(...).SignECDSA(signer, prv). Should +// we keep this? +func (tx *Transaction) SignECDSA(signer Signer, prv *ecdsa.PrivateKey) (*Transaction, error) { + return signer.SignECDSA(tx, prv) +} + +// WithSignature returns a new transaction with the given signature. +// This signature needs to be formatted as described in the yellow paper (v+27). +func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) { + return signer.WithSignature(tx, sig) +} + // Cost returns amount + gasprice * gaslimit. func (tx *Transaction) Cost() *big.Int { total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit) @@ -215,59 +356,18 @@ func (tx *Transaction) Cost() *big.Int { return total } -func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) { - return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S) -} - -func (tx *Transaction) publicKey(homestead bool) ([]byte, error) { - if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) { - return nil, ErrInvalidSig - } - - // encode the signature in uncompressed format - r, s := tx.data.R.Bytes(), tx.data.S.Bytes() - sig := make([]byte, 65) - copy(sig[32-len(r):32], r) - copy(sig[64-len(s):64], s) - sig[64] = tx.data.V - 27 - - // recover the public key from the signature - hash := tx.SigHash() - pub, err := crypto.Ecrecover(hash[:], sig) - if err != nil { - glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err) - return nil, err - } - if len(pub) == 0 || pub[0] != 4 { - return nil, errors.New("invalid public key") - } - return pub, nil -} - -func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) { - if len(sig) != 65 { - panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) - } - cpy := &Transaction{data: tx.data} - cpy.data.R = new(big.Int).SetBytes(sig[:32]) - cpy.data.S = new(big.Int).SetBytes(sig[32:64]) - cpy.data.V = sig[64] + 27 - return cpy, nil -} - -func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) { - h := tx.SigHash() - sig, err := crypto.Sign(h[:], prv) - if err != nil { - return nil, err - } - return tx.WithSignature(sig) +func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { + return tx.data.V, tx.data.R, tx.data.S } func (tx *Transaction) String() string { + // make a best guess about the signer and use that to derive + // the sender. + signer := deriveSigner(tx.data.V) + var from, to string - if f, err := tx.From(); err != nil { - from = "[invalid sender]" + if f, err := Sender(signer, tx); err != nil { // derive but don't cache + from = "[invalid sender: invalid sig]" } else { from = fmt.Sprintf("%x", f[:]) } @@ -370,49 +470,93 @@ func (s *TxByPrice) Pop() interface{} { return x } -// SortByPriceAndNonce sorts the transactions by price in such a way that the -// nonce orderings within a single account are maintained. +// TransactionsByPriceAndNonce represents a set of transactions that can return +// transactions in a profit-maximising sorted order, while supporting removing +// entire batches of transactions for non-executable accounts. +type TransactionsByPriceAndNonce struct { + txs map[common.Address]Transactions // Per account nonce-sorted list of transactions + heads TxByPrice // Next transaction for each unique account (price heap) +} + +// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve +// price sorted transactions in a nonce-honouring way. // -// Note, this is not as trivial as it seems from the first look as there are three -// different criteria that need to be taken into account (price, nonce, account -// match), which cannot be done with any plain sorting method, as certain items -// cannot be compared without context. -// -// This method first sorts the separates the list of transactions into individual -// sender accounts and sorts them by nonce. After the account nonce ordering is -// satisfied, the results are merged back together by price, always comparing only -// the head transaction from each account. This is done via a heap to keep it fast. -func SortByPriceAndNonce(txs []*Transaction) { - // Separate the transactions by account and sort by nonce - byNonce := make(map[common.Address][]*Transaction) - for _, tx := range txs { - acc, _ := tx.From() // we only sort valid txs so this cannot fail - byNonce[acc] = append(byNonce[acc], tx) - } - for _, accTxs := range byNonce { - sort.Sort(TxByNonce(accTxs)) - } +// Note, the input map is reowned so the caller should not interact any more with +// if after providng it to the constructor. +func NewTransactionsByPriceAndNonce(txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { // Initialize a price based heap with the head transactions - byPrice := make(TxByPrice, 0, len(byNonce)) - for acc, accTxs := range byNonce { - byPrice = append(byPrice, accTxs[0]) - byNonce[acc] = accTxs[1:] + heads := make(TxByPrice, 0, len(txs)) + for acc, accTxs := range txs { + heads = append(heads, accTxs[0]) + txs[acc] = accTxs[1:] } - heap.Init(&byPrice) + heap.Init(&heads) - // Merge by replacing the best with the next from the same account - txs = txs[:0] - for len(byPrice) > 0 { - // Retrieve the next best transaction by price - best := heap.Pop(&byPrice).(*Transaction) - - // Push in its place the next transaction from the same account - acc, _ := best.From() // we only sort valid txs so this cannot fail - if accTxs, ok := byNonce[acc]; ok && len(accTxs) > 0 { - heap.Push(&byPrice, accTxs[0]) - byNonce[acc] = accTxs[1:] - } - // Accumulate the best priced transaction - txs = append(txs, best) + // Assemble and return the transaction set + return &TransactionsByPriceAndNonce{ + txs: txs, + heads: heads, } } + +// Peek returns the next transaction by price. +func (t *TransactionsByPriceAndNonce) Peek() *Transaction { + if len(t.heads) == 0 { + return nil + } + return t.heads[0] +} + +// Shift replaces the current best head with the next one from the same account. +func (t *TransactionsByPriceAndNonce) Shift() { + signer := deriveSigner(t.heads[0].data.V) + // derive signer but don't cache. + acc, _ := Sender(signer, t.heads[0]) // we only sort valid txs so this cannot fail + if txs, ok := t.txs[acc]; ok && len(txs) > 0 { + t.heads[0], t.txs[acc] = txs[0], txs[1:] + heap.Fix(&t.heads, 0) + } else { + heap.Pop(&t.heads) + } +} + +// Pop removes the best transaction, *not* replacing it with the next one from +// the same account. This should be used when a transaction cannot be executed +// and hence all subsequent ones should be discarded from the same account. +func (t *TransactionsByPriceAndNonce) Pop() { + heap.Pop(&t.heads) +} + +// Message is a fully derived transaction and implements core.Message +// +// NOTE: In a future PR this will be removed. +type Message struct { + to *common.Address + from common.Address + nonce uint64 + amount, price, gasLimit *big.Int + data []byte + checkNonce bool +} + +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte, checkNonce bool) Message { + return Message{ + from: from, + to: to, + nonce: nonce, + amount: amount, + price: price, + gasLimit: gasLimit, + data: data, + checkNonce: checkNonce, + } +} + +func (m Message) From() common.Address { return m.from } +func (m Message) To() *common.Address { return m.to } +func (m Message) GasPrice() *big.Int { return m.price } +func (m Message) Value() *big.Int { return m.amount } +func (m Message) Gas() *big.Int { return m.gasLimit } +func (m Message) Nonce() uint64 { return m.nonce } +func (m Message) Data() []byte { return m.data } +func (m Message) CheckNonce() bool { return m.checkNonce } diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go new file mode 100644 index 000000000..7d571643f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go @@ -0,0 +1,362 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +var ErrInvalidChainId = errors.New("invalid chaid id for signer") + +// sigCache is used to cache the derived sender and contains +// the signer used to derive it. +type sigCache struct { + signer Signer + from common.Address +} + +// MakeSigner returns a Signer based on the given chain config and block number. +func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { + var signer Signer + switch { + case config.IsEIP155(blockNumber): + signer = NewEIP155Signer(config.ChainId) + case config.IsHomestead(blockNumber): + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +// SignECDSA signs the transaction using the given signer and private key +func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := s.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return s.WithSignature(tx, sig) +} + +// Sender derives the sender from the tx using the signer derivation +// functions. + +// Sender returns the address derived from the signature (V, R, S) using secp256k1 +// elliptic curve and an error if it failed deriving or upon an incorrect +// signature. +// +// Sender may cache the address, allowing it to be used regardless of +// signing method. The cache is invalidated if the cached signer does +// not match the signer used in the current call. +func Sender(signer Signer, tx *Transaction) (common.Address, error) { + if sc := tx.from.Load(); sc != nil { + sigCache := sc.(sigCache) + // If the signer used to derive from in a previous + // call is not the same as used current, invalidate + // the cache. + if sigCache.signer.Equal(signer) { + return sigCache.from, nil + } + } + + pubkey, err := signer.PublicKey(tx) + if err != nil { + return common.Address{}, err + } + var addr common.Address + copy(addr[:], crypto.Keccak256(pubkey[1:])[12:]) + tx.from.Store(sigCache{signer: signer, from: addr}) + return addr, nil +} + +// SignatureValues returns the ECDSA signature values contained in the transaction. +func SignatureValues(signer Signer, tx *Transaction) (v byte, r *big.Int, s *big.Int) { + return normaliseV(signer, tx.data.V), new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S) +} + +type Signer interface { + // Hash returns the rlp encoded hash for signatures + Hash(tx *Transaction) common.Hash + // PubilcKey returns the public key derived from the signature + PublicKey(tx *Transaction) ([]byte, error) + // SignECDSA signs the transaction with the given and returns a copy of the tx + SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) + // WithSignature returns a copy of the transaction with the given signature + WithSignature(tx *Transaction, sig []byte) (*Transaction, error) + // Checks for equality on the signers + Equal(Signer) bool +} + +// EIP155Transaction implements TransactionInterface using the +// EIP155 rules +type EIP155Signer struct { + HomesteadSigner + + chainId, chainIdMul *big.Int +} + +func NewEIP155Signer(chainId *big.Int) EIP155Signer { + return EIP155Signer{ + chainId: chainId, + chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), + } +} + +func (s EIP155Signer) Equal(s2 Signer) bool { + eip155, ok := s2.(EIP155Signer) + return ok && eip155.chainId.Cmp(s.chainId) == 0 +} + +func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + return SignECDSA(s, tx, prv) +} + +func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) { + // if the transaction is not protected fall back to homestead signer + if !tx.Protected() { + return (HomesteadSigner{}).PublicKey(tx) + } + + if tx.ChainId().Cmp(s.chainId) != 0 { + return nil, ErrInvalidChainId + } + + V := normaliseV(s, tx.data.V) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + + // encode the signature in uncompressed format + R, S := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(R):32], R) + copy(sig[64-len(S):64], S) + sig[64] = V - 27 + + // recover the public key from the signature + hash := s.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// WithSignature returns a new transaction with the given signature. +// This signature needs to be formatted as described in the yellow paper (v+27). +func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + if s.chainId.BitLen() > 0 { + cpy.data.V = big.NewInt(int64(sig[64] - 27 + 35)) + cpy.data.V.Add(cpy.data.V, s.chainIdMul) + } + return cpy, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s EIP155Signer) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + s.chainId, uint(0), uint(0), + }) +} + +func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := s.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return s.WithSignature(tx, sig) +} + +// HomesteadTransaction implements TransactionInterface using the +// homestead rules. +type HomesteadSigner struct{ FrontierSigner } + +func (s HomesteadSigner) Equal(s2 Signer) bool { + _, ok := s2.(HomesteadSigner) + return ok +} + +// WithSignature returns a new transaction with the given snature. +// This snature needs to be formatted as described in the yellow paper (v+27). +func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + return cpy, nil +} + +func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := hs.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return hs.WithSignature(tx, sig) +} + +func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + V := byte(tx.data.V.Uint64()) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V - 27 + + // recover the public key from the snature + hash := hs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +type FrontierSigner struct{} + +func (s FrontierSigner) Equal(s2 Signer) bool { + _, ok := s2.(FrontierSigner) + return ok +} + +// WithSignature returns a new transaction with the given snature. +// This snature needs to be formatted as described in the yellow paper (v+27). +func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + return cpy, nil +} + +func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := fs.Hash(tx) + sig, err := crypto.SignEthereum(h[:], prv) + if err != nil { + return nil, err + } + return fs.WithSignature(tx, sig) +} + +// Hash returns the hash to be sned by the sender. +// It does not uniquely identify the transaction. +func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + }) +} + +func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + + V := byte(tx.data.V.Uint64()) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V - 27 + + // recover the public key from the snature + hash := fs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// normaliseV returns the Ethereum version of the V parameter +func normaliseV(s Signer, v *big.Int) byte { + if s, ok := s.(EIP155Signer); ok { + stdV := v.BitLen() <= 8 && (v.Uint64() == 27 || v.Uint64() == 28) + if s.chainId.BitLen() > 0 && !stdV { + nv := byte((new(big.Int).Sub(v, s.chainIdMul).Uint64()) - 35 + 27) + return nv + } + } + return byte(v.Uint64()) +} + +// deriveChainId derives the chain id from the given v parameter +func deriveChainId(v *big.Int) *big.Int { + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go b/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go index 844d3f328..70455a4c2 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go @@ -27,7 +27,7 @@ type ContractRef interface { ReturnGas(*big.Int, *big.Int) Address() common.Address Value() *big.Int - SetCode([]byte) + SetCode(common.Hash, []byte) ForEachStorage(callback func(key, value common.Hash) bool) } @@ -44,8 +44,9 @@ type Contract struct { jumpdests destinations // result of JUMPDEST analysis. Code []byte - Input []byte + CodeHash common.Hash CodeAddr *common.Address + Input []byte value, Gas, UsedGas, Price *big.Int @@ -143,14 +144,16 @@ func (c *Contract) Value() *big.Int { } // SetCode sets the code to the contract -func (self *Contract) SetCode(code []byte) { +func (self *Contract) SetCode(hash common.Hash, code []byte) { self.Code = code + self.CodeHash = hash } // SetCallCode sets the code of the contract and address of the backing data // object -func (self *Contract) SetCallCode(addr *common.Address, code []byte) { +func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { self.Code = code + self.CodeHash = hash self.CodeAddr = addr } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go b/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go index 664887454..e97c1e58c 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/environment.go @@ -20,25 +20,20 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" ) -// RuleSet is an interface that defines the current rule set during the -// execution of the EVM instructions (e.g. whether it's homestead) -type RuleSet interface { - IsHomestead(*big.Int) bool -} - // Environment is an EVM requirement and helper which allows access to outside // information such as states. type Environment interface { // The current ruleset - RuleSet() RuleSet + ChainConfig() *params.ChainConfig // The state database Db() Database // Creates a restorable snapshot - MakeSnapshot() Database + SnapshotDatabase() int // Set database to previous snapshot - SetSnapshot(Database) + RevertToSnapshot(int) // Address of the original invoker (first occurrence of the VM invoker) Origin() common.Address // The block number this VM is invoked on @@ -73,8 +68,6 @@ type Environment interface { 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) - - StructLogs() []StructLog } // Vm is the basic interface for an implementation of the EVM. @@ -96,6 +89,8 @@ type Database interface { 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) @@ -105,9 +100,15 @@ type Database interface { GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) - Delete(common.Address) bool + 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 - IsDeleted(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 } // Account represents a contract or basic ethereum account. @@ -119,7 +120,7 @@ type Account interface { Balance() *big.Int Address() common.Address ReturnGas(*big.Int, *big.Int) - SetCode([]byte) + SetCode(common.Hash, []byte) ForEachStorage(cb func(key, value common.Hash) bool) Value() *big.Int } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go index 116fbe456..1766bf9fb 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go @@ -26,3 +26,4 @@ import ( 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") diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go index 09feddd7d..fdbc0df7f 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go @@ -35,10 +35,29 @@ var ( GasStop = big.NewInt(0) GasContractByte = big.NewInt(200) + + n64 = big.NewInt(64) ) +// calcGas returns the actual gas cost of the call. +// +// The cost of gas was changed during the homestead price change HF. To allow for EIP150 +// to be implemented. The returned gas is gas - base * 63 / 64. +func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int { + if gasTable.CreateBySuicide != nil { + availableGas = new(big.Int).Sub(availableGas, base) + g := new(big.Int).Div(availableGas, n64) + g.Sub(availableGas, g) + + if g.Cmp(callCost) < 0 { + return g + } + } + return callCost +} + // baseCheck checks for any stack error underflows -func baseCheck(op OpCode, stack *stack, gas *big.Int) error { +func baseCheck(op OpCode, stack *Stack, gas *big.Int) error { // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit // PUSH is also allowed to calculate the same price for all PUSHes // DUP requirements are handled elsewhere (except for the stack limit check) @@ -127,18 +146,19 @@ var _baseCheck = map[OpCode]req{ MSIZE: {0, GasQuickStep, 1}, GAS: {0, GasQuickStep, 1}, BLOCKHASH: {1, GasExtStep, 1}, - BALANCE: {1, GasExtStep, 1}, - EXTCODESIZE: {1, GasExtStep, 1}, - EXTCODECOPY: {4, GasExtStep, 0}, + BALANCE: {1, Zero, 1}, + EXTCODESIZE: {1, Zero, 1}, + EXTCODECOPY: {4, Zero, 0}, SLOAD: {1, params.SloadGas, 1}, SSTORE: {2, Zero, 0}, SHA3: {2, params.Sha3Gas, 1}, CREATE: {3, params.CreateGas, 1}, - CALL: {7, params.CallGas, 1}, - CALLCODE: {7, params.CallGas, 1}, - DELEGATECALL: {6, params.CallGas, 1}, - JUMPDEST: {0, params.JumpdestGas, 0}, + // Zero is calculated in the gasSwitch + CALL: {7, Zero, 1}, + CALLCODE: {7, Zero, 1}, + DELEGATECALL: {6, Zero, 1}, SUICIDE: {1, Zero, 0}, + JUMPDEST: {0, params.JumpdestGas, 0}, RETURN: {2, Zero, 0}, PUSH1: {0, GasFastestStep, 1}, DUP1: {0, Zero, 1}, diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go index e2fc5ee0f..4f98953b5 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go @@ -21,20 +21,21 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) 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 @@ -58,7 +59,7 @@ 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) if err != nil { @@ -114,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))) @@ -142,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)) @@ -162,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)) @@ -171,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 { @@ -191,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) { - x, y := stack.pop(), stack.pop() - stack.push(U256(x.Exp(x, y, Pow256))) +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) @@ -213,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)) @@ -227,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)) @@ -236,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)) @@ -245,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)) @@ -254,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)) @@ -263,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)) @@ -272,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()])) @@ -293,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) @@ -303,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) @@ -314,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) stack.push(new(big.Int).Set(balance)) } -func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +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() @@ -361,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(len(env.Db().GetCode(addr)))) + l := big.NewInt(int64(env.Db().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() @@ -383,7 +384,7 @@ 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() @@ -395,11 +396,11 @@ func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Con memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) } -func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.push(new(big.Int).Set(contract.Price)) } -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) @@ -410,43 +411,43 @@ func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contr } } -func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +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) { +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) { +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) { +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) { +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() @@ -459,68 +460,73 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m env.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() 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)) } -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) ) - contract.UseGas(contract.Gas) + 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) // 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.RuleSet().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)) @@ -529,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() @@ -560,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() @@ -591,7 +597,7 @@ 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) @@ -605,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) { +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) - env.Db().Delete(contract.Address()) + env.Db().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++ { @@ -636,7 +642,7 @@ func makeLog(size int) instrFn { // 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 @@ -645,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)) } } @@ -654,7 +660,7 @@ func makeDup(size int64) instrFn { func makeSwap(size int64) instrFn { // switch n + 1 otherwise n would be swapped with n size += 1 - return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) { stack.swap(int(size)) } } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go index e2374df42..b75558d39 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jit.go @@ -303,7 +303,7 @@ func RunProgram(program *Program, env Environment, contract *Contract, input []b 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.RuleSet().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, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -425,7 +425,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi } gas.Set(g) case SUICIDE: - if !statedb.IsDeleted(contract.Address()) { + if !statedb.HasSuicided(contract.Address()) { statedb.AddRefund(params.SuicideRefundGas) } case MLOAD: @@ -491,7 +491,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi // jitBaseCheck is the same as baseCheck except it doesn't do the look up in the // gas table. This is done during compilation instead. -func jitBaseCheck(instr instruction, stack *stack, gas *big.Int) error { +func jitBaseCheck(instr instruction, stack *Stack, gas *big.Int) error { err := stack.require(instr.spop) if err != nil { return err diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go index 255c4f189..4e997f636 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go @@ -16,7 +16,11 @@ package vm -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) type jumpPtr struct { fn instrFn @@ -25,7 +29,7 @@ type jumpPtr struct { type vmJumpTable [256]jumpPtr -func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable { +func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable { var jumpTable vmJumpTable // when initialising a new VM execution we must first check the homestead diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/log.go b/vendor/github.com/ethereum/go-ethereum/core/vm/log.go index e4cc6021b..b292f5f43 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/log.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/log.go @@ -18,6 +18,7 @@ package vm import ( "encoding/json" + "errors" "fmt" "io" @@ -25,18 +26,33 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -type Log struct { - // Consensus fields - Address common.Address - Topics []common.Hash - Data []byte +var errMissingLogFields = errors.New("missing required JSON log fields") - // Derived fields (don't reorder!) - BlockNumber uint64 - TxHash common.Hash - TxIndex uint - BlockHash common.Hash - Index uint +// 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!). + 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 +} + +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"` + TxHash *common.Hash `json:"transactionHash"` + BlockHash *common.Hash `json:"blockHash"` + Index string `json:"logIndex"` } func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log { @@ -64,19 +80,50 @@ func (l *Log) String() string { return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index) } +// MarshalJSON implements json.Marshaler. func (r *Log) MarshalJSON() ([]byte, error) { - fields := map[string]interface{}{ - "address": r.Address, - "data": fmt.Sprintf("%#x", r.Data), - "blockNumber": fmt.Sprintf("%#x", r.BlockNumber), - "logIndex": fmt.Sprintf("%#x", r.Index), - "blockHash": r.BlockHash, - "transactionHash": r.TxHash, - "transactionIndex": fmt.Sprintf("%#x", r.TxIndex), - "topics": r.Topics, - } + 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), + }) +} - return json.Marshal(fields) +// UnmarshalJSON implements json.Umarshaler. +func (r *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 == "" { + return errMissingLogFields + } + declog := Log{ + Address: *dec.Address, + Topics: *dec.Topics, + TxHash: *dec.TxHash, + BlockHash: *dec.BlockHash, + } + if _, err := fmt.Sscanf(dec.Data, "0x%x", &declog.Data); err != nil { + return fmt.Errorf("invalid hex log data") + } + 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 + return nil } type Logs []*Log diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go index cbdc8a744..9e13d703b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go @@ -36,19 +36,13 @@ func (self Storage) Copy() Storage { return cpy } -// StructLogCollector is the basic interface to capture emited logs by the EVM logger. -type StructLogCollector interface { - // Adds the structured log to the collector. - AddStructLog(StructLog) -} - // LogConfig are the configuration options for structured logger the EVM type LogConfig struct { - DisableMemory bool // disable memory capture - DisableStack bool // disable stack capture - DisableStorage bool // disable storage capture - FullStorage bool // show full storage (slow) - Collector StructLogCollector // the log collector + DisableMemory bool // disable memory capture + DisableStack bool // disable stack capture + DisableStorage bool // disable storage capture + FullStorage bool // show full storage (slow) + Limit int // maximum length of output, but zero means unlimited } // StructLog is emitted to the Environment each cycle and lists information about the current internal state @@ -65,34 +59,45 @@ type StructLog struct { Err error } -// Logger is an EVM state logger and implements VmLogger. +// Tracer is used to collect execution traces from an EVM transaction +// execution. CaptureState is called for each step of the VM with the +// current VM state. +// 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 +} + +// StructLogger is an EVM state logger and implements Tracer. // -// Logger can capture state based on the given Log configuration and also keeps +// StructLogger can capture state based on the given Log configuration and also keeps // a track record of modified storage which is used in reporting snapshots of the // contract their storage. -type Logger struct { +type StructLogger struct { cfg LogConfig - env Environment + logs []StructLog changedValues map[common.Address]Storage } -// newLogger returns a new logger -func newLogger(cfg LogConfig, env Environment) *Logger { - return &Logger{ - cfg: cfg, - env: env, +// NewLogger returns a new logger +func NewStructLogger(cfg *LogConfig) *StructLogger { + logger := &StructLogger{ changedValues: make(map[common.Address]Storage), } + if cfg != nil { + logger.cfg = *cfg + } + return logger } // 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 *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, depth int, err error) { - // short circuit if no log collector is present - if l.cfg.Collector == nil { - return +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 } // initialise new changed values storage container for this contract @@ -139,7 +144,7 @@ func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory * 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. - l.env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool { + env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool { storage[key] = value // Return true, indicating we'd like to continue. return true @@ -150,9 +155,15 @@ func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory * } } // create a new snaptshot of the EVM. - log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, l.env.Depth(), err} - // Add the log to the collector - l.cfg.Collector.AddStructLog(log) + log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err} + + l.logs = append(l.logs, log) + return nil +} + +// StructLogs returns a list of captured log entries +func (l *StructLogger) StructLogs() []StructLog { + return l.logs } // StdErrFormat formats a slice of StructLogs to human readable format diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go index 7d861f1de..9d2b037a5 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go @@ -248,7 +248,7 @@ var opCodeToString = map[OpCode]string{ CALLDATACOPY: "CALLDATACOPY", CODESIZE: "CODESIZE", CODECOPY: "CODECOPY", - GASPRICE: "TXGASPRICE", + GASPRICE: "GASPRICE", // 0x40 range - block operations BLOCKHASH: "BLOCKHASH", diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go index d8c98e545..f1a2b60d3 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go @@ -23,13 +23,14 @@ 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 { - ruleSet vm.RuleSet - depth int - state *state.StateDB + chainConfig *params.ChainConfig + depth int + state *state.StateDB origin common.Address coinbase common.Address @@ -39,8 +40,6 @@ type Env struct { difficulty *big.Int gasLimit *big.Int - logs []vm.StructLog - getHashFn func(uint64) common.Hash evm *vm.EVM @@ -49,46 +48,34 @@ type Env struct { // NewEnv returns a new vm.Environment func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { env := &Env{ - ruleSet: cfg.RuleSet, - state: state, - origin: cfg.Origin, - coinbase: cfg.Coinbase, - number: cfg.BlockNumber, - time: cfg.Time, - difficulty: cfg.Difficulty, - gasLimit: cfg.GasLimit, + chainConfig: cfg.ChainConfig, + state: state, + origin: cfg.Origin, + coinbase: cfg.Coinbase, + number: cfg.BlockNumber, + time: cfg.Time, + difficulty: cfg.Difficulty, + gasLimit: cfg.GasLimit, } env.evm = vm.New(env, vm.Config{ Debug: cfg.Debug, EnableJit: !cfg.DisableJit, ForceJit: !cfg.DisableJit, - - Logger: vm.LogConfig{ - Collector: env, - }, }) return env } -func (self *Env) StructLogs() []vm.StructLog { - return self.logs -} - -func (self *Env) AddStructLog(log vm.StructLog) { - self.logs = append(self.logs, log) -} - -func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } -func (self *Env) Vm() vm.Vm { return self.evm } -func (self *Env) Origin() common.Address { return self.origin } -func (self *Env) BlockNumber() *big.Int { return self.number } -func (self *Env) Coinbase() common.Address { return self.coinbase } -func (self *Env) Time() *big.Int { return self.time } -func (self *Env) Difficulty() *big.Int { return self.difficulty } -func (self *Env) Db() vm.Database { return self.state } -func (self *Env) GasLimit() *big.Int { return self.gasLimit } -func (self *Env) VmType() vm.Type { return vm.StdVmTy } +func (self *Env) 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) } @@ -100,11 +87,11 @@ func (self *Env) SetDepth(i int) { self.depth = i } func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool { return self.state.GetBalance(from).Cmp(balance) >= 0 } -func (self *Env) MakeSnapshot() vm.Database { - return self.state.Copy() +func (self *Env) SnapshotDatabase() int { + return self.state.Snapshot() } -func (self *Env) SetSnapshot(copy vm.Database) { - self.state.Set(copy.(*state.StateDB)) +func (self *Env) RevertToSnapshot(snapshot int) { + self.state.RevertToSnapshot(snapshot) } func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go index 309d508c3..d51b435f8 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go @@ -22,20 +22,23 @@ 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" ) // The default, always homestead, rule set for the vm env type ruleSet struct{} func (ruleSet) IsHomestead(*big.Int) bool { return true } +func (ruleSet) GasTable(*big.Int) params.GasTable { + return params.GasTableHomesteadGasRepriceFork +} // Config is a basic type specifying certain configuration flags for running // the EVM. type Config struct { - RuleSet vm.RuleSet + ChainConfig *params.ChainConfig Difficulty *big.Int Origin common.Address Coinbase common.Address @@ -53,8 +56,16 @@ type Config struct { // sets defaults on the config func setDefaults(cfg *Config) { - if cfg.RuleSet == nil { - cfg.RuleSet = ruleSet{} + if cfg.ChainConfig == nil { + cfg.ChainConfig = ¶ms.ChainConfig{ + ChainId: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + } } if cfg.Difficulty == nil { @@ -104,7 +115,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { receiver = cfg.State.CreateAccount(common.StringToAddress("contract")) ) // set the receiver's (the executing contract) code for execution. - receiver.SetCode(code) + receiver.SetCode(crypto.Keccak256Hash(code), code) // Call the code with the given configuration. ret, err := vmenv.Call( diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go b/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go index c69ceddf4..648d8a04a 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/segments.go @@ -24,7 +24,7 @@ type jumpSeg struct { gas *big.Int } -func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) { +func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { if !contract.UseGas(j.gas) { return nil, OutOfGasError } @@ -42,7 +42,7 @@ type pushSeg struct { gas *big.Int } -func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) { +func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error if !contract.UseGas(s.gas) { diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go b/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go index 0046edec2..f6c1f76e4 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go @@ -24,58 +24,58 @@ import ( // stack is an object for basic stack operations. Items popped to the stack are // expected to be changed and modified. stack does not take care of adding newly // initialised objects. -type stack struct { +type Stack struct { data []*big.Int } -func newstack() *stack { - return &stack{} +func newstack() *Stack { + return &Stack{} } -func (st *stack) Data() []*big.Int { +func (st *Stack) Data() []*big.Int { return st.data } -func (st *stack) push(d *big.Int) { +func (st *Stack) push(d *big.Int) { // NOTE push limit (1024) is checked in baseCheck //stackItem := new(big.Int).Set(d) //st.data = append(st.data, stackItem) st.data = append(st.data, d) } -func (st *stack) pushN(ds ...*big.Int) { +func (st *Stack) pushN(ds ...*big.Int) { st.data = append(st.data, ds...) } -func (st *stack) pop() (ret *big.Int) { +func (st *Stack) pop() (ret *big.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return } -func (st *stack) len() int { +func (st *Stack) len() int { return len(st.data) } -func (st *stack) swap(n int) { +func (st *Stack) swap(n int) { st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] } -func (st *stack) dup(n int) { +func (st *Stack) dup(n int) { st.push(new(big.Int).Set(st.data[st.len()-n])) } -func (st *stack) peek() *big.Int { +func (st *Stack) peek() *big.Int { return st.data[st.len()-1] } -func (st *stack) require(n int) error { +func (st *Stack) require(n int) error { if st.len() < n { return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n) } return nil } -func (st *stack) Print() { +func (st *Stack) Print() { fmt.Println("### stack ###") if len(st.data) > 0 { for i, val := range st.data { diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go index 52e782b23..56aca6912 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm.go @@ -33,7 +33,7 @@ type Config struct { Debug bool EnableJit bool ForceJit bool - Logger LogConfig + Tracer Tracer } // EVM is used to run Ethereum based contracts and will utilise the @@ -44,22 +44,16 @@ type EVM struct { env Environment jumpTable vmJumpTable cfg Config - - logger *Logger + gasTable params.GasTable } // New returns a new instance of the EVM. func New(env Environment, cfg Config) *EVM { - var logger *Logger - if cfg.Debug { - logger = newLogger(cfg.Logger, env) - } - return &EVM{ env: env, - jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), + jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()), cfg: cfg, - logger: logger, + gasTable: env.ChainConfig().GasTable(env.BlockNumber()), } } @@ -79,11 +73,14 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { return nil, nil } - var ( - codehash = crypto.Keccak256Hash(contract.Code) // codehash is used when doing jump dest caching - program *Program - ) - if evm.cfg.EnableJit { + codehash := contract.CodeHash // codehash is used when doing jump dest caching + if codehash == (common.Hash{}) { + codehash = crypto.Keccak256Hash(contract.Code) + } + var program *Program + if false { + // JIT disabled due to JIT not being Homestead gas reprice ready. + // If the JIT is enabled check the status of the JIT program, // if it doesn't exist compile a new program in a separate // goroutine or wait for compilation to finish if the JIT is @@ -149,7 +146,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.logger.captureState(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) } }() @@ -175,8 +172,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // Get the memory location of pc 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.env, contract, caller, op, statedb, mem, stack) + newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack) if err != nil { return nil, err } @@ -191,7 +189,10 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { mem.Resize(newMemSize.Uint64()) // Add a log message if evm.cfg.Debug { - evm.logger.captureState(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 + } } if opPtr := evm.jumpTable[op]; opPtr.valid { @@ -241,7 +242,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(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, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -253,6 +254,34 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef // stack Check, memory resize & gas phase switch op { + case SUICIDE: + // EIP150 homestead gas reprice fork: + if gasTable.CreateBySuicide != nil { + gas.Set(gasTable.Suicide) + var ( + address = common.BigToAddress(stack.data[len(stack.data)-1]) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + ) + + if eip158 { + // if empty and transfers value + if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 { + gas.Add(gas, gasTable.CreateBySuicide) + } + } else if !env.Db().Exist(address) { + gas.Add(gas, gasTable.CreateBySuicide) + } + } + + if !statedb.HasSuicided(contract.Address()) { + statedb.AddRefund(params.SuicideRefundGas) + } + case EXTCODESIZE: + gas.Set(gasTable.ExtcodeSize) + case BALANCE: + gas.Set(gasTable.Balance) + case SLOAD: + gas.Set(gasTable.SLoad) case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: n := int(op - SWAP1 + 2) err := stack.require(n) @@ -281,8 +310,11 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas)) newMemSize = calcMemSize(mStart, mSize) + + quadMemGas(mem, newMemSize, gas) case EXP: - gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) + expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8) + gas.Add(gas, new(big.Int).Mul(big.NewInt(expByteLen), gasTable.ExpByte)) case SSTORE: err := stack.require(2) if err != nil { @@ -309,67 +341,109 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef g = params.SstoreResetGas } gas.Set(g) - case SUICIDE: - if !statedb.IsDeleted(contract.Address()) { - statedb.AddRefund(params.SuicideRefundGas) - } case MLOAD: newMemSize = calcMemSize(stack.peek(), u256(32)) + quadMemGas(mem, newMemSize, gas) case MSTORE8: newMemSize = calcMemSize(stack.peek(), u256(1)) + quadMemGas(mem, newMemSize, gas) case MSTORE: newMemSize = calcMemSize(stack.peek(), u256(32)) + quadMemGas(mem, newMemSize, gas) case RETURN: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) + quadMemGas(mem, newMemSize, gas) case SHA3: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) words := toWordSize(stack.data[stack.len()-2]) gas.Add(gas, words.Mul(words, params.Sha3WordGas)) + + quadMemGas(mem, newMemSize, gas) case CALLDATACOPY: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, params.CopyGas)) + + quadMemGas(mem, newMemSize, gas) case CODECOPY: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, params.CopyGas)) + + quadMemGas(mem, newMemSize, gas) case EXTCODECOPY: + gas.Set(gasTable.ExtcodeCopy) + newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) words := toWordSize(stack.data[stack.len()-4]) gas.Add(gas, words.Mul(words, params.CopyGas)) + quadMemGas(mem, newMemSize, gas) case CREATE: newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) - case CALL, CALLCODE: - gas.Add(gas, stack.data[stack.len()-1]) + quadMemGas(mem, newMemSize, gas) + case CALL, CALLCODE: + gas.Set(gasTable.Calls) + + transfersValue := stack.data[len(stack.data)-3].BitLen() > 0 if op == CALL { - if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { + var ( + address = common.BigToAddress(stack.data[len(stack.data)-2]) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + ) + if eip158 { + if env.Db().Empty(address) && transfersValue { + gas.Add(gas, params.CallNewAccountGas) + } + } else if !env.Db().Exist(address) { gas.Add(gas, params.CallNewAccountGas) } } - - if len(stack.data[stack.len()-3].Bytes()) > 0 { + if transfersValue { gas.Add(gas, params.CallValueTransferGas) } - x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) newMemSize = common.BigMax(x, y) + + quadMemGas(mem, newMemSize, gas) + + cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1]) + // Replace the stack item with the new gas calculation. This means that + // either the original item is left on the stack or the item is replaced by: + // (availableGas - gas) * 63 / 64 + // We replace the stack item so that it's available when the opCall instruction is + // called. This information is otherwise lost due to the dependency on *current* + // available gas. + stack.data[stack.len()-1] = cg + gas.Add(gas, cg) + case DELEGATECALL: - gas.Add(gas, stack.data[stack.len()-1]) + gas.Set(gasTable.Calls) x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6]) y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) newMemSize = common.BigMax(x, y) + + quadMemGas(mem, newMemSize, gas) + + cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1]) + // Replace the stack item with the new gas calculation. This means that + // either the original item is left on the stack or the item is replaced by: + // (availableGas - gas) * 63 / 64 + // We replace the stack item so that it's available when the opCall instruction is + // called. + stack.data[stack.len()-1] = cg + gas.Add(gas, cg) + } - quadMemGas(mem, newMemSize, gas) return newMemSize, gas, nil } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm_env.go b/vendor/github.com/ethereum/go-ethereum/core/vm_env.go index 599672382..43637bd13 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm_env.go @@ -23,6 +23,7 @@ import ( "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 @@ -41,19 +42,18 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash { } type VMEnv struct { - chainConfig *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 + 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 - logs []vm.StructLog // Logs for the custom structured logger getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes } -func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv { +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, @@ -63,27 +63,22 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m getHashFn: GetHashFn(header.ParentHash, chain), } - // if no log collector is present set self as the collector - if cfg.Logger.Collector == nil { - cfg.Logger.Collector = env - } - env.evm = vm.New(env, cfg) return env } -func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f } -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) 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) } @@ -95,12 +90,12 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool { return self.state.GetBalance(from).Cmp(balance) >= 0 } -func (self *VMEnv) MakeSnapshot() vm.Database { - return self.state.Copy() +func (self *VMEnv) SnapshotDatabase() int { + return self.state.Snapshot() } -func (self *VMEnv) SetSnapshot(copy vm.Database) { - self.state.Set(copy.(*state.StateDB)) +func (self *VMEnv) RevertToSnapshot(snapshot int) { + self.state.RevertToSnapshot(snapshot) } func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { @@ -121,11 +116,3 @@ func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []b 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) } - -func (self *VMEnv) StructLogs() []vm.StructLog { - return self.logs -} - -func (self *VMEnv) AddStructLog(log vm.StructLog) { - self.logs = append(self.logs, log) -} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go index 85f097095..e611bd8f4 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go @@ -78,6 +78,12 @@ func Ripemd160(data []byte) []byte { return ripemd.Sum(nil) } +// Ecrecover returns the public key for the private key that was used to +// calculate the signature. +// +// Note: secp256k1 expects the recover id to be either 0, 1. Ethereum +// signatures have a recover id with an offset of 27. Callers must take +// this into account and if "recovering" from an Ethereum signature adjust. func Ecrecover(hash, sig []byte) ([]byte, error) { return secp256k1.RecoverPubkey(hash, sig) } @@ -192,17 +198,40 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { return &ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil } -func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { - if len(hash) != 32 { - return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) +// Sign calculates an ECDSA signature. +// This function is susceptible to choosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be choosen by an adversery. Common +// solution is to hash any input before calculating the signature. +// +// Note: the calculated signature is not Ethereum compliant. The yellow paper +// dictates Ethereum singature to have a V value with and offset of 27 v in [27,28]. +// Use SignEthereum to get an Ethereum compliant signature. +func Sign(data []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { + if len(data) != 32 { + return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(data)) } seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8) defer zeroBytes(seckey) - sig, err = secp256k1.Sign(hash, seckey) + sig, err = secp256k1.Sign(data, seckey) return } +// SignEthereum calculates an Ethereum ECDSA signature. +// This function is susceptible to choosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be freely choosen by an adversery. +// Common solution is to hash the message before calculating the signature. +func SignEthereum(data []byte, prv *ecdsa.PrivateKey) ([]byte, error) { + sig, err := Sign(data, prv) + if err != nil { + return nil, err + } + sig[64] += 27 // as described in the yellow paper + return sig, err +} + func Encrypt(pub *ecdsa.PublicKey, message []byte) ([]byte, error) { return ecies.Encrypt(rand.Reader, ecies.ImportECDSAPublic(pub), message, nil, nil) } diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go index 13e7058fa..46d03ed38 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !amd64 appengine gccgo + package sha3 // rc stores the round constants for use in the ι step. diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.go new file mode 100644 index 000000000..de035c550 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!appengine,!gccgo + +package sha3 + +// This function is implemented in keccakf_amd64.s. + +//go:noescape + +func keccakF1600(state *[25]uint64) diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.s b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.s new file mode 100644 index 000000000..a35335178 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.s @@ -0,0 +1,392 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!appengine,!gccgo + +// This code was translated into a form compatible with 6a from the public +// domain sources at https://github.com/gvanas/KeccakCodePackage + +// Offsets in state +#define _ba (0*8) +#define _be (1*8) +#define _bi (2*8) +#define _bo (3*8) +#define _bu (4*8) +#define _ga (5*8) +#define _ge (6*8) +#define _gi (7*8) +#define _go (8*8) +#define _gu (9*8) +#define _ka (10*8) +#define _ke (11*8) +#define _ki (12*8) +#define _ko (13*8) +#define _ku (14*8) +#define _ma (15*8) +#define _me (16*8) +#define _mi (17*8) +#define _mo (18*8) +#define _mu (19*8) +#define _sa (20*8) +#define _se (21*8) +#define _si (22*8) +#define _so (23*8) +#define _su (24*8) + +// Temporary registers +#define rT1 AX + +// Round vars +#define rpState DI +#define rpStack SP + +#define rDa BX +#define rDe CX +#define rDi DX +#define rDo R8 +#define rDu R9 + +#define rBa R10 +#define rBe R11 +#define rBi R12 +#define rBo R13 +#define rBu R14 + +#define rCa SI +#define rCe BP +#define rCi rBi +#define rCo rBo +#define rCu R15 + +#define MOVQ_RBI_RCE MOVQ rBi, rCe +#define XORQ_RT1_RCA XORQ rT1, rCa +#define XORQ_RT1_RCE XORQ rT1, rCe +#define XORQ_RBA_RCU XORQ rBa, rCu +#define XORQ_RBE_RCU XORQ rBe, rCu +#define XORQ_RDU_RCU XORQ rDu, rCu +#define XORQ_RDA_RCA XORQ rDa, rCa +#define XORQ_RDE_RCE XORQ rDe, rCe + +#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \ + /* Prepare round */ \ + MOVQ rCe, rDa; \ + ROLQ $1, rDa; \ + \ + MOVQ _bi(iState), rCi; \ + XORQ _gi(iState), rDi; \ + XORQ rCu, rDa; \ + XORQ _ki(iState), rCi; \ + XORQ _mi(iState), rDi; \ + XORQ rDi, rCi; \ + \ + MOVQ rCi, rDe; \ + ROLQ $1, rDe; \ + \ + MOVQ _bo(iState), rCo; \ + XORQ _go(iState), rDo; \ + XORQ rCa, rDe; \ + XORQ _ko(iState), rCo; \ + XORQ _mo(iState), rDo; \ + XORQ rDo, rCo; \ + \ + MOVQ rCo, rDi; \ + ROLQ $1, rDi; \ + \ + MOVQ rCu, rDo; \ + XORQ rCe, rDi; \ + ROLQ $1, rDo; \ + \ + MOVQ rCa, rDu; \ + XORQ rCi, rDo; \ + ROLQ $1, rDu; \ + \ + /* Result b */ \ + MOVQ _ba(iState), rBa; \ + MOVQ _ge(iState), rBe; \ + XORQ rCo, rDu; \ + MOVQ _ki(iState), rBi; \ + MOVQ _mo(iState), rBo; \ + MOVQ _su(iState), rBu; \ + XORQ rDe, rBe; \ + ROLQ $44, rBe; \ + XORQ rDi, rBi; \ + XORQ rDa, rBa; \ + ROLQ $43, rBi; \ + \ + MOVQ rBe, rCa; \ + MOVQ rc, rT1; \ + ORQ rBi, rCa; \ + XORQ rBa, rT1; \ + XORQ rT1, rCa; \ + MOVQ rCa, _ba(oState); \ + \ + XORQ rDu, rBu; \ + ROLQ $14, rBu; \ + MOVQ rBa, rCu; \ + ANDQ rBe, rCu; \ + XORQ rBu, rCu; \ + MOVQ rCu, _bu(oState); \ + \ + XORQ rDo, rBo; \ + ROLQ $21, rBo; \ + MOVQ rBo, rT1; \ + ANDQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _bi(oState); \ + \ + NOTQ rBi; \ + ORQ rBa, rBu; \ + ORQ rBo, rBi; \ + XORQ rBo, rBu; \ + XORQ rBe, rBi; \ + MOVQ rBu, _bo(oState); \ + MOVQ rBi, _be(oState); \ + B_RBI_RCE; \ + \ + /* Result g */ \ + MOVQ _gu(iState), rBe; \ + XORQ rDu, rBe; \ + MOVQ _ka(iState), rBi; \ + ROLQ $20, rBe; \ + XORQ rDa, rBi; \ + ROLQ $3, rBi; \ + MOVQ _bo(iState), rBa; \ + MOVQ rBe, rT1; \ + ORQ rBi, rT1; \ + XORQ rDo, rBa; \ + MOVQ _me(iState), rBo; \ + MOVQ _si(iState), rBu; \ + ROLQ $28, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ga(oState); \ + G_RT1_RCA; \ + \ + XORQ rDe, rBo; \ + ROLQ $45, rBo; \ + MOVQ rBi, rT1; \ + ANDQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _ge(oState); \ + G_RT1_RCE; \ + \ + XORQ rDi, rBu; \ + ROLQ $61, rBu; \ + MOVQ rBu, rT1; \ + ORQ rBa, rT1; \ + XORQ rBo, rT1; \ + MOVQ rT1, _go(oState); \ + \ + ANDQ rBe, rBa; \ + XORQ rBu, rBa; \ + MOVQ rBa, _gu(oState); \ + NOTQ rBu; \ + G_RBA_RCU; \ + \ + ORQ rBu, rBo; \ + XORQ rBi, rBo; \ + MOVQ rBo, _gi(oState); \ + \ + /* Result k */ \ + MOVQ _be(iState), rBa; \ + MOVQ _gi(iState), rBe; \ + MOVQ _ko(iState), rBi; \ + MOVQ _mu(iState), rBo; \ + MOVQ _sa(iState), rBu; \ + XORQ rDi, rBe; \ + ROLQ $6, rBe; \ + XORQ rDo, rBi; \ + ROLQ $25, rBi; \ + MOVQ rBe, rT1; \ + ORQ rBi, rT1; \ + XORQ rDe, rBa; \ + ROLQ $1, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ka(oState); \ + K_RT1_RCA; \ + \ + XORQ rDu, rBo; \ + ROLQ $8, rBo; \ + MOVQ rBi, rT1; \ + ANDQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _ke(oState); \ + K_RT1_RCE; \ + \ + XORQ rDa, rBu; \ + ROLQ $18, rBu; \ + NOTQ rBo; \ + MOVQ rBo, rT1; \ + ANDQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _ki(oState); \ + \ + MOVQ rBu, rT1; \ + ORQ rBa, rT1; \ + XORQ rBo, rT1; \ + MOVQ rT1, _ko(oState); \ + \ + ANDQ rBe, rBa; \ + XORQ rBu, rBa; \ + MOVQ rBa, _ku(oState); \ + K_RBA_RCU; \ + \ + /* Result m */ \ + MOVQ _ga(iState), rBe; \ + XORQ rDa, rBe; \ + MOVQ _ke(iState), rBi; \ + ROLQ $36, rBe; \ + XORQ rDe, rBi; \ + MOVQ _bu(iState), rBa; \ + ROLQ $10, rBi; \ + MOVQ rBe, rT1; \ + MOVQ _mi(iState), rBo; \ + ANDQ rBi, rT1; \ + XORQ rDu, rBa; \ + MOVQ _so(iState), rBu; \ + ROLQ $27, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ma(oState); \ + M_RT1_RCA; \ + \ + XORQ rDi, rBo; \ + ROLQ $15, rBo; \ + MOVQ rBi, rT1; \ + ORQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _me(oState); \ + M_RT1_RCE; \ + \ + XORQ rDo, rBu; \ + ROLQ $56, rBu; \ + NOTQ rBo; \ + MOVQ rBo, rT1; \ + ORQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _mi(oState); \ + \ + ORQ rBa, rBe; \ + XORQ rBu, rBe; \ + MOVQ rBe, _mu(oState); \ + \ + ANDQ rBa, rBu; \ + XORQ rBo, rBu; \ + MOVQ rBu, _mo(oState); \ + M_RBE_RCU; \ + \ + /* Result s */ \ + MOVQ _bi(iState), rBa; \ + MOVQ _go(iState), rBe; \ + MOVQ _ku(iState), rBi; \ + XORQ rDi, rBa; \ + MOVQ _ma(iState), rBo; \ + ROLQ $62, rBa; \ + XORQ rDo, rBe; \ + MOVQ _se(iState), rBu; \ + ROLQ $55, rBe; \ + \ + XORQ rDu, rBi; \ + MOVQ rBa, rDu; \ + XORQ rDe, rBu; \ + ROLQ $2, rBu; \ + ANDQ rBe, rDu; \ + XORQ rBu, rDu; \ + MOVQ rDu, _su(oState); \ + \ + ROLQ $39, rBi; \ + S_RDU_RCU; \ + NOTQ rBe; \ + XORQ rDa, rBo; \ + MOVQ rBe, rDa; \ + ANDQ rBi, rDa; \ + XORQ rBa, rDa; \ + MOVQ rDa, _sa(oState); \ + S_RDA_RCA; \ + \ + ROLQ $41, rBo; \ + MOVQ rBi, rDe; \ + ORQ rBo, rDe; \ + XORQ rBe, rDe; \ + MOVQ rDe, _se(oState); \ + S_RDE_RCE; \ + \ + MOVQ rBo, rDi; \ + MOVQ rBu, rDo; \ + ANDQ rBu, rDi; \ + ORQ rBa, rDo; \ + XORQ rBi, rDi; \ + XORQ rBo, rDo; \ + MOVQ rDi, _si(oState); \ + MOVQ rDo, _so(oState) \ + +// func keccakF1600(state *[25]uint64) +TEXT ·keccakF1600(SB), 0, $200-8 + MOVQ state+0(FP), rpState + SUBQ $(8*25), SP + + // Convert the user state into an internal state + NOTQ _be(rpState) + NOTQ _bi(rpState) + NOTQ _go(rpState) + NOTQ _ki(rpState) + NOTQ _mi(rpState) + NOTQ _sa(rpState) + + // Execute the KeccakF permutation + MOVQ _ba(rpState), rCa + MOVQ _be(rpState), rCe + MOVQ _bu(rpState), rCu + + XORQ _ga(rpState), rCa + XORQ _ge(rpState), rCe + XORQ _gu(rpState), rCu + + XORQ _ka(rpState), rCa + XORQ _ke(rpState), rCe + XORQ _ku(rpState), rCu + + XORQ _ma(rpState), rCa + XORQ _me(rpState), rCe + XORQ _mu(rpState), rCu + + XORQ _sa(rpState), rCa + XORQ _se(rpState), rCe + MOVQ _si(rpState), rDi + MOVQ _so(rpState), rDo + XORQ _su(rpState), rCu + + mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP) + + // Revert the internal state to the user state + NOTQ _be(rpState) + NOTQ _bi(rpState) + NOTQ _go(rpState) + NOTQ _ki(rpState) + NOTQ _mi(rpState) + NOTQ _sa(rpState) + + ADDQ $(8*25), SP + RET diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go index c8fd31cb0..c86167c0b 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go @@ -42,7 +42,7 @@ type state struct { storage [maxRate]byte // Specific to SHA-3 and SHAKE. - fixedOutput bool // whether this is a fixed-ouput-length instance + fixedOutput bool // whether this is a fixed-output-length instance outputLen int // the default output size in bytes state spongeDirection // whether the sponge is absorbing or squeezing } diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go index d622979c1..46a0d63a6 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !amd64,!386 appengine +// +build !amd64,!386,!ppc64le appengine package sha3 diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go index c7851a1d8..929a486a7 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build amd64 386 +// +build amd64 386 ppc64le // +build !appengine package sha3 diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api.go b/vendor/github.com/ethereum/go-ethereum/eth/api.go index 3b7abb69a..b3185c392 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api.go @@ -25,6 +25,7 @@ import ( "math/big" "os" "runtime" + "time" "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/common" @@ -36,10 +37,14 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" ) +const defaultTraceTimeout = 5 * time.Second + // PublicEthereumAPI provides an API to access Ethereum full node-related // information. type PublicEthereumAPI struct { @@ -98,7 +103,7 @@ func (s *PublicMinerAPI) SubmitWork(nonce rpc.HexNumber, solution, digest common // result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty func (s *PublicMinerAPI) GetWork() (work [3]string, err error) { if !s.e.IsMining() { - if err := s.e.StartMining(0, ""); err != nil { + if err := s.e.StartMining(0); err != nil { return work, err } } @@ -137,7 +142,7 @@ func (s *PrivateMinerAPI) Start(threads *rpc.HexNumber) (bool, error) { threads = rpc.NewHexNumber(runtime.NumCPU()) } - err := s.e.StartMining(threads.Int(), "") + err := s.e.StartMining(threads.Int()) if err == nil { return true, nil } @@ -284,14 +289,14 @@ func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI { } // DumpBlock retrieves the entire state of the database at a given block. -func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) { +func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) { block := api.eth.BlockChain().GetBlockByNumber(number) if block == nil { - return state.World{}, fmt.Errorf("block #%d not found", number) + return state.Dump{}, fmt.Errorf("block #%d not found", number) } - stateDb, err := state.New(block.Root(), api.eth.ChainDb()) + stateDb, err := api.eth.BlockChain().StateAt(block.Root()) if err != nil { - return state.World{}, err + return state.Dump{}, err } return stateDb.RawDump(), nil } @@ -299,13 +304,13 @@ func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) { // PrivateDebugAPI is the collection of Etheruem full node APIs exposed over // the private debugging endpoint. type PrivateDebugAPI struct { - config *core.ChainConfig + config *params.ChainConfig eth *Ethereum } // NewPrivateDebugAPI creates a new API definition for the full node-related // private debug methods of the Ethereum service. -func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI { +func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebugAPI { return &PrivateDebugAPI{config: config, eth: eth} } @@ -317,9 +322,16 @@ type BlockTraceResult struct { Error string `json:"error"` } +// TraceArgs holds extra parameters to trace functions +type TraceArgs struct { + *vm.LogConfig + Tracer *string + Timeout *string +} + // TraceBlock processes the given block's RLP but does not import the block in to // the chain. -func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) BlockTraceResult { var block types.Block err := rlp.Decode(bytes.NewReader(blockRlp), &block) if err != nil { @@ -336,7 +348,7 @@ func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) Block // TraceBlockFromFile loads the block's RLP from the given file name and attempts to // process it but does not import the block in to the chain. -func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig) BlockTraceResult { blockRlp, err := ioutil.ReadFile(file) if err != nil { return BlockTraceResult{Error: fmt.Sprintf("could not read file: %v", err)} @@ -345,7 +357,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) B } // TraceBlockByNumber processes the block by canonical block number. -func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.LogConfig) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByNumber(number) if block == nil { @@ -361,7 +373,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) } // TraceBlockByHash processes the block by hash. -func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.LogConfig) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByHash(hash) if block == nil { @@ -376,49 +388,38 @@ func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config } } -// TraceCollector collects EVM structered logs. -// -// TraceCollector implements vm.Collector -type TraceCollector struct { - traces []vm.StructLog -} - -// AddStructLog adds a structered log. -func (t *TraceCollector) AddStructLog(slog vm.StructLog) { - t.traces = append(t.traces, slog) -} - // traceBlock processes the given block but does not save the state. -func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (bool, []vm.StructLog, error) { +func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConfig) (bool, []vm.StructLog, error) { // Validate and reprocess the block var ( blockchain = api.eth.BlockChain() validator = blockchain.Validator() processor = blockchain.Processor() - collector = &TraceCollector{} ) - if config == nil { - config = new(vm.Config) + + structLogger := vm.NewStructLogger(logConfig) + + config := vm.Config{ + Debug: true, + Tracer: structLogger, } - config.Debug = true // make sure debug is set. - config.Logger.Collector = collector if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false); err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - statedb, err := state.New(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), api.eth.ChainDb()) + statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) if err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - receipts, _, usedGas, err := processor.Process(block, statedb, *config) + receipts, _, usedGas, err := processor.Process(block, statedb, config) if err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } if err := validator.ValidateState(block, blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas); err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - return true, collector.traces, nil + return true, structLogger.StructLogs(), nil } // callmsg is the message type used for call transations. @@ -450,12 +451,43 @@ func formatError(err error) string { return err.Error() } +type timeoutError struct{} + +func (t *timeoutError) Error() string { + return "Execution time exceeded" +} + // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ethapi.ExecutionResult, error) { - if logger == nil { - logger = new(vm.LogConfig) +func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.Hash, config *TraceArgs) (interface{}, error) { + var tracer vm.Tracer + if config != nil && config.Tracer != nil { + timeout := defaultTraceTimeout + if config.Timeout != nil { + var err error + if timeout, err = time.ParseDuration(*config.Timeout); err != nil { + return nil, err + } + } + + var err error + if tracer, err = ethapi.NewJavascriptTracer(*config.Tracer); err != nil { + return nil, err + } + + // Handle timeouts and RPC cancellations + deadlineCtx, cancel := context.WithTimeout(ctx, timeout) + go func() { + <-deadlineCtx.Done() + tracer.(*ethapi.JavascriptTracer).Stop(&timeoutError{}) + }() + defer cancel() + } else if config == nil { + tracer = vm.NewStructLogger(nil) + } else { + tracer = vm.NewStructLogger(config.LogConfig) } + // Retrieve the tx from the chain and the containing block tx, blockHash, _, txIndex := core.GetTransaction(api.eth.ChainDb(), txHash) if tx == nil { @@ -470,25 +502,19 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC if parent == nil { return nil, fmt.Errorf("block parent %x not found", block.ParentHash()) } - stateDb, err := state.New(parent.Root(), api.eth.ChainDb()) + stateDb, err := api.eth.BlockChain().StateAt(parent.Root()) if err != nil { return nil, err } + + signer := types.MakeSigner(api.config, block.Number()) // Mutate the state and trace the selected transaction for idx, tx := range block.Transactions() { // Assemble the transaction call message - from, err := tx.FromFrontier() + msg, err := tx.AsMessage(signer) if err != nil { return nil, fmt.Errorf("sender retrieval failed: %v", err) } - msg := callmsg{ - addr: from, - to: tx.To(), - gas: tx.Gas(), - gasPrice: tx.GasPrice(), - value: tx.Value(), - data: tx.Data(), - } // 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{}) @@ -500,16 +526,22 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC continue } // Otherwise trace the transaction and return - vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger}) + vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), 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) } - return ðapi.ExecutionResult{ - Gas: gas, - ReturnValue: fmt.Sprintf("%x", ret), - StructLogs: ethapi.FormatLogs(vmenv.StructLogs()), - }, nil + + switch tracer := tracer.(type) { + case *vm.StructLogger: + return ðapi.ExecutionResult{ + Gas: gas, + ReturnValue: fmt.Sprintf("%x", ret), + StructLogs: ethapi.FormatLogs(tracer.StructLogs()), + }, nil + case *ethapi.JavascriptTracer: + return tracer.GetResult() + } } return nil, errors.New("database inconsistency") } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go index aa4c75e20..0925132ef 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go @@ -1,18 +1,18 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. +// This file is part of the go-ethereum library. // -// 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 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. // -// go-ethereum is distributed in the hope that it will be useful, +// 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 General Public License for more details. +// GNU Lesser 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 . +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . package eth @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/params" rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -40,6 +41,14 @@ type EthApiBackend struct { gpo *gasprice.GasPriceOracle } +func (b *EthApiBackend) ChainConfig() *params.ChainConfig { + return b.eth.chainConfig +} + +func (b *EthApiBackend) CurrentBlock() *types.Block { + return b.eth.blockchain.CurrentBlock() +} + func (b *EthApiBackend) SetHead(number uint64) { b.eth.blockchain.SetHead(number) } @@ -81,7 +90,7 @@ func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc. if header == nil || err != nil { return nil, nil, err } - stateDb, err := state.New(header.Root, b.eth.chainDb) + stateDb, err := b.eth.BlockChain().StateAt(header.Root) return EthApiState{stateDb}, header, err } @@ -98,12 +107,11 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { } 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.Copy() - addr, _ := msg.From() - from := stateDb.GetOrNewStateObject(addr) + 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, b.eth.chainConfig.VmConfig), vmError, nil + return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil } func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { @@ -118,21 +126,25 @@ func (b *EthApiBackend) RemoveTx(txHash common.Hash) { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() - b.eth.txPool.RemoveTx(txHash) + b.eth.txPool.Remove(txHash) } func (b *EthApiBackend) GetPoolTransactions() types.Transactions { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() - return b.eth.txPool.GetTransactions() + var txs types.Transactions + for _, batch := range b.eth.txPool.Pending() { + txs = append(txs, batch...) + } + return txs } -func (b *EthApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { +func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() - return b.eth.txPool.GetTransaction(txHash) + return b.eth.txPool.Get(hash) } func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { @@ -149,7 +161,7 @@ func (b *EthApiBackend) Stats() (pending int, queued int) { return b.eth.txPool.Stats() } -func (b *EthApiBackend) TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { b.eth.txMu.Lock() defer b.eth.txMu.Unlock() diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend.go b/vendor/github.com/ethereum/go-ethereum/eth/backend.go index 38b4eb60e..2351a62c8 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/backend.go @@ -35,7 +35,6 @@ import ( "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/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -47,6 +46,7 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -64,17 +64,16 @@ var ( ) type Config struct { - ChainConfig *core.ChainConfig // chain configuration + ChainConfig *params.ChainConfig // chain configuration NetworkId int // Network ID to use for selecting peers to connect to Genesis string // Genesis JSON to seed the chain database with FastSync bool // Enables the state download based fast synchronisation algorithm LightMode bool // Running in light client mode - NoDefSrv bool // No default LES server LightServ int // Maximum percentage of time allowed for serving LES requests LightPeers int // Maximum number of LES client peers + MaxPeers int // Maximum number of global peers - BlockChainVersion int SkipBcVersionCheck bool // e.g. blockchain export DatabaseCache int DatabaseHandles int @@ -106,14 +105,14 @@ type Config struct { } type LesServer interface { - Start() + Start(srvr *p2p.Server) Stop() Protocols() []p2p.Protocol } // Ethereum implements the Ethereum full node service. type Ethereum struct { - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig // Channel for shutting down the service shutdownChan chan bool // Channel for shutting down the ethereum stopDbUpgrade func() // stop chain db sequential key upgrade @@ -122,7 +121,7 @@ type Ethereum struct { txMu sync.Mutex blockchain *core.BlockChain protocolManager *ProtocolManager - ls LesServer + lesServer LesServer // DB interfaces chainDb ethdb.Database // Block chain database @@ -148,7 +147,7 @@ type Ethereum struct { } func (s *Ethereum) AddLesServer(ls LesServer) { - s.ls = ls + s.lesServer = ls } // New creates a new Ethereum object (including the @@ -195,10 +194,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if !config.SkipBcVersionCheck { bcVersion := core.GetBlockChainVersion(chainDb) - if bcVersion != config.BlockChainVersion && bcVersion != 0 { - return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, config.BlockChainVersion) + if bcVersion != core.BlockChainVersion && bcVersion != 0 { + return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion) } - core.WriteBlockChainVersion(chainDb, config.BlockChainVersion) + core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) } // load the genesis block or write a new one if no genesis @@ -218,10 +217,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig) eth.chainConfig = config.ChainConfig - eth.chainConfig.VmConfig = vm.Config{ - EnableJit: config.EnableJit, - ForceJit: config.ForceJit, - } + + glog.V(logger.Info).Infoln("Chain config:", eth.chainConfig) eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) if err != nil { @@ -233,7 +230,18 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool - if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { + maxPeers := config.MaxPeers + if config.LightServ > 0 { + // if we are running a light server, limit the number of ETH peers so that we reserve some space for incoming LES connections + // temporary solution until the new peer connectivity API is finished + halfPeers := maxPeers / 2 + maxPeers -= config.LightPeers + if maxPeers < halfPeers { + maxPeers = halfPeers + } + } + + if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { return nil, err } eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) @@ -328,7 +336,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend), + Service: filters.NewPublicFilterAPI(s.ApiBackend, false), Public: true, }, { Namespace: "admin", @@ -378,6 +386,17 @@ func (self *Ethereum) SetEtherbase(etherbase common.Address) { self.miner.SetEtherbase(etherbase) } +func (s *Ethereum) StartMining(threads int) error { + eb, err := s.Etherbase() + if err != nil { + err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) + glog.V(logger.Error).Infoln(err) + return err + } + go s.miner.Start(eb, threads) + return nil +} + func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) Miner() *miner.Miner { return s.miner } @@ -396,10 +415,10 @@ func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManage // Protocols implements node.Service, returning all the currently configured // network protocols to start. func (s *Ethereum) Protocols() []p2p.Protocol { - if s.ls == nil { + if s.lesServer == nil { return s.protocolManager.SubProtocols } else { - return append(s.protocolManager.SubProtocols, s.ls.Protocols()...) + return append(s.protocolManager.SubProtocols, s.lesServer.Protocols()...) } } @@ -411,8 +430,8 @@ func (s *Ethereum) Start(srvr *p2p.Server) error { s.StartAutoDAG() } s.protocolManager.Start() - if s.ls != nil { - s.ls.Start() + if s.lesServer != nil { + s.lesServer.Start(srvr) } return nil } @@ -425,8 +444,8 @@ func (s *Ethereum) Stop() error { } s.blockchain.Stop() s.protocolManager.Stop() - if s.ls != nil { - s.ls.Stop() + if s.lesServer != nil { + s.lesServer.Stop() } s.txPool.Stop() s.miner.Stop() diff --git a/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go b/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go index 3a6c3d85c..e0f05f540 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/eth/bind.go b/vendor/github.com/ethereum/go-ethereum/eth/bind.go index 26fc02860..747965d37 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/bind.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/bind.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -19,6 +19,7 @@ package eth import ( "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -50,47 +51,62 @@ func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend { } } -// HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated -// with the contract from the local API, and checking its size. -func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { - if ctx == nil { - ctx = context.Background() - } - block := rpc.LatestBlockNumber - if pending { - block = rpc.PendingBlockNumber - } - out, err := b.bcapi.GetCode(ctx, contract, block) - return len(common.FromHex(out)) > 0, err +// CodeAt retrieves any code associated with the contract from the local API. +func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) { + out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum)) + return common.FromHex(out), err +} + +// CodeAt retrieves any code associated with the contract from the local API. +func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber) + return common.FromHex(out), err } // ContractCall implements bind.ContractCaller executing an Ethereum contract // call with the specified data as the input. The pending flag requests execution // against the pending block, not the stable head of the chain. -func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { - if ctx == nil { - ctx = context.Background() - } - // Convert the input args to the API spec - args := ethapi.CallArgs{ - To: &contract, - Data: common.ToHex(data), - } - block := rpc.LatestBlockNumber - if pending { - block = rpc.PendingBlockNumber - } - // Execute the call and convert the output back to Go types - out, err := b.bcapi.Call(ctx, args, block) +func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) { + out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum)) return common.FromHex(out), err } +// ContractCall implements bind.ContractCaller executing an Ethereum contract +// call with the specified data as the input. The pending flag requests execution +// against the pending block, not the stable head of the chain. +func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { + out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber) + return common.FromHex(out), err +} + +func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs { + args := ethapi.CallArgs{ + To: msg.To, + From: msg.From, + Data: common.ToHex(msg.Data), + } + if msg.Gas != nil { + args.Gas = *rpc.NewHexNumber(msg.Gas) + } + if msg.GasPrice != nil { + args.GasPrice = *rpc.NewHexNumber(msg.GasPrice) + } + if msg.Value != nil { + args.Value = *rpc.NewHexNumber(msg.Value) + } + return args +} + +func toBlockNumber(num *big.Int) rpc.BlockNumber { + if num == nil { + return rpc.LatestBlockNumber + } + return rpc.BlockNumber(num.Int64()) +} + // PendingAccountNonce implements bind.ContractTransactor retrieving the current // pending nonce associated with an account. -func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { - if ctx == nil { - ctx = context.Background() - } +func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber) return out.Uint64(), err } @@ -98,9 +114,6 @@ func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account commo // SuggestGasPrice implements bind.ContractTransactor retrieving the currently // suggested gas price to allow a timely execution of a transaction. func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - if ctx == nil { - ctx = context.Background() - } return b.eapi.GasPrice(ctx) } @@ -109,25 +122,14 @@ func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) // the backend blockchain. There is no guarantee that this is 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 (b *ContractBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - if ctx == nil { - ctx = context.Background() - } - out, err := b.bcapi.EstimateGas(ctx, ethapi.CallArgs{ - From: sender, - To: contract, - Value: *rpc.NewHexNumber(value), - Data: common.ToHex(data), - }) +func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { + out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg)) return out.BigInt(), err } // SendTransaction implements bind.ContractTransactor injects the transaction // into the pending pool for execution. func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - if ctx == nil { - ctx = context.Background() - } raw, _ := rlp.EncodeToBytes(tx) _, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw)) return err diff --git a/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go b/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go index a0bce7f92..5fd73a586 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go @@ -1,4 +1,4 @@ -// Copyright 2014 The go-ethereum Authors +// 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 @@ -50,6 +50,8 @@ func upgradeSequentialKeys(db ethdb.Database) (stopFn func()) { return nil // empty database, nothing to do } + glog.V(logger.Info).Infof("Upgrading chain database to use sequential keys") + stopChn := make(chan struct{}) stoppedChn := make(chan struct{}) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go index c36dfb7e0..e41376810 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go @@ -19,6 +19,7 @@ package downloader import ( "sync" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" @@ -73,9 +74,10 @@ func (api *PublicDownloaderAPI) eventLoop() { var notification interface{} switch event.Data.(type) { case StartEvent: - result := &SyncingResult{Syncing: true} - result.Status.Origin, result.Status.Current, result.Status.Height, result.Status.Pulled, result.Status.Known = api.d.Progress() - notification = result + notification = &SyncingResult{ + Syncing: true, + Status: api.d.Progress(), + } case DoneEvent, FailedEvent: notification = false } @@ -117,19 +119,10 @@ func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, return rpcSub, nil } -// Progress gives progress indications when the node is synchronising with the Ethereum network. -type Progress struct { - Origin uint64 `json:"startingBlock"` - Current uint64 `json:"currentBlock"` - Height uint64 `json:"highestBlock"` - Pulled uint64 `json:"pulledStates"` - Known uint64 `json:"knownStates"` -} - // SyncingResult provides information about the current synchronisation status for this node. type SyncingResult struct { - Syncing bool `json:"syncing"` - Status Progress `json:"status"` + Syncing bool `json:"syncing"` + Status ethereum.SyncProgress `json:"status"` } // uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop. diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go index d9a344da7..b1f4b8169 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go @@ -28,6 +28,7 @@ import ( "sync/atomic" "time" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" @@ -63,12 +64,12 @@ var ( maxHeadersProcess = 2048 // Number of header download results to import at once into the chain maxResultsProcess = 2048 // Number of content download results to import at once into the chain - fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync - fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected - fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it - fsPivotInterval = 512 // Number of headers out of which to randomize the pivot point - fsMinFullBlocks = 1024 // Number of blocks to retrieve fully even in fast sync - fsCriticalTrials = 10 // Number of times to retry in the cricical section before bailing + fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync + fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected + fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it + fsPivotInterval = 256 // Number of headers out of which to randomize the pivot point + fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync + fsCriticalTrials = uint32(32) // Number of times to retry in the cricical section before bailing ) var ( @@ -104,7 +105,7 @@ type Downloader struct { peers *peerSet // Set of active peers from which download can proceed fsPivotLock *types.Header // Pivot header on critical section entry (cannot change between retries) - fsPivotFails int // Number of fast sync failures in the critical section + fsPivotFails uint32 // Number of subsequent fast sync failures in the critical section rttEstimate uint64 // Round trip time to target for download requests rttConfidence uint64 // Confidence in the estimated RTT (unit: millionths to allow atomic ops) @@ -211,7 +212,7 @@ func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader he // In addition, during the state download phase of fast synchronisation the number // of processed and the total number of known states are also returned. Otherwise // these are zero. -func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) { +func (d *Downloader) Progress() ethereum.SyncProgress { // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks pendingStates := uint64(d.queue.PendingNodeData()) @@ -228,7 +229,13 @@ func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) { case LightSync: current = d.headHeader().Number.Uint64() } - return d.syncStatsChainOrigin, current, d.syncStatsChainHeight, d.syncStatsStateDone, d.syncStatsStateDone + pendingStates + return ethereum.SyncProgress{ + StartingBlock: d.syncStatsChainOrigin, + CurrentBlock: current, + HighestBlock: d.syncStatsChainHeight, + PulledStates: d.syncStatsStateDone, + KnownStates: d.syncStatsStateDone + pendingStates, + } } // Synchronising returns whether the downloader is currently retrieving blocks. @@ -354,7 +361,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode // Set the requested sync mode, unless it's forbidden d.mode = mode - if d.mode == FastSync && d.fsPivotFails >= fsCriticalTrials { + if d.mode == FastSync && atomic.LoadUint32(&d.fsPivotFails) >= fsCriticalTrials { d.mode = FullSync } // Retrieve the origin peer and initiate the downloading process @@ -473,6 +480,11 @@ func (d *Downloader) spawnSync(origin uint64, fetchers ...func() error) error { d.queue.Close() d.cancel() wg.Wait() + + // If sync failed in the critical section, bump the fail counter + if err != nil && d.mode == FastSync && d.fsPivotLock != nil { + atomic.AddUint32(&d.fsPivotFails, 1) + } return err } @@ -919,10 +931,10 @@ func (d *Downloader) fetchNodeData() error { var ( deliver = func(packet dataPack) (int, error) { start := time.Now() - return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(err error, delivered int) { + return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(delivered int, progressed bool, err error) { // If the peer returned old-requested data, forgive if err == trie.ErrNotRequested { - glog.V(logger.Info).Infof("peer %s: replied to stale state request, forgiving", packet.PeerId()) + glog.V(logger.Debug).Infof("peer %s: replied to stale state request, forgiving", packet.PeerId()) return } if err != nil { @@ -941,11 +953,17 @@ func (d *Downloader) fetchNodeData() error { } d.syncStatsLock.Lock() d.syncStatsStateDone += uint64(delivered) + syncStatsStateDone := d.syncStatsStateDone // Thread safe copy for the log below d.syncStatsLock.Unlock() + // If real database progress was made, reset any fast-sync pivot failure + if progressed && atomic.LoadUint32(&d.fsPivotFails) > 1 { + glog.V(logger.Debug).Infof("fast-sync progressed, resetting fail counter from %d", atomic.LoadUint32(&d.fsPivotFails)) + atomic.StoreUint32(&d.fsPivotFails, 1) // Don't ever reset to 0, as that will unlock the pivot block + } // Log a message to the user and return if delivered > 0 { - glog.V(logger.Info).Infof("imported %d state entries in %v: processed %d, pending at least %d", delivered, time.Since(start), d.syncStatsStateDone, pending) + glog.V(logger.Info).Infof("imported %3d state entries in %9v: processed %d, pending at least %d", delivered, common.PrettyDuration(time.Since(start)), syncStatsStateDone, pending) } }) } @@ -1182,7 +1200,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { // If we're already past the pivot point, this could be an attack, thread carefully if rollback[len(rollback)-1].Number.Uint64() > pivot { // If we didn't ever fail, lock in te pivot header (must! not! change!) - if d.fsPivotFails == 0 { + if atomic.LoadUint32(&d.fsPivotFails) == 0 { for _, header := range rollback { if header.Number.Uint64() == pivot { glog.V(logger.Warn).Infof("Fast-sync critical section failure, locked pivot to header #%d [%x…]", pivot, header.Hash().Bytes()[:4]) @@ -1190,7 +1208,6 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { } } } - d.fsPivotFails++ } } }() diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go index fd239f7e4..b7ad92099 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go @@ -362,20 +362,20 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header { // Make sure chain order is honoured and preserved throughout hash := header.Hash() if header.Number == nil || header.Number.Uint64() != from { - glog.V(logger.Warn).Infof("Header #%v [%x] broke chain ordering, expected %d", header.Number, hash[:4], from) + glog.V(logger.Warn).Infof("Header #%v [%x…] broke chain ordering, expected %d", header.Number, hash[:4], from) break } if q.headerHead != (common.Hash{}) && q.headerHead != header.ParentHash { - glog.V(logger.Warn).Infof("Header #%v [%x] broke chain ancestry", header.Number, hash[:4]) + glog.V(logger.Warn).Infof("Header #%v [%x…] broke chain ancestry", header.Number, hash[:4]) break } // Make sure no duplicate requests are executed if _, ok := q.blockTaskPool[hash]; ok { - glog.V(logger.Warn).Infof("Header #%d [%x] already scheduled for block fetch", header.Number.Uint64(), hash[:4]) + glog.V(logger.Warn).Infof("Header #%d [%x…] already scheduled for block fetch", header.Number.Uint64(), hash[:4]) continue } if _, ok := q.receiptTaskPool[hash]; ok { - glog.V(logger.Warn).Infof("Header #%d [%x] already scheduled for receipt fetch", header.Number.Uint64(), hash[:4]) + glog.V(logger.Warn).Infof("Header #%d [%x…] already scheduled for receipt fetch", header.Number.Uint64(), hash[:4]) continue } // Queue the header for content retrieval @@ -388,7 +388,16 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header { q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) } if q.mode == FastSync && header.Number.Uint64() == q.fastSyncPivot { - // Pivoting point of the fast sync, retrieve the state tries + // Pivoting point of the fast sync, switch the state retrieval to this + glog.V(logger.Debug).Infof("Switching state downloads to %d [%x…]", header.Number.Uint64(), header.Hash().Bytes()[:4]) + + q.stateTaskIndex = 0 + q.stateTaskPool = make(map[common.Hash]int) + q.stateTaskQueue.Reset() + for _, req := range q.statePendPool { + req.Hashes = make(map[common.Hash]int) // Make sure executing requests fail, but don't disappear + } + q.stateSchedLock.Lock() q.stateScheduler = state.NewStateSync(header.Root, q.stateDatabase) q.stateSchedLock.Unlock() @@ -866,10 +875,10 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh accepted := len(headers) == MaxHeaderFetch if accepted { if headers[0].Number.Uint64() != request.From { - glog.V(logger.Detail).Infof("Peer %s: first header #%v [%x] broke chain ordering, expected %d", id, headers[0].Number, headers[0].Hash().Bytes()[:4], request.From) + glog.V(logger.Detail).Infof("Peer %s: first header #%v [%x…] broke chain ordering, expected %d", id, headers[0].Number, headers[0].Hash().Bytes()[:4], request.From) accepted = false } else if headers[len(headers)-1].Hash() != target { - glog.V(logger.Detail).Infof("Peer %s: last header #%v [%x] broke skeleton structure, expected %x", id, headers[len(headers)-1].Number, headers[len(headers)-1].Hash().Bytes()[:4], target[:4]) + glog.V(logger.Detail).Infof("Peer %s: last header #%v [%x…] broke skeleton structure, expected %x", id, headers[len(headers)-1].Number, headers[len(headers)-1].Hash().Bytes()[:4], target[:4]) accepted = false } } @@ -877,12 +886,12 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh for i, header := range headers[1:] { hash := header.Hash() if want := request.From + 1 + uint64(i); header.Number.Uint64() != want { - glog.V(logger.Warn).Infof("Peer %s: header #%v [%x] broke chain ordering, expected %d", id, header.Number, hash[:4], want) + glog.V(logger.Warn).Infof("Peer %s: header #%v [%x…] broke chain ordering, expected %d", id, header.Number, hash[:4], want) accepted = false break } if headers[i].Hash() != header.ParentHash { - glog.V(logger.Warn).Infof("Peer %s: header #%v [%x] broke chain ancestry", id, header.Number, hash[:4]) + glog.V(logger.Warn).Infof("Peer %s: header #%v [%x…] broke chain ancestry", id, header.Number, hash[:4]) accepted = false break } @@ -1039,9 +1048,8 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ } // DeliverNodeData injects a node state data retrieval response into the queue. -// The method returns the number of node state entries originally requested, and -// the number of them actually accepted from the delivery. -func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, int)) (int, error) { +// The method returns the number of node state accepted from the delivery. +func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(int, bool, error)) (int, error) { q.lock.Lock() defer q.lock.Unlock() @@ -1099,31 +1107,34 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i // deliverNodeData is the asynchronous node data processor that injects a batch // of sync results into the state scheduler. -func (q *queue) deliverNodeData(results []trie.SyncResult, callback func(error, int)) { +func (q *queue) deliverNodeData(results []trie.SyncResult, callback func(int, bool, error)) { // Wake up WaitResults after the state has been written because it // might be waiting for the pivot block state to get completed. defer q.active.Signal() // Process results one by one to permit task fetches in between + progressed := false for i, result := range results { q.stateSchedLock.Lock() if q.stateScheduler == nil { // Syncing aborted since this async delivery started, bail out q.stateSchedLock.Unlock() - callback(errNoFetchesPending, i) + callback(i, progressed, errNoFetchesPending) return } - if _, err := q.stateScheduler.Process([]trie.SyncResult{result}); err != nil { + if prog, _, err := q.stateScheduler.Process([]trie.SyncResult{result}); err != nil { // Processing a state result failed, bail out q.stateSchedLock.Unlock() - callback(err, i) + callback(i, progressed, err) return + } else if prog { + progressed = true } // Item processing succeeded, release the lock (temporarily) q.stateSchedLock.Unlock() } - callback(nil, len(results)) + callback(len(results), progressed, nil) } // Prepare configures the result cache to allow accepting and caching inbound diff --git a/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go index bd235bb9e..e225b4996 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go @@ -38,7 +38,7 @@ const ( maxUncleDist = 7 // Maximum allowed backward distance from the chain head maxQueueDist = 32 // Maximum allowed distance from the chain head to queue hashLimit = 256 // Maximum number of unique blocks a peer may have announced - blockLimit = 64 // Maximum number of unique blocks a per may have delivered + blockLimit = 64 // Maximum number of unique blocks a peer may have delivered ) var ( @@ -661,7 +661,7 @@ func (f *Fetcher) insert(peer string, block *types.Block) { // If the parent's unknown, abort insertion parent := f.getBlock(block.ParentHash()) if parent == nil { - glog.V(logger.Debug).Infof("Peer %s: parent []%x] of block #%d [%x…] unknown", block.ParentHash().Bytes()[:4], peer, block.NumberU64(), hash[:4]) + glog.V(logger.Debug).Infof("Peer %s: parent [%x…] of block #%d [%x…] unknown", peer, block.ParentHash().Bytes()[:4], block.NumberU64(), hash[:4]) return } // Quickly validate the header and propagate the block if it passes diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go index 4ba3fb1f2..834513262 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/rpc" ) @@ -53,7 +52,8 @@ type filter struct { // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various // information related to the Ethereum protocol such als blocks, transactions and logs. type PublicFilterAPI struct { - apiBackend ethapi.Backend + backend Backend + useMipMap bool mux *event.TypeMux quit chan struct{} chainDb ethdb.Database @@ -63,13 +63,14 @@ type PublicFilterAPI struct { } // NewPublicFilterAPI returns a new PublicFilterAPI instance. -func NewPublicFilterAPI(apiBackend ethapi.Backend) *PublicFilterAPI { +func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { api := &PublicFilterAPI{ - apiBackend: apiBackend, - mux: apiBackend.EventMux(), - chainDb: apiBackend.ChainDb(), - events: NewEventSystem(apiBackend.EventMux()), - filters: make(map[rpc.ID]*filter), + backend: backend, + useMipMap: !lightMode, + mux: backend.EventMux(), + chainDb: backend.ChainDb(), + events: NewEventSystem(backend.EventMux(), backend, lightMode), + filters: make(map[rpc.ID]*filter), } go api.timeoutLoop() @@ -325,7 +326,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) } - filter := New(api.apiBackend) + filter := New(api.backend, api.useMipMap) filter.SetBeginBlock(crit.FromBlock.Int64()) filter.SetEndBlock(crit.ToBlock.Int64()) filter.SetAddresses(crit.Addresses) @@ -365,7 +366,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log return []Log{}, nil } - filter := New(api.apiBackend) + filter := New(api.backend, api.useMipMap) filter.SetBeginBlock(f.crit.FromBlock.Int64()) filter.SetEndBlock(f.crit.ToBlock.Int64()) filter.SetAddresses(f.crit.Addresses) @@ -541,4 +542,3 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error { return nil } - diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go index 1e6a6f6f9..4004af300 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go @@ -17,21 +17,29 @@ package filters import ( - // "math" + "math" "time" "github.com/ethereum/go-ethereum/common" - // "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) +type Backend interface { + ChainDb() ethdb.Database + EventMux() *event.TypeMux + HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) + GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) +} + // Filter can be used to retrieve and filter logs type Filter struct { - apiBackend ethapi.Backend + backend Backend + useMipMap bool created time.Time @@ -43,10 +51,11 @@ type Filter struct { // New creates a new filter which uses a bloom filter on blocks to figure out whether // a particular block is interesting or not. -func New(apiBackend ethapi.Backend) *Filter { +func New(backend Backend, useMipMap bool) *Filter { return &Filter{ - apiBackend: apiBackend, - db: apiBackend.ChainDb(), + backend: backend, + useMipMap: useMipMap, + db: backend.ChainDb(), } } @@ -75,7 +84,10 @@ func (f *Filter) SetTopics(topics [][]common.Hash) { // Run filters logs with the current parameters set func (f *Filter) Find(ctx context.Context) ([]Log, error) { - head, _ := f.apiBackend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + if head == nil { + return nil, nil + } headBlockNumber := head.Number.Uint64() var beginBlockNo uint64 = uint64(f.begin) @@ -90,14 +102,13 @@ func (f *Filter) Find(ctx context.Context) ([]Log, error) { // if no addresses are present we can't make use of fast search which // uses the mipmap bloom filters to check for fast inclusion and uses // higher range probability in order to ensure at least a false positive - // if len(self.addresses) == 0 { - return f.getLogs(ctx, beginBlockNo, endBlockNo) - // } - // return self.mipFind(beginBlockNo, endBlockNo, 0) + if !f.useMipMap || len(f.addresses) == 0 { + return f.getLogs(ctx, beginBlockNo, endBlockNo) + } + return f.mipFind(beginBlockNo, endBlockNo, 0), nil } -/*func (self *Filter) mipFind(start, end uint64, depth int) (logs vm.Logs) { ->>>>>>> les: light client protocol and API +func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { level := core.MIPMapLevels[depth] // normalise numerator so we can work in level specific batches and // work with the proper range checks @@ -112,7 +123,8 @@ func (f *Filter) Find(ctx context.Context) ([]Log, error) { start := uint64(math.Max(float64(num), float64(start))) end := uint64(math.Min(float64(num+level-1), float64(end))) if depth+1 == len(core.MIPMapLevels) { - logs = append(logs, f.getLogs(start, end)...) + l, _ := f.getLogs(context.Background(), start, end) + logs = append(logs, l...) } else { logs = append(logs, f.mipFind(start, end, depth+1)...) } @@ -125,11 +137,11 @@ func (f *Filter) Find(ctx context.Context) ([]Log, error) { } return logs -}*/ +} func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) { for i := start; i <= end; i++ { - header, err := f.apiBackend.HeaderByNumber(ctx, rpc.BlockNumber(i)) + header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i)) if header == nil || err != nil { return logs, err } @@ -138,7 +150,7 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er // current parameters if f.bloomFilter(header.Bloom) { // Get the logs of the block - receipts, err := f.apiBackend.GetReceipts(ctx, header.Hash()) + receipts, err := f.backend.GetReceipts(ctx, header.Hash()) if err != nil { return nil, err } @@ -207,9 +219,13 @@ Logs: } func (f *Filter) bloomFilter(bloom types.Bloom) bool { - if len(f.addresses) > 0 { + return bloomFilter(bloom, f.addresses, f.topics) +} + +func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { + if len(addresses) > 0 { var included bool - for _, addr := range f.addresses { + for _, addr := range addresses { if types.BloomLookup(bloom, addr) { included = true break @@ -221,7 +237,7 @@ func (f *Filter) bloomFilter(bloom types.Bloom) bool { } } - for _, sub := range f.topics { + for _, sub := range topics { var included bool for _, topic := range sub { if (topic == common.Hash{}) || types.BloomLookup(bloom, topic) { diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go index 04a55fd09..c2c072a9f 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" ) // Type determines the kind of filter and is used to put the filter in to @@ -95,6 +96,9 @@ type subscription struct { type EventSystem struct { mux *event.TypeMux sub event.Subscription + backend Backend + lightMode bool + lastHead *types.Header install chan *subscription // install filter for event notification uninstall chan *subscription // remove filter for event notification } @@ -105,9 +109,11 @@ type EventSystem struct { // // The returned manager has a loop that needs to be stopped with the Stop function // or by stopping the given mux. -func NewEventSystem(mux *event.TypeMux) *EventSystem { +func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem { m := &EventSystem{ mux: mux, + backend: backend, + lightMode: lightMode, install: make(chan *subscription), uninstall: make(chan *subscription), } @@ -235,7 +241,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti type filterIndex map[Type]map[rpc.ID]*subscription // broadcast event to filters that match criteria. -func broadcast(filters filterIndex, ev *event.Event) { +func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) { if ev == nil { return } @@ -279,9 +285,79 @@ func broadcast(filters filterIndex, ev *event.Event) { f.headers <- e.Block.Header() } } + if es.lightMode && len(filters[LogsSubscription]) > 0 { + es.lightFilterNewHead(e.Block.Header(), func(header *types.Header, remove bool) { + for _, f := range filters[LogsSubscription] { + if ev.Time.After(f.created) { + if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 { + f.logs <- matchedLogs + } + } + } + }) + } } } +func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { + oldh := es.lastHead + es.lastHead = newHeader + if oldh == nil { + return + } + newh := newHeader + // find common ancestor, create list of rolled back and new block hashes + var oldHeaders, newHeaders []*types.Header + for oldh.Hash() != newh.Hash() { + if oldh.Number.Uint64() >= newh.Number.Uint64() { + oldHeaders = append(oldHeaders, oldh) + oldh = core.GetHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) + } + if oldh.Number.Uint64() < newh.Number.Uint64() { + newHeaders = append(newHeaders, newh) + newh = core.GetHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) + if newh == nil { + // happens when CHT syncing, nothing to do + newh = oldh + } + } + } + // roll back old blocks + for _, h := range oldHeaders { + callBack(h, true) + } + // check new blocks (array is in reverse order) + for i := len(newHeaders) - 1; i >= 0; i-- { + callBack(newHeaders[i], false) + } +} + +// 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) + 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 + for _, receipt := range receipts { + rl := make([]Log, len(receipt.Logs)) + for i, l := range receipt.Logs { + rl[i] = Log{l, remove} + } + unfiltered = append(unfiltered, rl...) + } + logs := filterLogs(unfiltered, addresses, topics) + //fmt.Println("found", len(logs)) + return logs + } + return nil +} + // eventLoop (un)installs filters and processes mux events. func (es *EventSystem) eventLoop() { var ( @@ -294,7 +370,7 @@ func (es *EventSystem) eventLoop() { if !active { // system stopped return } - broadcast(index, ev) + es.broadcast(index, ev) case f := <-es.install: if _, found := index[f.typ]; !found { index[f.typ] = make(map[rpc.ID]*subscription) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go index ce075ff4e..8886d32d7 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -78,7 +78,7 @@ func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error return lastPrice, nil } - blockNum := head.GetNumberU64() + blockNum := head.Number.Uint64() chn := make(chan lpResult, LpoMaxBlocks) sent := 0 exp := 0 diff --git a/vendor/github.com/ethereum/go-ethereum/eth/gpu_mining.go b/vendor/github.com/ethereum/go-ethereum/eth/gpu_mining.go deleted file mode 100644 index 12fa74601..000000000 --- a/vendor/github.com/ethereum/go-ethereum/eth/gpu_mining.go +++ /dev/null @@ -1,102 +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 . - -// +build opencl - -package eth - -import ( - "fmt" - "math/big" - "strconv" - "strings" - "time" - - "github.com/ethereum/ethash" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/miner" -) - -func (s *Ethereum) StartMining(threads int, gpus string) error { - eb, err := s.Etherbase() - if err != nil { - err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) - glog.V(logger.Error).Infoln(err) - return err - } - - // GPU mining - if gpus != "" { - var ids []int - for _, s := range strings.Split(gpus, ",") { - i, err := strconv.Atoi(s) - if err != nil { - return fmt.Errorf("Invalid GPU id(s): %v", err) - } - if i < 0 { - return fmt.Errorf("Invalid GPU id: %v", i) - } - ids = append(ids, i) - } - - // TODO: re-creating miner is a bit ugly - s.miner = miner.New(s, s.chainConfig, s.EventMux(), ethash.NewCL(ids)) - go s.miner.Start(eb, len(ids)) - return nil - } - - // CPU mining - go s.miner.Start(eb, threads) - return nil -} - -func GPUBench(gpuid uint64) { - e := ethash.NewCL([]int{int(gpuid)}) - - var h common.Hash - bogoHeader := &types.Header{ - ParentHash: h, - Number: big.NewInt(int64(42)), - Difficulty: big.NewInt(int64(999999999999999)), - } - bogoBlock := types.NewBlock(bogoHeader, nil, nil, nil) - - err := ethash.InitCL(bogoBlock.NumberU64(), e) - if err != nil { - fmt.Println("OpenCL init error: ", err) - return - } - - stopChan := make(chan struct{}) - reportHashRate := func() { - for { - time.Sleep(3 * time.Second) - fmt.Printf("hashes/s : %v\n", e.GetHashrate()) - } - } - fmt.Printf("Starting benchmark (%v seconds)\n", 60) - go reportHashRate() - go e.Search(bogoBlock, stopChan, 0) - time.Sleep(60 * time.Second) - fmt.Println("OK.") -} - -func PrintOpenCLDevices() { - ethash.PrintDevices() -} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/handler.go b/vendor/github.com/ethereum/go-ethereum/eth/handler.go index 751408788..8f05cc5b1 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/handler.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/handler.go @@ -17,6 +17,7 @@ package eth import ( + "encoding/json" "errors" "fmt" "math" @@ -36,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" ) @@ -66,7 +68,8 @@ type ProtocolManager struct { txpool txPool blockchain *core.BlockChain chaindb ethdb.Database - chainconfig *core.ChainConfig + chainconfig *params.ChainConfig + maxPeers int downloader *downloader.Downloader fetcher *fetcher.Fetcher @@ -93,7 +96,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { +func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ networkId: networkId, @@ -102,6 +105,7 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, blockchain: blockchain, chaindb: chaindb, chainconfig: config, + maxPeers: maxPeers, peers: newPeerSet(), newPeerCh: make(chan *peer), noMorePeers: make(chan struct{}), @@ -252,10 +256,10 @@ func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *p // handle is the callback invoked to manage the life cycle of an eth peer. When // this function terminates, the peer is disconnected. func (pm *ProtocolManager) handle(p *peer) error { - if pm.peers.Len() >= 20 { + if pm.peers.Len() >= pm.maxPeers { return p2p.DiscTooManyPeers - } - + } + glog.V(logger.Debug).Infof("%v: peer connected [%s]", p, p.Name()) // Execute the Ethereum handshake @@ -291,7 +295,7 @@ func (pm *ProtocolManager) handle(p *peer) error { } // Start a timer to disconnect if the peer doesn't reply in time p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() { - glog.V(logger.Warn).Infof("%v: timed out DAO fork-check, dropping", p) + glog.V(logger.Debug).Infof("%v: timed out DAO fork-check, dropping", p) pm.removePeer(p.id) }) // Make sure it's cleaned up if the peer dies off @@ -375,14 +379,24 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case query.Origin.Hash != (common.Hash{}) && !query.Reverse: // Hash based traversal towards the leaf block - if header := pm.blockchain.GetHeaderByNumber(origin.Number.Uint64() + query.Skip + 1); header != nil { - if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { - query.Origin.Hash = header.Hash() + var ( + current = origin.Number.Uint64() + next = current + query.Skip + 1 + ) + if next <= current { + infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ") + glog.V(logger.Warn).Infof("%v: GetBlockHeaders skip overflow attack (current %v, skip %v, next %v)\nMalicious peer infos: %s", p, current, query.Skip, next, infos) + unknown = true + } else { + if header := pm.blockchain.GetHeaderByNumber(next); header != nil { + if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { + query.Origin.Hash = header.Hash() + } else { + unknown = true + } } else { unknown = true } - } else { - unknown = true } case query.Reverse: // Number based traversal towards the genesis block @@ -638,9 +652,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } - if err := request.Block.ValidateFields(); err != nil { - return errResp(ErrDecode, "block validation %v: %v", msg, err) - } request.Block.ReceivedAt = msg.ReceivedAt request.Block.ReceivedFrom = p @@ -684,7 +695,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } p.MarkTransaction(tx.Hash()) } - pm.txpool.AddTransactions(txs) + pm.txpool.AddBatch(txs) default: return errResp(ErrInvalidMsgCode, "%v", msg.Code) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/protocol.go b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go index 69b3be578..3f65c204b 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/protocol.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go @@ -97,12 +97,12 @@ var errorToString = map[int]string{ } type txPool interface { - // AddTransactions should add the given transactions to the pool. - AddTransactions([]*types.Transaction) + // AddBatch should add the given transactions to the pool. + AddBatch([]*types.Transaction) - // GetTransactions should return pending transactions. + // Pending should return pending transactions. // The slice should be modifiable by the caller. - GetTransactions() types.Transactions + Pending() map[common.Address]types.Transactions } // statusData is the network packet for the status message. diff --git a/vendor/github.com/ethereum/go-ethereum/eth/sync.go b/vendor/github.com/ethereum/go-ethereum/eth/sync.go index e1946edda..6584bb1e2 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/sync.go @@ -45,7 +45,10 @@ type txsync struct { // syncTransactions starts sending all currently pending transactions to the given peer. func (pm *ProtocolManager) syncTransactions(p *peer) { - txs := pm.txpool.GetTransactions() + var txs types.Transactions + for _, batch := range pm.txpool.Pending() { + txs = append(txs, batch...) + } if len(txs) == 0 { return } diff --git a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go new file mode 100644 index 000000000..a095aa076 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go @@ -0,0 +1,419 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package ethclient provides a client for the Ethereum RPC API. +package ethclient + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// Client defines typed wrappers for the Ethereum RPC API. +type Client struct { + c *rpc.Client +} + +// Dial connects a client to the given URL. +func Dial(rawurl string) (*Client, error) { + c, err := rpc.Dial(rawurl) + if err != nil { + return nil, err + } + return NewClient(c), nil +} + +// NewClient creates a client that uses the given RPC client. +func NewClient(c *rpc.Client) *Client { + return &Client{c} +} + +// Blockchain Access + +// BlockByHash returns the given full block. +// +// Note that loading full blocks requires two requests. Use HeaderByHash +// if you don't need all transactions or uncle headers. +func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) +} + +// BlockByNumber returns a block from the current canonical chain. If number is nil, the +// latest known block is returned. +// +// Note that loading full blocks requires two requests. Use HeaderByNumber +// if you don't need all transactions or uncle headers. +func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { + return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true) +} + +type rpcBlock struct { + Hash common.Hash `json:"hash"` + Transactions []*types.Transaction `json:"transactions"` + UncleHashes []common.Hash `json:"uncles"` +} + +func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { + var raw json.RawMessage + err := ec.c.CallContext(ctx, &raw, method, args...) + if err != nil { + return nil, err + } + // Decode header and transactions. + var head *types.Header + var body rpcBlock + if err := json.Unmarshal(raw, &head); err != nil { + return nil, err + } + if err := json.Unmarshal(raw, &body); err != nil { + return nil, err + } + // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. + if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { + return nil, fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles") + } + if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { + return nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles") + } + if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 { + return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions") + } + if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 { + return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions") + } + // Load uncles because they are not included in the block response. + var uncles []*types.Header + if len(body.UncleHashes) > 0 { + uncles = make([]*types.Header, len(body.UncleHashes)) + reqs := make([]rpc.BatchElem, len(body.UncleHashes)) + for i := range reqs { + reqs[i] = rpc.BatchElem{ + Method: "eth_getUncleByBlockHashAndIndex", + Args: []interface{}{body.Hash, fmt.Sprintf("%#x", i)}, + Result: &uncles[i], + } + } + if err := ec.c.BatchCallContext(ctx, reqs); err != nil { + return nil, err + } + for i := range reqs { + if reqs[i].Error != nil { + return nil, reqs[i].Error + } + } + } + return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil +} + +// HeaderByHash returns the block header with the given hash. +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) + return head, err +} + +// HeaderByNumber returns a block header from the current canonical chain. If number is +// nil, the latest known header is returned. +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) + 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") + } + } + return tx, err +} + +// 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 + err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash) + return num.Uint(), err +} + +// TransactionInBlock returns a single transaction at index in the given block. +func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { + 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 { + return nil, fmt.Errorf("server returned transaction without signature") + } + } + return tx, err +} + +// TransactionReceipt returns the receipt of a transaction by transaction hash. +// Note that the receipt is not available for pending transactions. +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") + } + return r, err +} + +func toBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + return fmt.Sprintf("%#x", number) +} + +type rpcProgress struct { + StartingBlock rpc.HexNumber + CurrentBlock rpc.HexNumber + HighestBlock rpc.HexNumber + PulledStates rpc.HexNumber + KnownStates rpc.HexNumber +} + +// SyncProgress retrieves the current progress of the sync algorithm. If there's +// no sync currently running, it returns nil. +func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { + var raw json.RawMessage + if err := ec.c.CallContext(ctx, &raw, "eth_syncing"); err != nil { + return nil, err + } + // Handle the possible response types + var syncing bool + if err := json.Unmarshal(raw, &syncing); err == nil { + return nil, nil // Not syncing (always false) + } + var progress *rpcProgress + if err := json.Unmarshal(raw, &progress); err != nil { + 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(), + }, nil +} + +// SubscribeNewHead subscribes to notifications about the current blockchain head +// on the given channel. +func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { + return ec.c.EthSubscribe(ctx, ch, "newHeads", map[string]struct{}{}) +} + +// State Access + +// 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 + err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber)) + return (*big.Int)(&result), err +} + +// 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 + err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber)) + return result, err +} + +// 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 + err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber)) + return result, err +} + +// 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 + err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber)) + return result.Uint64(), err +} + +// Filters + +// FilterLogs executes a filter query. +func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]vm.Log, error) { + var result []vm.Log + err := ec.c.CallContext(ctx, &result, "eth_getLogs", toFilterArg(q)) + return result, err +} + +// SubscribeFilterLogs subscribes to the results of a streaming filter query. +func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- vm.Log) (ethereum.Subscription, error) { + return ec.c.EthSubscribe(ctx, ch, "logs", toFilterArg(q)) +} + +func toFilterArg(q ethereum.FilterQuery) interface{} { + arg := map[string]interface{}{ + "fromBlock": toBlockNumArg(q.FromBlock), + "toBlock": toBlockNumArg(q.ToBlock), + "addresses": q.Addresses, + "topics": q.Topics, + } + if q.FromBlock == nil { + arg["fromBlock"] = "0x0" + } + return arg +} + +// Pending State + +// 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 + 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 + 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 + err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending") + return result, err +} + +// 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 + err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending") + return result.Uint64(), 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 + err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending") + return num.Uint(), err +} + +// TODO: SubscribePendingTransactions (needs server side) + +// Contract Calling + +// CallContract executes a message call transaction, which is directly executed in the VM +// of the node, but never mined into the blockchain. +// +// blockNumber selects the block height at which the call runs. It can be nil, in which +// 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 + err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber)) + if err != nil { + return nil, err + } + return common.FromHex(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 + err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending") + if err != nil { + return nil, err + } + return common.FromHex(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 + if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + +// EstimateGas tries to estimate the gas needed to execute a specific transaction based on +// the current pending state of the backend blockchain. There is no guarantee that this is +// 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 + err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) + if err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + +// SendTransaction injects a signed transaction into the pending pool for execution. +// +// If the transaction was a contract creation use the TransactionReceipt method to get the +// contract address after the transaction has been mined. +func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return err + } + return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) +} + +func toCallArg(msg ethereum.CallMsg) interface{} { + arg := map[string]interface{}{ + "from": msg.From, + "to": msg.To, + } + if len(msg.Data) > 0 { + arg["data"] = fmt.Sprintf("%#x", msg.Data) + } + if msg.Value != nil { + arg["value"] = fmt.Sprintf("%#x", msg.Value) + } + if msg.Gas != nil { + arg["gas"] = fmt.Sprintf("%#x", msg.Gas) + } + if msg.GasPrice != nil { + arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice) + } + return arg +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/database.go b/vendor/github.com/ethereum/go-ethereum/ethdb/database.go index a12ec4ceb..96d9a5982 100644 --- a/vendor/github.com/ethereum/go-ethereum/ethdb/database.go +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/database.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" @@ -86,6 +87,7 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { OpenFilesCacheCapacity: handles, BlockCacheCapacity: cache / 2 * opt.MiB, WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally + Filter: filter.NewBloomFilter(10), }) if _, corrupted := err.(*errors.ErrCorrupted); corrupted { db, err = leveldb.RecoverFile(file, nil) @@ -100,6 +102,11 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { }, nil } +// Path returns the path to the database directory. +func (db *LDBDatabase) Path() string { + return db.fn +} + // Put puts the given key / value to the queue func (self *LDBDatabase) Put(key []byte, value []byte) error { // Measure the database put latency, if requested diff --git a/vendor/github.com/ethereum/go-ethereum/interfaces.go b/vendor/github.com/ethereum/go-ethereum/interfaces.go new file mode 100644 index 000000000..aab0e2029 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/interfaces.go @@ -0,0 +1,184 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package ethereum defines interfaces for interacting with Ethereum. +package ethereum + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "golang.org/x/net/context" +) + +// TODO: move subscription to package event + +// Subscription represents an event subscription where events are +// delivered on a data channel. +type Subscription interface { + // Unsubscribe cancels the sending of events to the data channel + // and closes the error channel. + Unsubscribe() + // Err returns the subscription error channel. The error channel receives + // a value if there is an issue with the subscription (e.g. the network connection + // delivering the events has been closed). Only one value will ever be sent. + // The error channel is closed by Unsubscribe. + Err() <-chan error +} + +// ChainReader provides access to the blockchain. The methods in this interface access raw +// data from either the canonical chain (when requesting by block number) or any +// 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. +type ChainReader interface { + BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + 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) + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) +} + +// ChainStateReader wraps access to the state trie of the canonical blockchain. Note that +// implementations of the interface may be unable to return state values for old blocks. +// In many cases, using CallContract can be preferable to reading raw contract storage. +type ChainStateReader interface { + BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) +} + +// SyncProgress gives progress indications when the node is synchronising with +// the Ethereum network. +type SyncProgress struct { + StartingBlock uint64 // Block number where sync began + CurrentBlock uint64 // Current block number where sync is at + HighestBlock uint64 // Highest alleged block number in the chain + PulledStates uint64 // Number of state trie entries already downloaded + KnownStates uint64 // Total number os state trie entries known about +} + +// ChainSyncReader wraps access to the node's current sync status. If there's no +// sync currently running, it returns nil. +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' + To *common.Address // the destination contract (nil for contract creation) + Gas *big.Int // if nil, the call executes with near-infinite gas + GasPrice *big.Int // wei <-> gas exchange ratio + Value *big.Int // amount of wei sent along with the call + Data []byte // input data, usually an ABI-encoded contract method invocation +} + +// A ContractCaller provides contract calls, essentially transactions that are executed by +// the EVM but not mined into the blockchain. ContractCall is a low-level method to +// execute such calls. For applications which are structured around specific contracts, +// the abigen tool provides a nicer, properly typed way to perform calls. +type ContractCaller interface { + CallContract(ctx context.Context, call CallMsg, blockNumber *big.Int) ([]byte, error) +} + +// FilterQuery contains options for contact log filtering. +type FilterQuery struct { + FromBlock *big.Int // beginning of the queried range, nil means genesis block + ToBlock *big.Int // end of the range, nil means latest block + Addresses []common.Address // restricts matches to events created by specific contracts + + // The Topic list restricts matches to particular event topics. Each event has a list + // of topics. Topics matches a prefix of that list. An empty element slice matches any + // topic. Non-empty elements represent an alternative that matches any of the + // contained topics. + // + // Examples: + // {} or nil matches any topic list + // {{A}} matches topic A in first position + // {{}, {B}} matches any topic in first position, B in second position + // {{A}}, {B}} matches topic A in first position, B in second position + // {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position + Topics [][]common.Hash +} + +// LogFilterer provides access to contract log events using a one-off query or continuous +// event subscription. +type LogFilterer interface { + FilterLogs(ctx context.Context, q FilterQuery) ([]vm.Log, error) + SubscribeFilterLogs(ctx context.Context, q FilterQuery, ch chan<- vm.Log) (Subscription, error) +} + +// TransactionSender wraps transaction sending. The SendTransaction method injects a +// signed transaction into the pending transaction pool for execution. If the transaction +// was a contract creation, the TransactionReceipt method can be used to retrieve the +// contract address after the transaction has been mined. +// +// The transaction must be signed and have a valid nonce to be included. Consumers of the +// API can use package accounts to maintain local private keys and need can retrieve the +// next available nonce using PendingNonceAt. +type TransactionSender interface { + SendTransaction(ctx context.Context, tx *types.Transaction) error +} + +// GasPricer wraps the gas price oracle, which monitors the blockchain to determine the +// optimal gas price given current fee market conditions. +type GasPricer interface { + SuggestGasPrice(ctx context.Context) (*big.Int, error) +} + +// A PendingStateReader provides access to the pending state, which is the result of all +// known executable transactions which have not yet been included in the blockchain. It is +// commonly used to display the result of ’unconfirmed’ actions (e.g. wallet value +// transfers) initiated by the user. The PendingNonceAt operation is a good way to +// retrieve the next available transaction nonce for a specific account. +type PendingStateReader interface { + PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) + PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + PendingTransactionCount(ctx context.Context) (uint, error) +} + +// PendingContractCaller can be used to perform calls against the pending state. +type PendingContractCaller interface { + PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error) +} + +// GasEstimator wraps EstimateGas, which tries to estimate the gas needed to execute a +// specific transaction based on the pending state. There is no guarantee that this is 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. +type GasEstimator interface { + EstimateGas(ctx context.Context, call CallMsg) (usedGas *big.Int, err error) +} + +// A PendingStateEventer provides access to real time notifications about changes to the +// pending state. +type PendingStateEventer interface { + SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go index 9fc5fc4fe..d7bbfae1e 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go @@ -52,6 +52,11 @@ var ( Usage: "pprof HTTP server listening port", Value: 6060, } + pprofAddrFlag = cli.StringFlag{ + Name: "pprofaddr", + Usage: "pprof HTTP server listening interface", + Value: "127.0.0.1", + } memprofilerateFlag = cli.IntFlag{ Name: "memprofilerate", Usage: "Turn on memory profiling with the given rate", @@ -74,7 +79,7 @@ var ( // Flags holds all command-line flags required for debugging. var Flags = []cli.Flag{ verbosityFlag, vmoduleFlag, backtraceAtFlag, - pprofFlag, pprofPortFlag, + pprofFlag, pprofAddrFlag, pprofPortFlag, memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag, } @@ -101,7 +106,7 @@ func Setup(ctx *cli.Context) error { // pprof server if ctx.GlobalBool(pprofFlag.Name) { - address := fmt.Sprintf("127.0.0.1:%d", ctx.GlobalInt(pprofPortFlag.Name)) + address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name)) go func() { glog.V(logger.Info).Infof("starting pprof server at http://%s/debug/pprof", address) glog.Errorln(http.ListenAndServe(address, nil)) diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go index 24b0d2482..89a17255c 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -20,7 +20,6 @@ import ( "bytes" "encoding/hex" "encoding/json" - "errors" "fmt" "math/big" "strings" @@ -43,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/pborman/uuid" "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" "golang.org/x/net/context" ) @@ -77,19 +77,19 @@ func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber { // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled func (s *PublicEthereumAPI) Syncing() (interface{}, error) { - origin, current, height, pulled, known := s.b.Downloader().Progress() + progress := s.b.Downloader().Progress() // Return not syncing if the synchronisation already completed - if current >= height { + if progress.CurrentBlock >= progress.HighestBlock { return false, nil } // Otherwise gather the block sync stats return map[string]interface{}{ - "startingBlock": rpc.NewHexNumber(origin), - "currentBlock": rpc.NewHexNumber(current), - "highestBlock": rpc.NewHexNumber(height), - "pulledStates": rpc.NewHexNumber(pulled), - "knownStates": rpc.NewHexNumber(known), + "startingBlock": rpc.NewHexNumber(progress.StartingBlock), + "currentBlock": rpc.NewHexNumber(progress.CurrentBlock), + "highestBlock": rpc.NewHexNumber(progress.HighestBlock), + "pulledStates": rpc.NewHexNumber(progress.PulledStates), + "knownStates": rpc.NewHexNumber(progress.KnownStates), }, nil } @@ -104,32 +104,26 @@ func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI { } // Content returns the transactions contained within the transaction pool. -func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string][]*RPCTransaction { - content := map[string]map[string]map[string][]*RPCTransaction{ - "pending": make(map[string]map[string][]*RPCTransaction), - "queued": make(map[string]map[string][]*RPCTransaction), +func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { + content := map[string]map[string]map[string]*RPCTransaction{ + "pending": make(map[string]map[string]*RPCTransaction), + "queued": make(map[string]map[string]*RPCTransaction), } pending, queue := s.b.TxPoolContent() // Flatten the pending transactions - for account, batches := range pending { - dump := make(map[string][]*RPCTransaction) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) - } + for account, txs := range pending { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) } content["pending"][account.Hex()] = dump } // Flatten the queued transactions - for account, batches := range queue { - dump := make(map[string][]*RPCTransaction) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) - } + for account, txs := range queue { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) } content["queued"][account.Hex()] = dump } @@ -147,10 +141,10 @@ func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { // Inspect retrieves the content of the transaction pool and flattens it into an // easily inspectable list. -func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string { - content := map[string]map[string]map[string][]string{ - "pending": make(map[string]map[string][]string), - "queued": make(map[string]map[string][]string), +func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string { + content := map[string]map[string]map[string]string{ + "pending": make(map[string]map[string]string), + "queued": make(map[string]map[string]string), } pending, queue := s.b.TxPoolContent() @@ -162,24 +156,18 @@ func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string { return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) } // Flatten the pending transactions - for account, batches := range pending { - dump := make(map[string][]string) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], format(tx)) - } + for account, txs := range pending { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) } content["pending"][account.Hex()] = dump } // Flatten the queued transactions - for account, batches := range queue { - dump := make(map[string][]string) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], format(tx)) - } + for account, txs := range queue { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) } content["queued"][account.Hex()] = dump } @@ -306,7 +294,8 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes()) + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + signature, err := s.am.SignWithPassphrase(args.From, passwd, signer.Hash(tx).Bytes()) if err != nil { return common.Hash{}, err } @@ -314,6 +303,66 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs return submitTransaction(ctx, s.b, tx, signature) } +// signHash is a helper function that calculates a hash for the given message that can be +// safely used to calculate a signature from. The hash is calulcated with: +// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). +func signHash(message string) []byte { + data := common.FromHex(message) + // Give context to the signed message. This prevents an adversery to sign a tx. + // It has no cryptographic purpose. + msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) + // Always hash, this prevents choosen plaintext attacks that can extract key information + return crypto.Keccak256([]byte(msg)) +} + +// Sign calculates an Ethereum ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)) +// +// The key used to calculate the signature is decrypted with the given password. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign +func (s *PrivateAccountAPI) Sign(ctx context.Context, message string, addr common.Address, passwd string) (string, error) { + hash := signHash(message) + signature, err := s.b.AccountManager().SignWithPassphrase(addr, passwd, hash) + if err != nil { + return "0x", err + } + return common.ToHex(signature), nil +} + +// EcRecover returns the address for the account that was used to create the signature. +// Note, this function is compatible with eth_sign and personal_sign. As such it recovers +// the address of: +// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) +// addr = ecrecover(hash, signature) +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover +func (s *PrivateAccountAPI) EcRecover(ctx context.Context, message string, signature string) (common.Address, error) { + var ( + hash = signHash(message) + sig = common.FromHex(signature) + ) + + if len(sig) != 65 { + return common.Address{}, fmt.Errorf("signature must be 65 bytes long") + } + + // see crypto.Ecrecover description + if sig[64] == 27 || sig[64] == 28 { + sig[64] -= 27 + } + + rpk, err := crypto.Ecrecover(hash, sig) + if err != nil { + return common.Address{}, err + } + + pubKey := crypto.ToECDSAPub(rpk) + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + + return recoveredAddr, nil +} + // SignAndSendTransaction was renamed to SendTransaction. This method is deprecated // and will be removed in the future. It primary goal is to give clients time to update. func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { @@ -483,6 +532,8 @@ type CallArgs struct { } func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { + defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) + state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { return "0x", common.Big0, err @@ -502,22 +553,14 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr } // Assemble the CALL invocation - msg := callmsg{ - addr: addr, - to: args.To, - gas: args.Gas.BigInt(), - gasPrice: args.GasPrice.BigInt(), - value: args.Value.BigInt(), - data: common.FromHex(args.Data), + gas, gasPrice := args.Gas.BigInt(), args.GasPrice.BigInt() + if gas.Cmp(common.Big0) == 0 { + gas = big.NewInt(50000000) } - - if msg.gas.Cmp(common.Big0) == 0 { - msg.gas = big.NewInt(50000000) - } - - if msg.gasPrice.Cmp(common.Big0) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + if gasPrice.Cmp(common.Big0) == 0 { + gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) } + msg := types.NewMessage(addr, args.To, 0, args.Value.BigInt(), gas, gasPrice, common.FromHex(args.Data), false) // Execute the call and return vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) @@ -601,82 +644,30 @@ func FormatLogs(structLogs []vm.StructLog) []StructLogRes { return formattedStructLogs } -// TraceCall executes a call and returns the amount of gas, created logs and optionally returned values. -func (s *PublicBlockChainAPI) TraceCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) { - state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, err - } - - var addr common.Address - if args.From == (common.Address{}) { - accounts := s.b.AccountManager().Accounts() - if len(accounts) == 0 { - addr = common.Address{} - } else { - addr = accounts[0].Address - } - } else { - addr = args.From - } - - // Assemble the CALL invocation - msg := callmsg{ - addr: addr, - to: args.To, - gas: args.Gas.BigInt(), - gasPrice: args.GasPrice.BigInt(), - value: args.Value.BigInt(), - data: common.FromHex(args.Data), - } - - if msg.gas.Cmp(common.Big0) == 0 { - msg.gas = big.NewInt(50000000) - } - - if msg.gasPrice.Cmp(common.Big0) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) - } - - // Execute the call and return - vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) - if err != nil { - return nil, err - } - gp := new(core.GasPool).AddGas(common.MaxBig) - ret, gas, err := core.ApplyMessage(vmenv, msg, gp) - if err := vmError(); err != nil { - return nil, err - } - return &ExecutionResult{ - Gas: gas, - ReturnValue: fmt.Sprintf("%x", ret), - StructLogs: FormatLogs(vmenv.StructLogs()), - }, nil -} - // rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { + head := b.Header() // copies the header once fields := map[string]interface{}{ - "number": rpc.NewHexNumber(b.Number()), + "number": rpc.NewHexNumber(head.Number), "hash": b.Hash(), - "parentHash": b.ParentHash(), - "nonce": b.Header().Nonce, - "sha3Uncles": b.UncleHash(), - "logsBloom": b.Bloom(), - "stateRoot": b.Root(), - "miner": b.Coinbase(), - "difficulty": rpc.NewHexNumber(b.Difficulty()), + "parentHash": head.ParentHash, + "nonce": head.Nonce, + "mixHash": head.MixDigest, + "sha3Uncles": head.UncleHash, + "logsBloom": head.Bloom, + "stateRoot": head.Root, + "miner": head.Coinbase, + "difficulty": rpc.NewHexNumber(head.Difficulty), "totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())), - "extraData": fmt.Sprintf("0x%x", b.Extra()), + "extraData": rpc.HexBytes(head.Extra), "size": rpc.NewHexNumber(b.Size().Int64()), - "gasLimit": rpc.NewHexNumber(b.GasLimit()), - "gasUsed": rpc.NewHexNumber(b.GasUsed()), - "timestamp": rpc.NewHexNumber(b.Time()), - "transactionsRoot": b.TxHash(), - "receiptRoot": b.ReceiptHash(), + "gasLimit": rpc.NewHexNumber(head.GasLimit), + "gasUsed": rpc.NewHexNumber(head.GasUsed), + "timestamp": rpc.NewHexNumber(head.Time), + "transactionsRoot": head.TxHash, + "receiptsRoot": head.ReceiptHash, } if inclTx { @@ -719,26 +710,36 @@ type RPCTransaction struct { Gas *rpc.HexNumber `json:"gas"` GasPrice *rpc.HexNumber `json:"gasPrice"` Hash common.Hash `json:"hash"` - Input string `json:"input"` + Input rpc.HexBytes `json:"input"` Nonce *rpc.HexNumber `json:"nonce"` To *common.Address `json:"to"` TransactionIndex *rpc.HexNumber `json:"transactionIndex"` Value *rpc.HexNumber `json:"value"` + V *rpc.HexNumber `json:"v"` + R *rpc.HexNumber `json:"r"` + S *rpc.HexNumber `json:"s"` } // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { - from, _ := tx.FromFrontier() - + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) + v, r, s := types.SignatureValues(signer, tx) return &RPCTransaction{ From: from, Gas: rpc.NewHexNumber(tx.Gas()), GasPrice: rpc.NewHexNumber(tx.GasPrice()), Hash: tx.Hash(), - Input: fmt.Sprintf("0x%x", tx.Data()), + Input: rpc.HexBytes(tx.Data()), Nonce: rpc.NewHexNumber(tx.Nonce()), To: tx.To(), Value: rpc.NewHexNumber(tx.Value()), + V: rpc.NewHexNumber(v), + R: rpc.NewHexNumber(r), + S: rpc.NewHexNumber(s), } } @@ -746,11 +747,12 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransaction, error) { if txIndex >= 0 && txIndex < len(b.Transactions()) { tx := b.Transactions()[txIndex] - from, err := tx.FromFrontier() - if err != nil { - return nil, err + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) } - + from, _ := types.Sender(signer, tx) + v, r, s := tx.RawSignatureValues() return &RPCTransaction{ BlockHash: b.Hash(), BlockNumber: rpc.NewHexNumber(b.Number()), @@ -758,17 +760,30 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti Gas: rpc.NewHexNumber(tx.Gas()), GasPrice: rpc.NewHexNumber(tx.GasPrice()), Hash: tx.Hash(), - Input: fmt.Sprintf("0x%x", tx.Data()), + Input: rpc.HexBytes(tx.Data()), Nonce: rpc.NewHexNumber(tx.Nonce()), To: tx.To(), TransactionIndex: rpc.NewHexNumber(txIndex), Value: rpc.NewHexNumber(tx.Value()), + V: rpc.NewHexNumber(v), + R: rpc.NewHexNumber(r), + S: rpc.NewHexNumber(s), }, nil } return nil, nil } +// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. +func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex int) (rpc.HexBytes, error) { + if txIndex >= 0 && txIndex < len(b.Transactions()) { + tx := b.Transactions()[txIndex] + return rlp.EncodeToBytes(tx) + } + + return nil, nil +} + // newRPCTransaction returns a transaction that will serialize to the RPC representation. func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) { for idx, tx := range b.Transactions() { @@ -854,6 +869,22 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context return nil, nil } +// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (rpc.HexBytes, error) { + if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + return newRPCRawTransactionFromBlockIndex(block, index.Int()) + } + return nil, nil +} + +// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (rpc.HexBytes, error) { + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + return newRPCRawTransactionFromBlockIndex(block, index.Int()) + } + return nil, nil +} + // GetTransactionCount returns the number of transactions the given address has sent for the given block number func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) @@ -919,9 +950,23 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txH return nil, nil } +// GetRawTransactionByHash returns the bytes of the transaction for the given hash. +func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (rpc.HexBytes, error) { + var tx *types.Transaction + var err error + + if tx, _, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } else if tx == nil { + return nil, nil + } + + return rlp.EncodeToBytes(tx) +} + // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { - //fmt.Println("API GetTransactionReceipt", txHash) receipt := core.GetReceipt(s.b.ChainDb(), txHash) if receipt == nil { glog.V(logger.Debug).Infof("receipt not found for transaction %s", txHash.Hex()) @@ -929,27 +974,25 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma } tx, _, err := getTransaction(s.b.ChainDb(), s.b, txHash) - //fmt.Println("getTransaction", err) if err != nil { glog.V(logger.Debug).Infof("%v\n", err) return nil, nil } txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), txHash) - //fmt.Println("getTransactionBlockData", txBlock, blockIndex, index, err) if err != nil { glog.V(logger.Debug).Infof("%v\n", err) return nil, nil } - from, err := tx.FromFrontier() - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) } + from, _ := types.Sender(signer, tx) fields := map[string]interface{}{ - "root": common.Bytes2Hex(receipt.PostState), + "root": rpc.HexBytes(receipt.PostState), "blockHash": txBlock, "blockNumber": rpc.NewHexNumber(blockIndex), "transactionHash": txHash, @@ -960,27 +1003,27 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma "cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed), "contractAddress": nil, "logs": receipt.Logs, + "logsBloom": receipt.Bloom, } - if receipt.Logs == nil { fields["logs"] = []vm.Logs{} } - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation - if bytes.Compare(receipt.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 { + if receipt.ContractAddress != (common.Address{}) { fields["contractAddress"] = receipt.ContractAddress } - return fields, nil } // sign is a helper function that signs a transaction with the private key of the given address. func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - signature, err := s.b.AccountManager().Sign(addr, tx.SigHash().Bytes()) + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + + signature, err := s.b.AccountManager().SignEthereum(addr, signer.Hash(tx).Bytes()) if err != nil { return nil, err } - return tx.WithSignature(signature) + return tx.WithSignature(signer, signature) } // SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. @@ -1014,7 +1057,9 @@ func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxA // submitTransaction is a helper function that submits tx to txPool and creates a log entry. func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, signature []byte) (common.Hash, error) { - signedTx, err := tx.WithSignature(signature) + signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) + + signedTx, err := tx.WithSignature(signer, signature) if err != nil { return common.Hash{}, err } @@ -1024,7 +1069,7 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, si } if signedTx.To() == nil { - from, _ := signedTx.From() + from, _ := types.Sender(signer, signedTx) addr := crypto.CreateAddress(from, signedTx.Nonce()) glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) } else { @@ -1046,23 +1091,27 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen Context: ctx, Args: status.SendTxArgs(args), Done: make(chan struct{}, 1), + Discard: make(chan struct{}, 1), } // send transaction to pending pool s.txQueue <- queuedTx - // now wait up until transaction is complete (via call to CompleteQueuedTransaction) or timeout occurs - timeout := make(chan struct{}, 1) - go func() { - time.Sleep(status.DefaultTxSendCompletionTimeout * time.Second) - timeout <- struct{}{} - }() - + // now wait up until transaction is: + // - completed (via CompleteQueuedTransaction), + // - discarded (via DiscardQueuedTransaction) + // - or times out + backend := GetStatusBackend() select { case <-queuedTx.Done: + backend.NotifyOnQueuedTxReturn(queuedTx, queuedTx.Err) return queuedTx.Hash, queuedTx.Err - case <-timeout: - return common.Hash{}, errors.New("transaction sending timed out") + case <-queuedTx.Discard: + backend.NotifyOnQueuedTxReturn(queuedTx, status.ErrQueuedTxDiscarded) + return queuedTx.Hash, queuedTx.Err + case <-time.After(status.DefaultTxSendCompletionTimeout * time.Second): + backend.NotifyOnQueuedTxReturn(queuedTx, status.ErrQueuedTxTimedOut) + return common.Hash{}, status.ErrQueuedTxTimedOut } return queuedTx.Hash, nil @@ -1092,7 +1141,8 @@ func (s *PublicTransactionPoolAPI) CompleteQueuedTransaction(ctx context.Context tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signature, err := s.b.AccountManager().SignWithPassphrase(args.From, passphrase, tx.SigHash().Bytes()) + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + signature, err := s.b.AccountManager().SignWithPassphrase(args.From, passphrase, tx.SigHash(signer).Bytes()) if err != nil { return common.Hash{}, err } @@ -1112,8 +1162,9 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return "", err } + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) if tx.To() == nil { - from, err := tx.FromFrontier() + from, err := types.Sender(signer, tx) if err != nil { return "", err } @@ -1126,11 +1177,16 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return tx.Hash().Hex(), nil } -// Sign signs the given hash using the key that matches the address. The key must be -// unlocked in order to sign the hash. -func (s *PublicTransactionPoolAPI) Sign(addr common.Address, hash common.Hash) (string, error) { - signature, error := s.b.AccountManager().Sign(addr, hash[:]) - return common.ToHex(signature), error +// Sign calculates an ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). +// +// The account associated with addr must be unlocked. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign +func (s *PublicTransactionPoolAPI) Sign(addr common.Address, message string) (string, error) { + hash := signHash(message) + signature, err := s.b.AccountManager().SignEthereum(addr, hash) + return common.ToHex(signature), err } // SignTransactionArgs represents the arguments to sign a transaction. @@ -1217,7 +1273,12 @@ type SignTransactionResult struct { } func newTx(t *types.Transaction) *Tx { - from, _ := t.FromFrontier() + var signer types.Signer = types.HomesteadSigner{} + if t.Protected() { + signer = types.NewEIP155Signer(t.ChainId()) + } + + from, _ := types.Sender(signer, t) return &Tx{ tx: t, To: t.To(), @@ -1283,7 +1344,11 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { pending := s.b.GetPoolTransactions() transactions := make([]*RPCTransaction, 0, len(pending)) for _, tx := range pending { - from, _ := tx.FromFrontier() + var signer types.Signer = types.HomesteadSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) if s.b.AccountManager().HasAddress(from) { transactions = append(transactions, newRPCPendingTransaction(tx)) } @@ -1293,11 +1358,15 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { // Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the // pool and reinsert it with the new gas price and limit. -func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx *Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { - +func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { pending := s.b.GetPoolTransactions() for _, p := range pending { - if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { + var signer types.Signer = types.HomesteadSigner{} + if p.Protected() { + signer = types.NewEIP155Signer(p.ChainId()) + } + + if pFrom, err := types.Sender(signer, p); err == nil && pFrom == tx.From && signer.Hash(p) == signer.Hash(tx.tx) { if gasPrice == nil { gasPrice = rpc.NewHexNumber(tx.tx.GasPrice()) } @@ -1307,9 +1376,9 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx *Tx, gasPrice, var newTx *types.Transaction if tx.tx.To() == nil { - newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasLimit.BigInt(), gasPrice.BigInt(), tx.tx.Data()) } else { - newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasLimit.BigInt(), gasPrice.BigInt(), tx.tx.Data()) } signedTx, err := s.sign(tx.From, newTx) @@ -1404,9 +1473,27 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { return ldb.LDB().GetProperty(property) } +func (api *PrivateDebugAPI) ChaindbCompact() error { + ldb, ok := api.b.ChainDb().(interface { + LDB() *leveldb.DB + }) + if !ok { + return fmt.Errorf("chaindbCompact does not work for memory databases") + } + for b := byte(0); b < 255; b++ { + glog.V(logger.Info).Infof("compacting chain DB range 0x%0.2X-0x%0.2X", b, b+1) + err := ldb.LDB().CompactRange(util.Range{Start: []byte{b}, Limit: []byte{b + 1}}) + if err != nil { + glog.Errorf("compaction error: %v", err) + return err + } + } + return nil +} + // SetHead rewinds the head of the blockchain to a previous block. -func (api *PrivateDebugAPI) SetHead(number uint64) { - api.b.SetHead(number) +func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { + api.b.SetHead(uint64(number.Int64())) } // PublicNetAPI offers network related RPC methods diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go index 8427b3e03..fdc4a39dc 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// 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 @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -58,7 +59,10 @@ type Backend interface { GetPoolTransaction(txHash common.Hash) *types.Transaction GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) Stats() (pending int, queued int) - TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) + TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) + + ChainConfig() *params.ChainConfig + CurrentBlock() *types.Block } type State interface { diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go index 31409350c..b72e41d69 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/status_backend.go @@ -3,6 +3,7 @@ package ethapi import ( "sync" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/les/status" "github.com/ethereum/go-ethereum/logger" @@ -46,6 +47,18 @@ func GetStatusBackend() *StatusBackend { return statusBackend } +func (b *StatusBackend) NotifyOnQueuedTxReturn(queuedTx *status.QueuedTx, err error) { + if b == nil { + return + } + + b.txQueue.NotifyOnQueuedTxReturn(queuedTx, err) +} + +func (b *StatusBackend) SetTransactionReturnHandler(fn status.EnqueuedTxReturnHandler) { + b.txQueue.SetTxReturnHandler(fn) +} + func (b *StatusBackend) SetTransactionQueueHandler(fn status.EnqueuedTxHandler) { b.txQueue.SetEnqueueHandler(fn) } @@ -79,6 +92,14 @@ func (b *StatusBackend) CompleteQueuedTransaction(id status.QueuedTxId, passphra } hash, err := b.txapi.CompleteQueuedTransaction(context.Background(), SendTxArgs(queuedTx.Args), passphrase) + + // on password error, notify the app, and keep tx in queue (so that CompleteQueuedTransaction() can be resent) + if err == accounts.ErrDecrypt { + b.NotifyOnQueuedTxReturn(queuedTx, err) + return hash, err // SendTransaction is still blocked + } + + // allow SendTransaction to return queuedTx.Hash = hash queuedTx.Err = err queuedTx.Done <- struct{}{} // sendTransaction() waits on this, notify so that it can return @@ -86,6 +107,23 @@ func (b *StatusBackend) CompleteQueuedTransaction(id status.QueuedTxId, passphra return hash, err } +// DiscardQueuedTransaction discards queued transaction forcing SendTransaction to return +func (b *StatusBackend) DiscardQueuedTransaction(id status.QueuedTxId) error { + queuedTx, err := b.txQueue.Get(id) + if err != nil { + return err + } + + // remove from queue, before notifying SendTransaction + b.TransactionQueue().Remove(queuedTx.Id) + + // allow SendTransaction to return + queuedTx.Err = status.ErrQueuedTxDiscarded + queuedTx.Discard <- struct{}{} // sendTransaction() waits on this, notify so that it can return + + return nil +} + func (b *StatusBackend) transactionQueueForwardingLoop() { txQueue, err := b.txapi.GetTransactionQueue() if err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go new file mode 100644 index 000000000..5f69826a3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go @@ -0,0 +1,318 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/robertkrimen/otto" +) + +// fakeBig is used to provide an interface to Javascript for 'big.NewInt' +type fakeBig struct{} + +// NewInt creates a new big.Int with the specified int64 value. +func (fb *fakeBig) NewInt(x int64) *big.Int { + return big.NewInt(x) +} + +// OpCodeWrapper provides a JavaScript-friendly wrapper around OpCode, to convince Otto to treat it +// as an object, instead of a number. +type opCodeWrapper struct { + op vm.OpCode +} + +// toNumber returns the ID of this opcode as an integer +func (ocw *opCodeWrapper) toNumber() int { + return int(ocw.op) +} + +// toString returns the string representation of the opcode +func (ocw *opCodeWrapper) toString() string { + return ocw.op.String() +} + +// isPush returns true if the op is a Push +func (ocw *opCodeWrapper) isPush() bool { + return ocw.op.IsPush() +} + +// MarshalJSON serializes the opcode as JSON +func (ocw *opCodeWrapper) MarshalJSON() ([]byte, error) { + return json.Marshal(ocw.op.String()) +} + +// toValue returns an otto.Value for the opCodeWrapper +func (ocw *opCodeWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(ocw) + obj := value.Object() + obj.Set("toNumber", ocw.toNumber) + obj.Set("toString", ocw.toString) + obj.Set("isPush", ocw.isPush) + return value +} + +// memoryWrapper provides a JS wrapper around vm.Memory +type memoryWrapper struct { + memory *vm.Memory +} + +// slice returns the requested range of memory as a byte slice +func (mw *memoryWrapper) slice(begin, end int64) []byte { + return mw.memory.Get(begin, end-begin) +} + +// getUint returns the 32 bytes at the specified address interpreted +// as an unsigned integer +func (mw *memoryWrapper) getUint(addr int64) *big.Int { + ret := big.NewInt(0) + ret.SetBytes(mw.memory.GetPtr(addr, 32)) + return ret +} + +// toValue returns an otto.Value for the memoryWrapper +func (mw *memoryWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(mw) + obj := value.Object() + obj.Set("slice", mw.slice) + obj.Set("getUint", mw.getUint) + return value +} + +// stackWrapper provides a JS wrapper around vm.Stack +type stackWrapper struct { + stack *vm.Stack +} + +// peek returns the nth-from-the-top element of the stack. +func (sw *stackWrapper) peek(idx int) *big.Int { + return sw.stack.Data()[len(sw.stack.Data())-idx-1] +} + +// length returns the length of the stack +func (sw *stackWrapper) length() int { + return len(sw.stack.Data()) +} + +// toValue returns an otto.Value for the stackWrapper +func (sw *stackWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(sw) + obj := value.Object() + obj.Set("peek", sw.peek) + obj.Set("length", sw.length) + return value +} + +// dbWrapper provides a JS wrapper around vm.Database +type dbWrapper struct { + db vm.Database +} + +// getBalance retrieves an account's balance +func (dw *dbWrapper) getBalance(addr common.Address) *big.Int { + return dw.db.GetBalance(addr) +} + +// getNonce retrieves an account's nonce +func (dw *dbWrapper) getNonce(addr common.Address) uint64 { + return dw.db.GetNonce(addr) +} + +// getCode retrieves an account's code +func (dw *dbWrapper) getCode(addr common.Address) []byte { + return dw.db.GetCode(addr) +} + +// getState retrieves an account's state data for the given hash +func (dw *dbWrapper) getState(addr common.Address, hash common.Hash) common.Hash { + return dw.db.GetState(addr, hash) +} + +// exists returns true iff the account exists +func (dw *dbWrapper) exists(addr common.Address) bool { + return dw.db.Exist(addr) +} + +// toValue returns an otto.Value for the dbWrapper +func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(dw) + obj := value.Object() + obj.Set("getBalance", dw.getBalance) + obj.Set("getNonce", dw.getNonce) + obj.Set("getCode", dw.getCode) + obj.Set("getState", dw.getState) + obj.Set("exists", dw.exists) + return value +} + +// JavascriptTracer provides an implementation of Tracer that evaluates a +// Javascript function for each VM execution step. +type JavascriptTracer struct { + vm *otto.Otto // Javascript VM instance + traceobj *otto.Object // User-supplied object to call + log map[string]interface{} // (Reusable) map for the `log` arg to `step` + logvalue otto.Value // JS view of `log` + memory *memoryWrapper // Wrapper around the VM memory + memvalue otto.Value // JS view of `memory` + stack *stackWrapper // Wrapper around the VM stack + stackvalue otto.Value // JS view of `stack` + db *dbWrapper // Wrapper around the VM environment + dbvalue otto.Value // JS view of `db` + err error // Error, if one has occurred +} + +// NewJavascriptTracer instantiates a new JavascriptTracer instance. +// code specifies a Javascript snippet, which must evaluate to an expression +// returning an object with 'step' and 'result' functions. +func NewJavascriptTracer(code string) (*JavascriptTracer, error) { + vm := otto.New() + vm.Interrupt = make(chan func(), 1) + + // Set up builtins for this environment + vm.Set("big", &fakeBig{}) + + jstracer, err := vm.Object("(" + code + ")") + if err != nil { + return nil, err + } + + // Check the required functions exist + step, err := jstracer.Get("step") + if err != nil { + return nil, err + } + if !step.IsFunction() { + return nil, fmt.Errorf("Trace object must expose a function step()") + } + + result, err := jstracer.Get("result") + if err != nil { + return nil, err + } + if !result.IsFunction() { + return nil, fmt.Errorf("Trace object must expose a function result()") + } + + // Create the persistent log object + log := make(map[string]interface{}) + logvalue, _ := vm.ToValue(log) + + // Create persistent wrappers for memory and stack + mem := &memoryWrapper{} + stack := &stackWrapper{} + db := &dbWrapper{} + + return &JavascriptTracer{ + vm: vm, + traceobj: jstracer, + log: log, + logvalue: logvalue, + memory: mem, + memvalue: mem.toValue(vm), + stack: stack, + stackvalue: stack.toValue(vm), + db: db, + dbvalue: db.toValue(vm), + err: nil, + }, nil +} + +// Stop terminates execution of any JavaScript +func (jst *JavascriptTracer) Stop(err error) { + jst.vm.Interrupt <- func() { + panic(err) + } +} + +// callSafely executes a method on a JS object, catching any panics and +// returning them as error objects. +func (jst *JavascriptTracer) callSafely(method string, argumentList ...interface{}) (ret interface{}, err error) { + defer func() { + if caught := recover(); caught != nil { + switch caught := caught.(type) { + case error: + err = caught + case string: + err = errors.New(caught) + case fmt.Stringer: + err = errors.New(caught.String()) + default: + panic(caught) + } + } + }() + + value, err := jst.traceobj.Call(method, argumentList...) + ret, _ = value.Export() + return ret, err +} + +func wrapError(context string, err error) error { + var message string + switch err := err.(type) { + case *otto.Error: + message = err.String() + default: + message = err.Error() + } + return fmt.Errorf("%v in server-side tracer function '%v'", message, context) +} + +// 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 { + if jst.err == nil { + jst.memory.memory = memory + jst.stack.stack = stack + jst.db.db = env.Db() + + ocw := &opCodeWrapper{op} + + jst.log["pc"] = pc + jst.log["op"] = ocw.toValue(jst.vm) + jst.log["gas"] = gas.Int64() + jst.log["gasPrice"] = cost.Int64() + jst.log["memory"] = jst.memvalue + jst.log["stack"] = jst.stackvalue + jst.log["depth"] = depth + jst.log["account"] = contract.Address() + jst.log["err"] = err + + _, err := jst.callSafely("step", jst.logvalue, jst.dbvalue) + if err != nil { + jst.err = wrapError("step", err) + } + } + return nil +} + +// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error +func (jst *JavascriptTracer) GetResult() (result interface{}, err error) { + if jst.err != nil { + return nil, jst.err + } + + result, err = jst.callSafely("result") + if err != nil { + err = wrapError("result", err) + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go index daa19b6bd..17fae5647 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/ethereum_js.go @@ -1987,7 +1987,7 @@ var padRight = function (string, chars, sign) { }; /** - * Should be called to get utf8 from it's hex representation + * Should be called to get utf8 from its hex representation * * @method toUtf8 * @param {String} string in hex @@ -2011,7 +2011,7 @@ var toUtf8 = function(hex) { }; /** - * Should be called to get ascii from it's hex representation + * Should be called to get ascii from its hex representation * * @method toAscii * @param {String} string in hex @@ -2109,7 +2109,7 @@ var extractTypeName = function (name) { }; /** - * Converts value to it's decimal representation in string + * Converts value to its decimal representation in string * * @method toDecimal * @param {String|Number|BigNumber} @@ -2120,7 +2120,7 @@ var toDecimal = function (value) { }; /** - * Converts value to it's hex representation + * Converts value to its hex representation * * @method fromDecimal * @param {String|Number|BigNumber} @@ -2134,7 +2134,7 @@ var fromDecimal = function (value) { }; /** - * Auto converts any given value into it's hex representation. + * Auto converts any given value into its hex representation. * * And even stringifys objects before. * diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go index f32e16243..8fe00cc4c 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go index e76e15177..c8a0cac8c 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go @@ -18,17 +18,159 @@ package web3ext var Modules = map[string]string{ - "admin": Admin_JS, - "debug": Debug_JS, - "eth": Eth_JS, - "miner": Miner_JS, - "net": Net_JS, - "personal": Personal_JS, - "rpc": RPC_JS, - "shh": Shh_JS, - "txpool": TxPool_JS, + "admin": Admin_JS, + "bzz": Bzz_JS, + "chequebook": Chequebook_JS, + "debug": Debug_JS, + "ens": ENS_JS, + "eth": Eth_JS, + "miner": Miner_JS, + "net": Net_JS, + "personal": Personal_JS, + "rpc": RPC_JS, + "shh": Shh_JS, + "txpool": TxPool_JS, } +const Bzz_JS = ` +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', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'swapEnabled', + call: 'bzz_swapEnabled', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'download', + call: 'bzz_download', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'upload', + call: 'bzz_upload', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'retrieve', + call: 'bzz_retrieve', + 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', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'put', + call: 'bzz_put', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'modify', + call: 'bzz_modify', + params: 4, + inputFormatter: [null, null, null, null] + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'hive', + getter: 'bzz_hive' + }), + new web3._extend.Property({ + name: 'info', + getter: 'bzz_info', + }), + ] +}); +` + +const ENS_JS = ` +web3._extend({ + property: 'ens', + methods: + [ + new web3._extend.Method({ + name: 'register', + call: 'ens_register', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'setContentHash', + call: 'ens_setContentHash', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'resolve', + call: 'ens_resolve', + params: 1, + inputFormatter: [null] + }), + ] +}) +` + +const Chequebook_JS = ` +web3._extend({ + property: 'chequebook', + methods: + [ + new web3._extend.Method({ + name: 'deposit', + call: 'chequebook_deposit', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Property({ + name: 'balance', + getter: 'chequebook_balance', + outputFormatter: web3._extend.utils.toDecimal + }), + new web3._extend.Method({ + name: 'cash', + call: 'chequebook_cash', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'issue', + call: 'chequebook_issue', + params: 2, + inputFormatter: [null, null] + }), + ] +}); +` + const Admin_JS = ` web3._extend({ property: 'admin', @@ -195,6 +337,10 @@ web3._extend({ params: 1, outputFormatter: console.log }), + new web3._extend.Method({ + name: 'chaindbCompact', + call: 'debug_chaindbCompact', + }), new web3._extend.Method({ name: 'metrics', call: 'debug_metrics', @@ -284,7 +430,8 @@ web3._extend({ new web3._extend.Method({ name: 'traceTransaction', call: 'debug_traceTransaction', - params: 1 + params: 2, + inputFormatter: [null, null] }) ], properties: [] @@ -325,6 +472,19 @@ web3._extend({ call: 'eth_submitTransaction', params: 1, inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'getRawTransaction', + call: 'eth_getRawTransactionByHash', + params: 1 + }), + new web3._extend.Method({ + name: 'getRawTransactionFromBlock', + call: function(args) { + return (web3._extend.utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getRawTransactionByBlockHashAndIndex' : 'eth_getRawTransactionByBlockNumberAndIndex'; + }, + params: 2, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.utils.toHex] }) ], properties: @@ -427,9 +587,20 @@ web3._extend({ call: 'personal_sendTransaction', params: 2, inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null] + }), + new web3._extend.Method({ + name: 'sign', + call: 'personal_sign', + params: 3, + inputFormatter: [null, web3._extend.formatters.inputAddressFormatter, null] + }), + new web3._extend.Method({ + name: 'ecRecover', + call: 'personal_ecRecover', + params: 2 }) ] -}); +}) ` const RPC_JS = ` diff --git a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go index f17e0cc91..b77767ed7 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go @@ -1,18 +1,18 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. // -// 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 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. // -// go-ethereum is distributed in the hope that it will be useful, +// 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 General Public License for more details. +// GNU Lesser 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 . +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . package les @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/params" rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -39,6 +40,14 @@ type LesApiBackend struct { gpo *gasprice.LightPriceOracle } +func (b *LesApiBackend) ChainConfig() *params.ChainConfig { + return b.eth.chainConfig +} + +func (b *LesApiBackend) CurrentBlock() *types.Block { + return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader()) +} + func (b *LesApiBackend) SetHead(number uint64) { b.eth.blockchain.SetHead(number) } @@ -81,13 +90,13 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int { 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() + addr := msg.From() from, err := stateDb.GetOrNewStateObject(ctx, addr) if err != nil { return nil, nil, err } from.SetBalance(common.MaxBig) - env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig) + env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}) return env, env.Error, nil } @@ -115,7 +124,7 @@ func (b *LesApiBackend) Stats() (pending int, queued int) { return b.eth.txPool.Stats(), 0 } -func (b *LesApiBackend) TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { return b.eth.txPool.Content() } diff --git a/vendor/github.com/ethereum/go-ethereum/les/backend.go b/vendor/github.com/ethereum/go-ethereum/les/backend.go index efa938c0d..d7b9c7e28 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/backend.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -24,11 +24,11 @@ import ( "github.com/ethereum/ethash" "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/core/vm" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" @@ -37,15 +37,18 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/light" + "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/params" rpc "github.com/ethereum/go-ethereum/rpc" ) type LightEthereum struct { odr *LesOdr relay *LesTxRelay - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig // Channel for shutting down the service shutdownChan chan bool // Handlers @@ -106,10 +109,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { return nil, errors.New("missing chain config") } eth.chainConfig = config.ChainConfig - eth.chainConfig.VmConfig = vm.Config{ - EnableJit: config.EnableJit, - ForceJit: config.ForceJit, - } eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.pow, eth.eventMux) if err != nil { if err == core.ErrNoGenesis { @@ -132,11 +131,38 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { return eth, nil } +type LightDummyAPI struct{} + +// Etherbase is the address that mining rewards will be send to +func (s *LightDummyAPI) Etherbase() (common.Address, error) { + return common.Address{}, fmt.Errorf("not supported") +} + +// Coinbase is the address that mining rewards will be send to (alias for Etherbase) +func (s *LightDummyAPI) Coinbase() (common.Address, error) { + return common.Address{}, fmt.Errorf("not supported") +} + +// Hashrate returns the POW hashrate +func (s *LightDummyAPI) Hashrate() *rpc.HexNumber { + return rpc.NewHexNumber(0) +} + +// Mining returns an indication if this node is currently mining. +func (s *LightDummyAPI) Mining() bool { + return false +} + // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *LightEthereum) APIs() []rpc.API { return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ { + Namespace: "eth", + Version: "1.0", + Service: &LightDummyAPI{}, + Public: true, + }, { Namespace: "eth", Version: "1.0", Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), @@ -144,7 +170,7 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend), + Service: filters.NewPublicFilterAPI(s.ApiBackend, true), Public: true, }, { Namespace: "net", @@ -173,8 +199,9 @@ func (s *LightEthereum) Protocols() []p2p.Protocol { // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. func (s *LightEthereum) Start(srvr *p2p.Server) error { + glog.V(logger.Info).Infof("WARNING: light client mode is an experimental feature") s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId) - s.protocolManager.Start() + s.protocolManager.Start(srvr) return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/les/fetcher.go b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go index bf9f010fb..ae9bf8474 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/fetcher.go +++ b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -18,7 +18,6 @@ package les import ( -//"fmt" "math/big" "sync" "time" @@ -29,58 +28,62 @@ import ( ) type lightFetcher struct { - pm *ProtocolManager - odr *LesOdr - chain BlockChain + pm *ProtocolManager + odr *LesOdr + chain BlockChain - headAnnouncedMu sync.Mutex - headAnnouncedBy map[common.Hash][]*peer - currentTd *big.Int - deliverChn chan fetchResponse - reqMu sync.RWMutex - requested map[uint64]fetchRequest - timeoutChn chan uint64 - notifyChn chan bool // true if initiated from outside - syncing bool - syncDone chan struct{} + headAnnouncedMu sync.Mutex + headAnnouncedBy map[common.Hash][]*peer + currentTd *big.Int + deliverChn chan fetchResponse + reqMu sync.RWMutex + requested map[uint64]fetchRequest + timeoutChn chan uint64 + notifyChn chan bool // true if initiated from outside + syncing bool + syncDone chan struct{} } type fetchRequest struct { - hash common.Hash + hash common.Hash amount uint64 - peer *peer + peer *peer } type fetchResponse struct { - reqID uint64 + reqID uint64 headers []*types.Header } func newLightFetcher(pm *ProtocolManager) *lightFetcher { f := &lightFetcher{ - pm: pm, - chain: pm.blockchain, - odr: pm.odr, + pm: pm, + chain: pm.blockchain, + odr: pm.odr, headAnnouncedBy: make(map[common.Hash][]*peer), - deliverChn: make(chan fetchResponse, 100), + deliverChn: make(chan fetchResponse, 100), requested: make(map[uint64]fetchRequest), - timeoutChn: make(chan uint64), - notifyChn: make(chan bool, 100), - syncDone: make(chan struct{}), - currentTd: big.NewInt(0), + timeoutChn: make(chan uint64), + notifyChn: make(chan bool, 100), + syncDone: make(chan struct{}), + currentTd: big.NewInt(0), } go f.syncLoop() return f } -func (f *lightFetcher) notify(p *peer, head *newBlockHashData) { +func (f *lightFetcher) notify(p *peer, head *announceData) { var headHash common.Hash if head == nil { // initial notify headHash = p.Head() - } else { + } else { + if core.GetTd(f.pm.chainDb, head.Hash, head.Number) != nil { + head.haveHeaders = head.Number + } + //fmt.Println("notify", p.id, head.Number, head.ReorgDepth, head.haveHeaders) if !p.addNotify(head) { -//fmt.Println("addNotify fail") + //fmt.Println("addNotify fail") f.pm.removePeer(p.id) } headHash = head.Hash @@ -100,27 +103,27 @@ func (f *lightFetcher) gotHeader(header *types.Header) { if peerList == nil { return } - number := header.GetNumberU64() + number := header.Number.Uint64() td := core.GetTd(f.pm.chainDb, hash, number) for _, peer := range peerList { peer.lock.Lock() ok := peer.gotHeader(hash, number, td) peer.lock.Unlock() if !ok { -//fmt.Println("gotHeader fail") + //fmt.Println("gotHeader fail") f.pm.removePeer(peer.id) } - } + } delete(f.headAnnouncedBy, hash) } -func (f *lightFetcher) nextRequest() (*peer, *newBlockHashData) { +func (f *lightFetcher) nextRequest() (*peer, *announceData) { var bestPeer *peer bestTd := f.currentTd for _, peer := range f.pm.peers.AllPeers() { peer.lock.RLock() if !peer.headInfo.requested && (peer.headInfo.Td.Cmp(bestTd) > 0 || - (bestPeer != nil && peer.headInfo.Td.Cmp(bestTd) == 0 && peer.headInfo.haveHeaders > bestPeer.headInfo.haveHeaders)) { + (bestPeer != nil && peer.headInfo.Td.Cmp(bestTd) == 0 && peer.headInfo.haveHeaders > bestPeer.headInfo.haveHeaders)) { bestPeer = peer bestTd = peer.headInfo.Td } @@ -133,6 +136,15 @@ func (f *lightFetcher) nextRequest() (*peer, *newBlockHashData) { res := bestPeer.headInfo res.requested = true bestPeer.lock.Unlock() + for _, peer := range f.pm.peers.AllPeers() { + if peer != bestPeer { + peer.lock.Lock() + if peer.headInfo.Hash == bestPeer.headInfo.Hash && peer.headInfo.haveHeaders == bestPeer.headInfo.haveHeaders { + peer.headInfo.requested = true + } + peer.lock.Unlock() + } + } return bestPeer, res } @@ -147,18 +159,18 @@ func (f *lightFetcher) requestedID(reqID uint64) bool { return ok } -func (f *lightFetcher) request(p *peer, block *newBlockHashData) { -//fmt.Println("request", block.Number, block.haveHeaders) - amount := block.Number-block.haveHeaders +func (f *lightFetcher) request(p *peer, block *announceData) { + //fmt.Println("request", p.id, block.Number, block.haveHeaders) + amount := block.Number - block.haveHeaders if amount == 0 { return } if amount > 100 { f.syncing = true go func() { -//fmt.Println("f.pm.synchronise(p)") + //fmt.Println("f.pm.synchronise(p)") f.pm.synchronise(p) -//fmt.Println("sync done") + //fmt.Println("sync done") f.syncDone <- struct{}{} }() return @@ -189,7 +201,7 @@ func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) boo return false } for _, header := range headers { - td := core.GetTd(f.pm.chainDb, header.Hash(), header.GetNumberU64()) + td := core.GetTd(f.pm.chainDb, header.Hash(), header.Number.Uint64()) if td == nil { return false } @@ -202,15 +214,15 @@ func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) boo } func (f *lightFetcher) checkSyncedHeaders() { -//fmt.Println("checkSyncedHeaders()") + //fmt.Println("checkSyncedHeaders()") for _, peer := range f.pm.peers.AllPeers() { peer.lock.Lock() h := peer.firstHeadInfo remove := false -loop: + loop: for h != nil { if td := core.GetTd(f.pm.chainDb, h.Hash, h.Number); td != nil { -//fmt.Println(" found", h.Number) + //fmt.Println(" found", h.Number) ok := peer.gotHeader(h.Hash, h.Number, td) if !ok { remove = true @@ -224,7 +236,7 @@ loop: } peer.lock.Unlock() if remove { -//fmt.Println("checkSync fail") + //fmt.Println("checkSync fail") f.pm.removePeer(peer.id) } } @@ -240,7 +252,7 @@ func (f *lightFetcher) syncLoop() { case <-f.pm.quitSync: return case ext := <-f.notifyChn: -//fmt.Println("<-f.notifyChn", f.syncing, ext, srtoNotify) + //fmt.Println("<-f.notifyChn", f.syncing, ext, srtoNotify) s := srtoNotify srtoNotify = false if !f.syncing && !(ext && s) { @@ -261,21 +273,21 @@ func (f *lightFetcher) syncLoop() { } f.reqMu.Unlock() if ok { -//fmt.Println("hard timeout") + //fmt.Println("hard timeout") f.pm.removePeer(req.peer.id) } case resp := <-f.deliverChn: -//fmt.Println("<-f.deliverChn", f.syncing) + //fmt.Println("<-f.deliverChn", f.syncing) f.reqMu.Lock() req, ok := f.requested[resp.reqID] delete(f.requested, resp.reqID) f.reqMu.Unlock() if !ok || !(f.syncing || f.processResponse(req, resp)) { -//fmt.Println("processResponse fail") + //fmt.Println("processResponse fail") f.pm.removePeer(req.peer.id) } case <-f.syncDone: -//fmt.Println("<-f.syncDone", f.syncing) + //fmt.Println("<-f.syncDone", f.syncing) f.checkSyncedHeaders() f.syncing = false } diff --git a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go index 1b569db0b..acb131ea4 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go +++ b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -99,7 +99,7 @@ type ServerNode struct { params *ServerParams sumCost uint64 // sum of req costs sent to this server pending map[uint64]uint64 // value = sumCost after sending the given req - lock sync.Mutex + lock sync.RWMutex } func NewServerNode(params *ServerParams) *ServerNode { @@ -135,8 +135,8 @@ func (peer *ServerNode) canSend(maxCost uint64) uint64 { } func (peer *ServerNode) CanSend(maxCost uint64) uint64 { - peer.lock.Lock() - defer peer.lock.Unlock() + peer.lock.RLock() + defer peer.lock.RUnlock() return peer.canSend(maxCost) } @@ -148,7 +148,10 @@ func (peer *ServerNode) SendRequest(reqID, maxCost uint64) { peer.recalcBLE(getTime()) for peer.bufEstimate < maxCost { - time.Sleep(time.Duration(peer.canSend(maxCost))) + wait := time.Duration(peer.canSend(maxCost)) + peer.lock.Unlock() + time.Sleep(wait) + peer.lock.Lock() peer.recalcBLE(getTime()) } peer.bufEstimate -= maxCost diff --git a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go index c0469e7b6..786884437 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go +++ b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/handler.go b/vendor/github.com/ethereum/go-ethereum/les/handler.go index ef64e25fb..83d73666f 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/handler.go +++ b/vendor/github.com/ethereum/go-ethereum/les/handler.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -23,6 +23,7 @@ import ( "fmt" "math/big" "sync" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -36,6 +37,8 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -85,7 +88,7 @@ type BlockChain interface { type txPool interface { // AddTransactions should add the given transactions to the pool. - AddTransactions([]*types.Transaction) + AddBatch([]*types.Transaction) } type ProtocolManager struct { @@ -93,12 +96,16 @@ type ProtocolManager struct { txpool txPool txrelay *LesTxRelay networkId int - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig blockchain BlockChain chainDb ethdb.Database odr *LesOdr server *LesServer + topicDisc *discv5.Network + lesTopic discv5.Topic + p2pServer *p2p.Server + downloader *downloader.Downloader fetcher *lightFetcher peers *peerSet @@ -123,7 +130,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(chainConfig *core.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { +func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ lightSync: lightSync, @@ -229,11 +236,56 @@ func (pm *ProtocolManager) removePeer(id string) { } } -func (pm *ProtocolManager) Start() { +func (pm *ProtocolManager) findServers() { + if pm.p2pServer == nil || pm.topicDisc == nil { + return + } + glog.V(logger.Debug).Infoln("Looking for topic", string(pm.lesTopic)) + enodes := make(chan string, 100) + stop := make(chan struct{}) + go pm.topicDisc.SearchTopic(pm.lesTopic, stop, enodes) + go func() { + added := make(map[string]bool) + for { + select { + case enode := <-enodes: + if !added[enode] { + glog.V(logger.Info).Infoln("Found LES server:", enode) + added[enode] = true + if node, err := discover.ParseNode(enode); err == nil { + pm.p2pServer.AddPeer(node) + } + } + case <-stop: + return + } + } + }() + select { + case <-time.After(time.Second * 20): + case <-pm.quitSync: + } + close(stop) +} + +func (pm *ProtocolManager) Start(srvr *p2p.Server) { + pm.p2pServer = srvr + if srvr != nil { + pm.topicDisc = srvr.DiscV5 + } + pm.lesTopic = discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8])) if pm.lightSync { // start sync handler + go pm.findServers() go pm.syncer() } else { + if pm.topicDisc != nil { + go func() { + glog.V(logger.Debug).Infoln("Starting registering topic", string(pm.lesTopic)) + pm.topicDisc.RegisterTopic(pm.lesTopic, pm.quitSync) + glog.V(logger.Debug).Infoln("Stopped registering topic", string(pm.lesTopic)) + }() + } go func() { for range pm.newPeerCh { } @@ -329,9 +381,8 @@ func (pm *ProtocolManager) handle(p *peer) error { // new block announce loop for { select { - case announce := <-p.newBlockHashChn: - p.SendNewBlockHash(announce) - //fmt.Println(" BROADCAST sent") + case announce := <-p.announceChn: + p.SendAnnounce(announce) case <-stop: return } @@ -342,11 +393,9 @@ func (pm *ProtocolManager) handle(p *peer) error { for { if err := pm.handleMsg(p); err != nil { glog.V(logger.Debug).Infof("%v: message handling failed: %v", p, err) - //fmt.Println("handleMsg err:", err) return err } } - return nil } var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg, GetHeaderProofsMsg} @@ -363,7 +412,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { var costs *requestCosts var reqCnt, maxReqs int - //fmt.Println("MSG", msg.Code, msg.Size) + glog.V(logger.Debug).Infoln("msg:", msg.Code, msg.Size) if rc, ok := p.fcCosts[msg.Code]; ok { // check if msg is a supported request type costs = rc if p.fcClient == nil { @@ -392,21 +441,23 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { // Handle the message depending on its contents switch msg.Code { case StatusMsg: - glog.V(logger.Debug).Infof("LES: received StatusMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== StatusMsg from peer %v", p.id) // Status messages should never arrive after the handshake return errResp(ErrExtraStatusMsg, "uncontrolled status message") // Block header query, collect the requested headers and reply - case NewBlockHashMsg: - var req newBlockHashData + case AnnounceMsg: + glog.V(logger.Debug).Infoln("<=== AnnounceMsg from peer %v:", p.id) + + var req announceData if err := msg.Decode(&req); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } - //fmt.Println("RECEIVED", req.Number, req.Hash, req.Td, req.ReorgDepth) + glog.V(logger.Detail).Infoln("AnnounceMsg:", req.Number, req.Hash, req.Td, req.ReorgDepth) pm.fetcher.notify(p, &req) case GetBlockHeadersMsg: - glog.V(logger.Debug).Infof("LES: received GetBlockHeadersMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetBlockHeadersMsg from peer %v", p.id) // Decode the complex header query var req struct { ReqID uint64 @@ -491,7 +542,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received BlockHeadersMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== BlockHeadersMsg from peer %v", p.id) // A batch of headers arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -511,7 +562,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetBlockBodiesMsg: - glog.V(logger.Debug).Infof("LES: received GetBlockBodiesMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetBlockBodiesMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -548,7 +599,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received BlockBodiesMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== BlockBodiesMsg from peer %v", p.id) // A batch of block bodies arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -565,7 +616,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetCodeMsg: - glog.V(logger.Debug).Infof("LES: received GetCodeMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetCodeMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -588,8 +639,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil { if trie, _ := trie.New(header.Root, pm.chainDb); trie != nil { sdata := trie.Get(req.AccKey) - if so, err := state.DecodeObject(common.Address{}, pm.chainDb, sdata); err == nil { - entry := so.Code() + var acc state.Account + if err := rlp.DecodeBytes(sdata, &acc); err == nil { + entry, _ := pm.chainDb.Get(acc.CodeHash) if bytes+len(entry) >= softResponseLimit { break } @@ -608,7 +660,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received CodeMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== CodeMsg from peer %v", p.id) // A batch of node state data arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -625,7 +677,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetReceiptsMsg: - glog.V(logger.Debug).Infof("LES: received GetReceiptsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetReceiptsMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -671,7 +723,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received ReceiptsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== ReceiptsMsg from peer %v", p.id) // A batch of receipts arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -688,7 +740,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetProofsMsg: - glog.V(logger.Debug).Infof("LES: received GetProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetProofsMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -714,10 +766,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil { if tr, _ := trie.New(header.Root, pm.chainDb); tr != nil { if len(req.AccKey) > 0 { - data := tr.Get(req.AccKey) + sdata := tr.Get(req.AccKey) tr = nil - if so, err := state.DecodeObject(common.Address{}, pm.chainDb, data); err == nil { - tr, _ = trie.New(common.BytesToHash(so.Root()), pm.chainDb) + var acc state.Account + if err := rlp.DecodeBytes(sdata, &acc); err == nil { + tr, _ = trie.New(acc.Root, pm.chainDb) } } if tr != nil { @@ -737,7 +790,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received ProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== ProofsMsg from peer %v", p.id) // A batch of merkle proofs arrived to one of our previous requests var resp struct { ReqID, BV uint64 @@ -754,7 +807,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } case GetHeaderProofsMsg: - glog.V(logger.Debug).Infof("LES: received GetHeaderProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== GetHeaderProofsMsg from peer %v", p.id) // Decode the retrieval message var req struct { ReqID uint64 @@ -798,7 +851,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { return errResp(ErrUnexpectedResponse, "") } - glog.V(logger.Debug).Infof("LES: received HeaderProofsMsg from peer %v", p.id) + glog.V(logger.Debug).Infof("<=== HeaderProofsMsg from peer %v", p.id) var resp struct { ReqID, BV uint64 Data []ChtResp @@ -826,12 +879,12 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if reqCnt > maxReqs || reqCnt > MaxTxSend { return errResp(ErrRequestRejected, "") } - pm.txpool.AddTransactions(txs) + pm.txpool.AddBatch(txs) _, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) default: - glog.V(logger.Debug).Infof("LES: received unknown message with code %d from peer %v", msg.Code, p.id) + glog.V(logger.Debug).Infof("<=== unknown message with code %d from peer %v", msg.Code, p.id) return errResp(ErrInvalidMsgCode, "%v", msg.Code) } diff --git a/vendor/github.com/ethereum/go-ethereum/les/metrics.go b/vendor/github.com/ethereum/go-ethereum/les/metrics.go index 88e6726e2..aa0796790 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/metrics.go +++ b/vendor/github.com/ethereum/go-ethereum/les/metrics.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr.go b/vendor/github.com/ethereum/go-ethereum/les/odr.go index 2674ba6a1..444b1da2a 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/odr.go +++ b/vendor/github.com/ethereum/go-ethereum/les/odr.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package les import ( diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go b/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go index 0323ce27f..e9b7eec7f 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go +++ b/vendor/github.com/ethereum/go-ethereum/les/odr_peerset.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package les import ( diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go b/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go index bf0346977..f4bd51888 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go +++ b/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/peer.go b/vendor/github.com/ethereum/go-ethereum/les/peer.go index a191eeeff..5d566d899 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/les/peer.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -51,11 +51,11 @@ type peer struct { id string - firstHeadInfo, headInfo *newBlockHashData + firstHeadInfo, headInfo *announceData headInfoLen int lock sync.RWMutex - newBlockHashChn chan newBlockHashData + announceChn chan announceData fcClient *flowcontrol.ClientNode // nil if the peer is server only fcServer *flowcontrol.ServerNode // nil if the peer is client only @@ -67,12 +67,12 @@ func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { id := p.ID() return &peer{ - Peer: p, - rw: rw, - version: version, - network: network, - id: fmt.Sprintf("%x", id[:8]), - newBlockHashChn: make(chan newBlockHashData, 20), + Peer: p, + rw: rw, + version: version, + network: network, + id: fmt.Sprintf("%x", id[:8]), + announceChn: make(chan announceData, 20), } } @@ -109,7 +109,7 @@ func (p *peer) headBlockInfo() blockInfo { return blockInfo{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td} } -func (p *peer) addNotify(announce *newBlockHashData) bool { +func (p *peer) addNotify(announce *announceData) bool { p.lock.Lock() defer p.lock.Unlock() @@ -121,11 +121,13 @@ func (p *peer) addNotify(announce *newBlockHashData) bool { p.firstHeadInfo = p.firstHeadInfo.next p.headInfoLen-- } - hh := p.headInfo.Number - announce.ReorgDepth - if p.headInfo.haveHeaders < hh { - hh = p.headInfo.haveHeaders + if announce.haveHeaders == 0 { + hh := p.headInfo.Number - announce.ReorgDepth + if p.headInfo.haveHeaders < hh { + hh = p.headInfo.haveHeaders + } + announce.haveHeaders = hh } - announce.haveHeaders = hh p.headInfo.next = announce p.headInfo = announce p.headInfoLen++ @@ -200,10 +202,10 @@ func (p *peer) GetRequestCost(msgcode uint64, amount int) uint64 { return cost } -// SendNewBlockHash announces the availability of a number of blocks through +// SendAnnounce announces the availability of a number of blocks through // a hash notification. -func (p *peer) SendNewBlockHash(request newBlockHashData) error { - return p2p.Send(p.rw, NewBlockHashMsg, request) +func (p *peer) SendAnnounce(request announceData) error { + return p2p.Send(p.rw, AnnounceMsg, request) } // SendBlockHeaders sends a batch of block headers to the remote peer. @@ -448,11 +450,10 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis } p.fcServerParams = params p.fcServer = flowcontrol.NewServerNode(params) - p.fcServerParams = params p.fcCosts = MRC.decode() } - p.firstHeadInfo = &newBlockHashData{Td: rTd, Hash: rHash, Number: rNum} + p.firstHeadInfo = &announceData{Td: rTd, Hash: rHash, Number: rNum} p.headInfo = p.firstHeadInfo p.headInfoLen = 1 return nil diff --git a/vendor/github.com/ethereum/go-ethereum/les/protocol.go b/vendor/github.com/ethereum/go-ethereum/les/protocol.go index 0ef2d8c06..46da2b8c8 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/protocol.go +++ b/vendor/github.com/ethereum/go-ethereum/les/protocol.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -47,7 +47,7 @@ const ( const ( // Protocol messages belonging to LPV1 StatusMsg = 0x00 - NewBlockHashMsg = 0x01 + AnnounceMsg = 0x01 GetBlockHeadersMsg = 0x02 BlockHeadersMsg = 0x03 GetBlockBodiesMsg = 0x04 @@ -111,30 +111,20 @@ type chainManager interface { Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) } -// statusData is the network packet for the status message. -type statusData struct { - ProtocolVersion uint32 - NetworkId uint32 - TD *big.Int - CurrentBlock common.Hash - GenesisBlock common.Hash - History uint64 - BL, MRR uint64 - MRC RequestCostList -} - -// newBlockHashData is the network packet for the block announcements. -type newBlockHashData struct{ - Hash common.Hash // Hash of one particular block being announced - Number uint64 // Number of one particular block being announced - Td *big.Int // Total difficulty of one particular block being announced +// announceData is the network packet for the block announcements. +type announceData struct { + Hash common.Hash // Hash of one particular block being announced + Number uint64 // Number of one particular block being announced + Td *big.Int // Total difficulty of one particular block being announced ReorgDepth uint64 + Update keyValueList haveHeaders uint64 // we have the headers of the remote peer's chain up to this number - headKnown bool - requested bool - next *newBlockHashData + headKnown bool + requested bool + next *announceData } + type blockInfo struct { Hash common.Hash // Hash of one particular block being announced Number uint64 // Number of one particular block being announced diff --git a/vendor/github.com/ethereum/go-ethereum/les/server.go b/vendor/github.com/ethereum/go-ethereum/les/server.go index f751b6a91..c763e8c63 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/server.go +++ b/vendor/github.com/ethereum/go-ethereum/les/server.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -19,7 +19,6 @@ package les import ( "encoding/binary" - "fmt" "math" "sync" "time" @@ -31,6 +30,8 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -66,8 +67,9 @@ func (s *LesServer) Protocols() []p2p.Protocol { return s.protocolManager.SubProtocols } -func (s *LesServer) Start() { - s.protocolManager.Start() +func (s *LesServer) Start(srvr *p2p.Server) { + s.protocolManager.Start(srvr) + } func (s *LesServer) Stop() { @@ -278,20 +280,22 @@ func (pm *ProtocolManager) blockLoop() { if len(peers) > 0 { header := ev.Data.(core.ChainHeadEvent).Block.Header() hash := header.Hash() - number := header.GetNumberU64() + number := header.Number.Uint64() td := core.GetTd(pm.chainDb, hash, number) if td != nil && td.Cmp(lastBroadcastTd) > 0 { var reorg uint64 if lastHead != nil { - reorg = lastHead.GetNumberU64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).GetNumberU64() + reorg = lastHead.Number.Uint64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() } lastHead = header lastBroadcastTd = td - //fmt.Println("BROADCAST", number, hash, td, reorg) - announce := newBlockHashData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} + + glog.V(logger.Debug).Infoln("===> ", number, hash, td, reorg) + + announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} for _, p := range peers { select { - case p.newBlockHashChn <- announce: + case p.announceChn <- announce: default: pm.removePeer(p.id) } @@ -390,7 +394,9 @@ func makeCht(db ethdb.Database) bool { lastChtNum = 0 } else { lastChtNum++ - fmt.Printf("CHT %d %064x\n", lastChtNum, root) + + glog.V(logger.Detail).Infof("cht: %d %064x", lastChtNum, root) + storeChtRoot(db, lastChtNum, root) var data [8]byte binary.BigEndian.PutUint64(data[:], lastChtNum) diff --git a/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go b/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go index 2b8a0d879..937879af2 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go +++ b/vendor/github.com/ethereum/go-ethereum/les/status/txqueue.go @@ -2,6 +2,9 @@ package status import ( "errors" + "sync" + + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" @@ -15,16 +18,22 @@ const ( var ( ErrQueuedTxIdNotFound = errors.New("transaction hash not found") + ErrQueuedTxTimedOut = errors.New("transaction sending timed out") + ErrQueuedTxDiscarded = errors.New("transaction has been discarded") ) // TxQueue is capped container that holds pending transactions type TxQueue struct { transactions map[QueuedTxId]*QueuedTx + mu sync.RWMutex // to guard trasactions map evictableIds chan QueuedTxId enqueueTicker chan struct{} - // when items are enqueued notify handlers + // when items are enqueued notify subscriber txEnqueueHandler EnqueuedTxHandler + + // when tx is returned (either successfully or with error) notify subscriber + txReturnHandler EnqueuedTxReturnHandler } // QueuedTx holds enough information to complete the queued transaction. @@ -34,14 +43,18 @@ type QueuedTx struct { Context context.Context Args SendTxArgs Done chan struct{} + Discard chan struct{} Err error } type QueuedTxId string -// QueuedTxHandler is a function that receives queued/pending transactions, when they get queued +// EnqueuedTxHandler is a function that receives queued/pending transactions, when they get queued type EnqueuedTxHandler func(QueuedTx) +// EnqueuedTxReturnHandler is a function that receives response when tx is complete (both on success and error) +type EnqueuedTxReturnHandler func(queuedTx *QueuedTx, err error) + // SendTxArgs represents the arguments to submbit a new transaction into the transaction pool. type SendTxArgs struct { From common.Address `json:"from"` @@ -68,11 +81,21 @@ func NewTransactionQueue() *TxQueue { func (q *TxQueue) evictionLoop() { for range q.enqueueTicker { if len(q.transactions) >= (DefaultTxQueueCap - 1) { // eviction is required to accommodate another/last item - delete(q.transactions, <-q.evictableIds) + q.Remove(<-q.evictableIds) + q.enqueueTicker <- struct{}{} // in case we pulled already removed item } } } +// Reset is to be used in tests only, as it simply creates new transaction map, w/o any cleanup of the previous one +func (q *TxQueue) Reset() { + q.mu.Lock() + defer q.mu.Unlock() + + q.transactions = make(map[QueuedTxId]*QueuedTx) + q.evictableIds = make(chan QueuedTxId, DefaultTxQueueCap) +} + func (q *TxQueue) Enqueue(tx *QueuedTx) error { if q.txEnqueueHandler == nil { //discard, until handler is provided return nil @@ -81,7 +104,9 @@ func (q *TxQueue) Enqueue(tx *QueuedTx) error { q.enqueueTicker <- struct{}{} // notify eviction loop that we are trying to insert new item q.evictableIds <- tx.Id // this will block when we hit DefaultTxQueueCap + q.mu.Lock() q.transactions[tx.Id] = tx + q.mu.Unlock() // notify handler q.txEnqueueHandler(*tx) @@ -90,19 +115,34 @@ func (q *TxQueue) Enqueue(tx *QueuedTx) error { } func (q *TxQueue) Get(id QueuedTxId) (*QueuedTx, error) { + q.mu.RLock() + defer q.mu.RUnlock() + if tx, ok := q.transactions[id]; ok { - delete(q.transactions, id) return tx, nil } return nil, ErrQueuedTxIdNotFound } +func (q *TxQueue) Remove(id QueuedTxId) { + q.mu.Lock() + defer q.mu.Unlock() + + delete(q.transactions, id) +} + func (q *TxQueue) Count() int { + q.mu.RLock() + defer q.mu.RUnlock() + return len(q.transactions) } func (q *TxQueue) Has(id QueuedTxId) bool { + q.mu.RLock() + defer q.mu.RUnlock() + _, ok := q.transactions[id] return ok @@ -111,3 +151,37 @@ func (q *TxQueue) Has(id QueuedTxId) bool { func (q *TxQueue) SetEnqueueHandler(fn EnqueuedTxHandler) { q.txEnqueueHandler = fn } + +func (q *TxQueue) SetTxReturnHandler(fn EnqueuedTxReturnHandler) { + q.txReturnHandler = fn +} + +func (q *TxQueue) NotifyOnQueuedTxReturn(queuedTx *QueuedTx, err error) { + if q == nil { + return + } + + // discard, if transaction is not found + if queuedTx == nil { + return + } + + // on success, remove item from the queue and stop propagating + if err == nil { + q.Remove(queuedTx.Id) + return + } + + // error occurred, send upward notification + if q.txReturnHandler == nil { // discard, until handler is provided + return + } + + // remove from queue on any error (except for password related one) and propagate + if err != accounts.ErrDecrypt { + q.Remove(queuedTx.Id) + } + + // notify handler + q.txReturnHandler(queuedTx, err) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/sync.go b/vendor/github.com/ethereum/go-ethereum/les/sync.go index f92f8ce04..72c979c61 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/les/sync.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 diff --git a/vendor/github.com/ethereum/go-ethereum/les/txrelay.go b/vendor/github.com/ethereum/go-ethereum/les/txrelay.go index 366e69a62..036158f5d 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/txrelay.go +++ b/vendor/github.com/ethereum/go-ethereum/les/txrelay.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package les import ( @@ -106,12 +107,12 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) { } } - for peer, list := range sendTo { - cost := peer.GetRequestCost(SendTxMsg, len(list)) - go func() { - peer.fcServer.SendRequest(0, cost) - peer.SendTxs(cost, list) - }() + for p, list := range sendTo { + cost := p.GetRequestCost(SendTxMsg, len(list)) + go func(p *peer, list types.Transactions, cost uint64) { + p.fcServer.SendRequest(0, cost) + p.SendTxs(cost, list) + }(p, list, cost) } } diff --git a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go index 8e45d54d3..1cea7a892 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go +++ b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package light import ( @@ -28,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/hashicorp/golang-lru" @@ -70,7 +72,7 @@ type LightChain struct { // NewLightChain returns a fully initialised light chain using information // available in the database. It initialises the default Ethereum header // validator. -func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { +func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -106,8 +108,8 @@ func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *e // add trusted CHT if config.DAOForkSupport { WriteTrustedCht(bc.chainDb, TrustedCht{ - Number: 523, - Root: common.HexToHash("c9c3203ca7e58bf0ceaae50ed00c7ae234e7b3cc054aee086a1d0873f843df4e"), + Number: 637, + Root: common.HexToHash("01e408d9b1942f05dba1a879f3eaafe34d219edaeb8223fecf1244cc023d3e23"), }) } else { WriteTrustedCht(bc.chainDb, TrustedCht{ @@ -120,8 +122,8 @@ func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *e if bc.genesisBlock.Hash() == (common.Hash{12, 215, 134, 162, 66, 93, 22, 241, 82, 198, 88, 49, 108, 66, 62, 108, 225, 24, 30, 21, 195, 41, 88, 38, 215, 201, 144, 76, 186, 156, 227, 3}) { // add trusted CHT for testnet WriteTrustedCht(bc.chainDb, TrustedCht{ - Number: 319, - Root: common.HexToHash("43b679ff9b4918b0b19e6256f20e35877365ec3e20b38e3b2a02cef5606176dc"), + Number: 452, + Root: common.HexToHash("511da2c88e32b14cf4a4e62f7fcbb297139faebc260a4ab5eb43cce6edcba324"), }) glog.V(logger.Info).Infoln("Added trusted CHT for testnet") } else { diff --git a/vendor/github.com/ethereum/go-ethereum/light/odr_util.go b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go index 458f8233b..5c72f90e9 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/odr_util.go +++ b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package light import ( diff --git a/vendor/github.com/ethereum/go-ethereum/light/state.go b/vendor/github.com/ethereum/go-ethereum/light/state.go index 08bb8e5ba..9f2376809 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/state.go +++ b/vendor/github.com/ethereum/go-ethereum/light/state.go @@ -20,14 +20,12 @@ import ( "math/big" "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" "golang.org/x/net/context" ) -// StartingNonce determines the default nonce when new accounts are being created. -var StartingNonce uint64 - // LightState is a memory representation of a state. // This version is ODR capable, caching only the already accessed part of the // state, retrieving unknown parts on-demand from the ODR backend. Changes are @@ -120,9 +118,9 @@ func (self *LightState) GetState(ctx context.Context, a common.Address, b common return common.Hash{}, err } -// IsDeleted returns true if the given account has been marked for deletion +// HasSuicided returns true if the given account has been marked for deletion // or false if the account does not exist -func (self *LightState) IsDeleted(ctx context.Context, addr common.Address) (bool, error) { +func (self *LightState) HasSuicided(ctx context.Context, addr common.Address) (bool, error) { stateObject, err := self.GetStateObject(ctx, addr) if err == nil && stateObject != nil { return stateObject.remove, nil @@ -156,7 +154,7 @@ func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce func (self *LightState) SetCode(ctx context.Context, addr common.Address, code []byte) error { stateObject, err := self.GetOrNewStateObject(ctx, addr) if err == nil && stateObject != nil { - stateObject.SetCode(code) + stateObject.SetCode(crypto.Keccak256Hash(code), code) } return err } @@ -171,7 +169,7 @@ func (self *LightState) SetState(ctx context.Context, addr common.Address, key c } // Delete marks an account to be removed and clears its balance -func (self *LightState) Delete(ctx context.Context, addr common.Address) (bool, error) { +func (self *LightState) Suicide(ctx context.Context, addr common.Address) (bool, error) { stateObject, err := self.GetOrNewStateObject(ctx, addr) if err == nil && stateObject != nil { stateObject.MarkForDeletion() @@ -237,7 +235,6 @@ func (self *LightState) newStateObject(addr common.Address) *StateObject { } stateObject := NewStateObject(addr, self.odr) - stateObject.SetNonce(StartingNonce) self.stateObjects[addr.Str()] = stateObject return stateObject @@ -273,7 +270,9 @@ func (self *LightState) Copy() *LightState { state.trie = self.trie state.id = self.id for k, stateObject := range self.stateObjects { - state.stateObjects[k] = stateObject.Copy() + if stateObject.dirty { + state.stateObjects[k] = stateObject.Copy() + } } state.refund.Set(self.refund) diff --git a/vendor/github.com/ethereum/go-ethereum/light/state_object.go b/vendor/github.com/ethereum/go-ethereum/light/state_object.go index 0eb7d68b2..61c3888fe 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/state_object.go +++ b/vendor/github.com/ethereum/go-ethereum/light/state_object.go @@ -79,8 +79,6 @@ type StateObject struct { codeHash []byte // The code for this account code Code - // Temporarily initialisation code - initCode Code // Cached storage (flushed when updated) storage Storage @@ -190,8 +188,7 @@ func (self *StateObject) Copy() *StateObject { stateObject.codeHash = common.CopyBytes(self.codeHash) stateObject.nonce = self.nonce stateObject.trie = self.trie - stateObject.code = common.CopyBytes(self.code) - stateObject.initCode = common.CopyBytes(self.initCode) + stateObject.code = self.code stateObject.storage = self.storage.Copy() stateObject.remove = self.remove stateObject.dirty = self.dirty @@ -220,9 +217,9 @@ func (self *StateObject) Code() []byte { } // SetCode sets the contract code -func (self *StateObject) SetCode(code []byte) { +func (self *StateObject) SetCode(hash common.Hash, code []byte) { self.code = code - self.codeHash = crypto.Keccak256(code) + self.codeHash = hash[:] self.dirty = true } diff --git a/vendor/github.com/ethereum/go-ethereum/light/trie.go b/vendor/github.com/ethereum/go-ethereum/light/trie.go index 6de6b4849..c5525358a 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/trie.go +++ b/vendor/github.com/ethereum/go-ethereum/light/trie.go @@ -78,7 +78,7 @@ func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error) func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) { err = t.do(ctx, key, func() (err error) { if t.trie == nil { - t.trie, err = trie.NewSecure(t.id.Root, t.db) + t.trie, err = trie.NewSecure(t.id.Root, t.db, 0) } if err == nil { res, err = t.trie.TryGet(key) @@ -97,7 +97,7 @@ func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) { err = t.do(ctx, key, func() (err error) { if t.trie == nil { - t.trie, err = trie.NewSecure(t.id.Root, t.db) + t.trie, err = trie.NewSecure(t.id.Root, t.db, 0) } if err == nil { err = t.trie.TryUpdate(key, value) @@ -111,7 +111,7 @@ func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) { func (t *LightTrie) Delete(ctx context.Context, key []byte) (err error) { err = t.do(ctx, key, func() (err error) { if t.trie == nil { - t.trie, err = trie.NewSecure(t.id.Root, t.db) + t.trie, err = trie.NewSecure(t.id.Root, t.db, 0) } if err == nil { err = t.trie.TryDelete(key) diff --git a/vendor/github.com/ethereum/go-ethereum/light/txpool.go b/vendor/github.com/ethereum/go-ethereum/light/txpool.go index e94b2da95..3eaa0cf54 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/txpool.go +++ b/vendor/github.com/ethereum/go-ethereum/light/txpool.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "golang.org/x/net/context" ) @@ -42,7 +43,8 @@ var txPermanent = uint64(500) // always receive all locally signed transactions in the same order as they are // created. type TxPool struct { - config *core.ChainConfig + config *params.ChainConfig + signer types.Signer quit chan bool eventMux *event.TypeMux events event.Subscription @@ -76,9 +78,10 @@ type TxRelayBackend interface { } // NewTxPool creates a new light transaction pool -func NewTxPool(config *core.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool { +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool { pool := &TxPool{ config: config, + signer: types.NewEIP155Signer(config.ChainId), nonce: make(map[common.Address]uint64), pending: make(map[common.Hash]*types.Transaction), mined: make(map[common.Hash][]*types.Transaction), @@ -90,7 +93,7 @@ func NewTxPool(config *core.ChainConfig, eventMux *event.TypeMux, chain *LightCh odr: chain.Odr(), chainDb: chain.Odr().Database(), head: chain.CurrentHeader().Hash(), - clearIdx: chain.CurrentHeader().GetNumberU64(), + clearIdx: chain.CurrentHeader().Number.Uint64(), } go pool.eventLoop() @@ -197,7 +200,7 @@ func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, idx uin if len(receipts) != len(block.Transactions()) { panic(nil) // should never happen if hashes did match } - core.SetReceiptsData(block, receipts) + core.SetReceiptsData(pool.config, block, receipts) } //fmt.Println("WriteReceipt", receipts[i].TxHash) core.WriteReceipt(pool.chainDb, receipts[i]) @@ -241,11 +244,11 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx // find common ancestor, create list of rolled back and new block hashes var oldHashes, newHashes []common.Hash for oldh.Hash() != newh.Hash() { - if oldh.GetNumberU64() >= newh.GetNumberU64() { + if oldh.Number.Uint64() >= newh.Number.Uint64() { oldHashes = append(oldHashes, oldh.Hash()) oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1) } - if oldh.GetNumberU64() < newh.GetNumberU64() { + if oldh.Number.Uint64() < newh.Number.Uint64() { newHashes = append(newHashes, newh.Hash()) newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1) if newh == nil { @@ -254,8 +257,8 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx } } } - if oldh.GetNumberU64() < pool.clearIdx { - pool.clearIdx = oldh.GetNumberU64() + if oldh.Number.Uint64() < pool.clearIdx { + pool.clearIdx = oldh.Number.Uint64() } // roll back old blocks for _, hash := range oldHashes { @@ -265,14 +268,14 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx // check mined txs of new blocks (array is in reversed order) for i := len(newHashes) - 1; i >= 0; i-- { hash := newHashes[i] - if err := pool.checkMinedTxs(ctx, hash, newHeader.GetNumberU64()-uint64(i), txc); err != nil { + if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil { return txc, err } pool.head = hash } // clear old mined tx entries of old blocks - if idx := newHeader.GetNumberU64(); idx > pool.clearIdx+txPermanent { + if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent { idx2 := idx - txPermanent for i := pool.clearIdx; i < idx2; i++ { hash := core.GetCanonicalHash(pool.chainDb, i) @@ -308,6 +311,7 @@ func (pool *TxPool) eventLoop() { m, r := txc.getLists() pool.relay.NewHead(pool.head, m, r) pool.homestead = pool.config.IsHomestead(head.Number) + pool.signer = types.MakeSigner(pool.config, head.Number) pool.mu.Unlock() } } @@ -339,7 +343,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error // Validate the transaction sender and it's sig. Throw // if the from fields is invalid. - if from, err = tx.From(); err != nil { + if from, err = types.Sender(pool.signer, tx); err != nil { return core.ErrInvalidSender } @@ -388,7 +392,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - if tx.Gas().Cmp(core.IntrinsicGas(tx.Data(), core.MessageCreatesContract(tx), pool.homestead)) < 0 { + if tx.Gas().Cmp(core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)) < 0 { return core.ErrIntrinsicGas } @@ -412,7 +416,8 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { self.pending[hash] = tx nonce := tx.Nonce() + 1 - addr, _ := tx.From() + + addr, _ := types.Sender(self.signer, tx) if nonce > self.nonce[addr] { self.nonce[addr] = nonce } @@ -420,7 +425,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { // Notify the subscribers. This event is posted in a goroutine // because it's possible that somewhere during the post "Remove transaction" // gets called which will then wait for the global tx pool lock and deadlock. - go self.eventMux.Post(core.TxPreEvent{tx}) + go self.eventMux.Post(core.TxPreEvent{Tx: tx}) } if glog.V(logger.Debug) { @@ -432,7 +437,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { } // we can ignore the error here because From is // verified in ValidateTransaction. - f, _ := tx.From() + f, _ := types.Sender(self.signer, tx) from := common.Bytes2Hex(f[:4]) glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) } @@ -463,7 +468,7 @@ func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error { // AddTransactions adds all valid transactions to the pool and passes them to // the tx relay backend -func (self *TxPool) AddTransactions(ctx context.Context, txs []*types.Transaction) { +func (self *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) { self.mu.Lock() defer self.mu.Unlock() var sendTx types.Transactions @@ -510,24 +515,18 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { // Content retrieves the data content of the transaction pool, returning all the // pending as well as queued transactions, grouped by account and nonce. -func (self *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { +func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { self.mu.RLock() defer self.mu.RUnlock() // Retrieve all the pending transactions and sort by account and by nonce - pending := make(map[common.Address]map[uint64][]*types.Transaction) + pending := make(map[common.Address]types.Transactions) for _, tx := range self.pending { - account, _ := tx.From() - - owned, ok := pending[account] - if !ok { - owned = make(map[uint64][]*types.Transaction) - pending[account] = owned - } - owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + account, _ := types.Sender(self.signer, tx) + pending[account] = append(pending[account], tx) } // There are no queued transactions in a light pool, just return an empty map - queued := make(map[common.Address]map[uint64][]*types.Transaction) + queued := make(map[common.Address]types.Transactions) return pending, queued } diff --git a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go index d3c55eb82..5d330b072 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go +++ b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -23,6 +23,8 @@ import ( "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" ) @@ -33,56 +35,44 @@ import ( type VMEnv struct { vm.Environment ctx context.Context - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig evm *vm.EVM state *VMState header *types.Header msg core.Message depth int chain *LightChain - typ vm.Type - // structured logging - logs []vm.StructLog - err error + err error } // NewEnv creates a new execution environment based on an ODR capable light state -func NewEnv(ctx context.Context, state *LightState, chainConfig *core.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv { +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, - typ: vm.StdVmTy, } env.state = &VMState{ctx: ctx, state: state, env: env} - // if no log collector is present set self as the collector - if cfg.Logger.Collector == nil { - cfg.Logger.Collector = env - } - env.evm = vm.New(env, cfg) return env } -func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f } -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) VmType() vm.Type { return self.typ } -func (self *VMEnv) SetVmType(t vm.Type) { self.typ = t } +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.GetNumberU64() == n { + if header.Number.Uint64() == n { return header.Hash() } } @@ -97,12 +87,12 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool { return self.state.GetBalance(from).Cmp(balance) >= 0 } -func (self *VMEnv) MakeSnapshot() vm.Database { - return &VMState{ctx: self.ctx, state: self.state.state.Copy(), env: self} +func (self *VMEnv) SnapshotDatabase() int { + return self.state.SnapshotDatabase() } -func (self *VMEnv) SetSnapshot(copy vm.Database) { - self.state.state.Set(copy.(*VMState).state) +func (self *VMEnv) RevertToSnapshot(idx int) { + self.state.RevertToSnapshot(idx) } func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { @@ -116,18 +106,14 @@ func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, 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) } -func (self *VMEnv) StructLogs() []vm.StructLog { - return self.logs -} - -func (self *VMEnv) AddStructLog(log vm.StructLog) { - self.logs = append(self.logs, log) -} - // Error returns the error (if any) that happened during execution. func (self *VMEnv) Error() error { return self.err @@ -137,9 +123,10 @@ func (self *VMEnv) Error() error { // passes it to any state operation that requires it. type VMState struct { vm.Database - ctx context.Context - state *LightState - env *VMEnv + ctx context.Context + state *LightState + snapshots []*LightState + env *VMEnv } // errHandler handles and stores any state error that happens during execution. @@ -149,6 +136,16 @@ func (s *VMState) errHandler(err error) { } } +func (self *VMState) SnapshotDatabase() int { + self.snapshots = append(self.snapshots, self.state.Copy()) + return len(self.snapshots) - 1 +} + +func (self *VMState) RevertToSnapshot(idx int) { + self.state.Set(self.snapshots[idx]) + self.snapshots = self.snapshots[:idx] +} + // GetAccount returns the account object of the given account or nil if the // account does not exist func (s *VMState) GetAccount(addr common.Address) vm.Account { @@ -208,6 +205,20 @@ func (s *VMState) GetCode(addr common.Address) []byte { return res } +// GetCodeHash returns the contract code hash at the given address +func (s *VMState) GetCodeHash(addr common.Address) common.Hash { + res, err := s.state.GetCode(s.ctx, addr) + s.errHandler(err) + return crypto.Keccak256Hash(res) +} + +// GetCodeSize returns the contract code size at the given address +func (s *VMState) GetCodeSize(addr common.Address) int { + res, err := s.state.GetCode(s.ctx, addr) + s.errHandler(err) + return len(res) +} + // SetCode sets the contract code at the specified account func (s *VMState) SetCode(addr common.Address, code []byte) { err := s.state.SetCode(s.ctx, addr, code) @@ -238,9 +249,9 @@ func (s *VMState) SetState(addr common.Address, key common.Hash, value common.Ha s.errHandler(err) } -// Delete marks an account to be removed and clears its balance -func (s *VMState) Delete(addr common.Address) bool { - res, err := s.state.Delete(s.ctx, addr) +// Suicide marks an account to be removed and clears its balance +func (s *VMState) Suicide(addr common.Address) bool { + res, err := s.state.Suicide(s.ctx, addr) s.errHandler(err) return res } @@ -252,10 +263,10 @@ func (s *VMState) Exist(addr common.Address) bool { return res } -// IsDeleted returns true if the given account has been marked for deletion +// HasSuicided returns true if the given account has been marked for deletion // or false if the account does not exist -func (s *VMState) IsDeleted(addr common.Address) bool { - res, err := s.state.IsDeleted(s.ctx, addr) +func (s *VMState) HasSuicided(addr common.Address) bool { + res, err := s.state.HasSuicided(s.ctx, addr) s.errHandler(err) return res } diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go b/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go index fcf8b5c32..d756894f3 100644 --- a/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go +++ b/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/rcrowley/go-metrics" + "github.com/rcrowley/go-metrics/exp" ) // MetricsEnabledFlag is the CLI flag name to use to enable metrics collections. @@ -44,6 +45,16 @@ func init() { Enabled = true } } + exp.Exp(metrics.DefaultRegistry) +} + +// NewCounter create a new metrics Counter, either a real one of a NOP stub depending +// on the metrics flag. +func NewCounter(name string) metrics.Counter { + if !Enabled { + return new(metrics.NilCounter) + } + return metrics.GetOrRegisterCounter(name, metrics.DefaultRegistry) } // NewMeter create a new metrics Meter, either a real one of a NOP stub depending diff --git a/vendor/github.com/ethereum/go-ethereum/miner/agent.go b/vendor/github.com/ethereum/go-ethereum/miner/agent.go index 4a4683bc6..16f2a7723 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/agent.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/agent.go @@ -43,8 +43,10 @@ type CpuAgent struct { func NewCpuAgent(index int, pow pow.PoW) *CpuAgent { miner := &CpuAgent{ - pow: pow, - index: index, + pow: pow, + index: index, + quit: make(chan struct{}), + workCh: make(chan *Work, 1), } return miner @@ -55,25 +57,15 @@ func (self *CpuAgent) Pow() pow.PoW { return self.pow } func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch } func (self *CpuAgent) Stop() { - self.mu.Lock() - defer self.mu.Unlock() - close(self.quit) } func (self *CpuAgent) Start() { - self.mu.Lock() - defer self.mu.Unlock() if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) { return // agent already started } - self.quit = make(chan struct{}) - // creating current op ch makes sure we're not closing a nil ch - // later on - self.workCh = make(chan *Work, 1) - go self.update() } diff --git a/vendor/github.com/ethereum/go-ethereum/miner/miner.go b/vendor/github.com/ethereum/go-ethereum/miner/miner.go index c16cbe6ae..c85a1cd8e 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/miner.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/miner.go @@ -50,8 +50,6 @@ type Miner struct { worker *worker - MinAcceptedGasPrice *big.Int - threads int coinbase common.Address mining int32 @@ -62,7 +60,7 @@ type Miner struct { shouldStart int32 // should start indicates whether we should start after sync } -func New(eth Backend, config *core.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { miner := &Miner{ eth: eth, mux: mux, @@ -107,12 +105,15 @@ out: } } +func (m *Miner) GasPrice() *big.Int { + return new(big.Int).Set(m.worker.gasPrice) +} + func (m *Miner) SetGasPrice(price *big.Int) { // FIXME block tests set a nil gas price. Quick dirty fix if price == nil { return } - m.worker.setGasPrice(price) } diff --git a/vendor/github.com/ethereum/go-ethereum/miner/worker.go b/vendor/github.com/ethereum/go-ethereum/miner/worker.go index 59406bf4e..2933b6bd3 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/worker.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/worker.go @@ -63,18 +63,18 @@ type uint64RingBuffer struct { // Work is the workers current environment and holds // all of the current state information type Work struct { - config *core.ChainConfig - state *state.StateDB // apply state changes here - ancestors *set.Set // ancestor set (used for checking uncle parent validity) - family *set.Set // family set (used for checking uncle invalidity) - uncles *set.Set // uncle set - remove *set.Set // tx which will be removed - tcount int // tx count in cycle - ignoredTransactors *set.Set - lowGasTransactors *set.Set - ownedAccounts *set.Set - lowGasTxs types.Transactions - localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) + config *params.ChainConfig + signer types.Signer + + state *state.StateDB // apply state changes here + ancestors *set.Set // ancestor set (used for checking uncle parent validity) + family *set.Set // family set (used for checking uncle invalidity) + uncles *set.Set // uncle set + tcount int // tx count in cycle + ownedAccounts *set.Set + lowGasTxs types.Transactions + failedTxs types.Transactions + localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) Block *types.Block // the new block @@ -92,7 +92,7 @@ type Result struct { // worker is the main object which takes care of applying messages to the new state type worker struct { - config *core.ChainConfig + config *params.ChainConfig mu sync.Mutex @@ -130,7 +130,7 @@ type worker struct { fullValidation bool } -func newWorker(config *core.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { +func newWorker(config *params.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { worker := &worker{ config: config, eth: eth, @@ -171,9 +171,9 @@ func (self *worker) pending() (*types.Block, *state.StateDB) { self.current.txs, nil, self.current.receipts, - ), self.current.state + ), self.current.state.Copy() } - return self.current.Block, self.current.state + return self.current.Block, self.current.state.Copy() } func (self *worker) start() { @@ -236,7 +236,12 @@ func (self *worker) update() { // Apply transaction to the pending state if we're not mining if atomic.LoadInt32(&self.mining) == 0 { self.currentMu.Lock() - self.current.commitTransactions(self.mux, types.Transactions{ev.Tx}, self.gasPrice, self.chain) + + acc, _ := types.Sender(self.current.signer, ev.Tx) + txs := map[common.Address]types.Transactions{acc: types.Transactions{ev.Tx}} + txset := types.NewTransactionsByPriceAndNonce(txs) + + self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain) self.currentMu.Unlock() } } @@ -273,7 +278,7 @@ func (self *worker) wait() { } go self.mux.Post(core.NewMinedBlockEvent{Block: block}) } else { - work.state.Commit() + work.state.Commit(self.config.IsEIP158(block.Number())) parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { glog.V(logger.Error).Infoln("Invalid block found during mining") @@ -358,12 +363,13 @@ func (self *worker) push(work *Work) { // makeCurrent creates a new environment for the current cycle. func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { - state, err := state.New(parent.Root(), self.eth.ChainDb()) + state, err := self.chain.StateAt(parent.Root()) if err != nil { return err } work := &Work{ config: self.config, + signer: types.NewEIP155Signer(self.config.ChainId), state: state, ancestors: set.New(), family: set.New(), @@ -383,10 +389,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error accounts := self.eth.AccountManager().Accounts() // Keep track of transactions which return errors so they can be removed - work.remove = set.New() work.tcount = 0 - work.ignoredTransactors = set.New() - work.lowGasTransactors = set.New() work.ownedAccounts = accountAddressesSet(accounts) if self.current != nil { work.localMinedBlocks = self.current.localMinedBlocks @@ -495,45 +498,11 @@ func (self *worker) commitNewWork() { if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { core.ApplyDAOHardFork(work.state) } + txs := types.NewTransactionsByPriceAndNonce(self.eth.TxPool().Pending()) + work.commitTransactions(self.mux, txs, self.gasPrice, self.chain) - /* //approach 1 - transactions := self.eth.TxPool().GetTransactions() - sort.Sort(types.TxByNonce(transactions)) - */ - - //approach 2 - transactions := self.eth.TxPool().GetTransactions() - types.SortByPriceAndNonce(transactions) - - /* // approach 3 - // commit transactions for this run. - txPerOwner := make(map[common.Address]types.Transactions) - // Sort transactions by owner - for _, tx := range self.eth.TxPool().GetTransactions() { - from, _ := tx.From() // we can ignore the sender error - txPerOwner[from] = append(txPerOwner[from], tx) - } - var ( - singleTxOwner types.Transactions - multiTxOwner types.Transactions - ) - // Categorise transactions by - // 1. 1 owner tx per block - // 2. multi txs owner per block - for _, txs := range txPerOwner { - if len(txs) == 1 { - singleTxOwner = append(singleTxOwner, txs[0]) - } else { - multiTxOwner = append(multiTxOwner, txs...) - } - } - sort.Sort(types.TxByPrice(singleTxOwner)) - sort.Sort(types.TxByNonce(multiTxOwner)) - transactions := append(singleTxOwner, multiTxOwner...) - */ - - work.commitTransactions(self.mux, transactions, self.gasPrice, self.chain) - self.eth.TxPool().RemoveTransactions(work.lowGasTxs) + self.eth.TxPool().RemoveBatch(work.lowGasTxs) + self.eth.TxPool().RemoveBatch(work.failedTxs) // compute uncles for the new block. var ( @@ -562,7 +531,7 @@ func (self *worker) commitNewWork() { if atomic.LoadInt32(&self.mining) == 1 { // commit state root after all state transitions. core.AccumulateRewards(work.state, header, uncles) - header.Root = work.state.IntermediateRoot() + header.Root = work.state.IntermediateRoot(self.config.IsEIP158(header.Number)) } // create the new block whose nonce will be mined. @@ -591,65 +560,62 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error { return nil } -func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) { +func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) { gp := new(core.GasPool).AddGas(env.header.GasLimit) var coalescedLogs vm.Logs - for _, tx := range transactions { + + for { + // Retrieve the next transaction and abort if all done + tx := txs.Peek() + if tx == nil { + break + } // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. - from, _ := tx.From() + // + // We use the eip155 signer regardless of the current hf. + from, _ := types.Sender(env.signer, tx) + // Check whether the tx is replay protected. If we're not in the EIP155 hf + // phase, start ignoring the sender until we do. + if tx.Protected() && !env.config.IsEIP155(env.header.Number) { + glog.V(logger.Detail).Infof("Transaction (%x) is replay protected, but we haven't yet hardforked. Transaction will be ignored until we hardfork.\n", tx.Hash()) - // Check if it falls within margin. Txs from owned accounts are always processed. + txs.Pop() + continue + } + + // Ignore any transactions (and accounts subsequently) with low gas limits if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { - // ignore the transaction and transactor. We ignore the transactor - // because nonce will fail after ignoring this transaction so there's - // no point - env.lowGasTransactors.Add(from) + // Pop the current low-priced transaction without shifting in the next from the account + glog.V(logger.Info).Infof("Transaction (%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4]) - glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4]) - } + env.lowGasTxs = append(env.lowGasTxs, tx) + txs.Pop() - // Continue with the next transaction if the transaction sender is included in - // the low gas tx set. This will also remove the tx and all sequential transaction - // from this transactor - if env.lowGasTransactors.Has(from) { - // add tx to the low gas set. This will be removed at the end of the run - // owned accounts are ignored - if !env.ownedAccounts.Has(from) { - env.lowGasTxs = append(env.lowGasTxs, tx) - } continue } - - // Move on to the next transaction when the transactor is in ignored transactions set - // This may occur when a transaction hits the gas limit. When a gas limit is hit and - // the transaction is processed (that could potentially be included in the block) it - // will throw a nonce error because the previous transaction hasn't been processed. - // Therefor we need to ignore any transaction after the ignored one. - if env.ignoredTransactors.Has(from) { - continue - } - - env.state.StartRecord(tx.Hash(), common.Hash{}, 0) + // Start executing the transaction + env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) err, logs := env.commitTransaction(tx, bc, gp) switch { case core.IsGasLimitErr(err): - // ignore the transactor so no nonce errors will be thrown for this account - // next time the worker is run, they'll be picked up again. - env.ignoredTransactors.Add(from) - + // Pop the current out-of-gas transaction without shifting in the next from the account glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) - case err != nil: - env.remove.Add(tx.Hash()) + txs.Pop() + + case err != nil: + // Pop the current failed transaction without shifting in the next from the account + glog.V(logger.Detail).Infof("Transaction (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) + env.failedTxs = append(env.failedTxs, tx) + txs.Pop() - if glog.V(logger.Detail) { - glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) - } default: - env.tcount++ + // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + txs.Shift() } } if len(coalescedLogs) > 0 || env.tcount > 0 { @@ -665,18 +631,11 @@ func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Trans } func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { - snap := env.state.Copy() + snap := env.state.Snapshot() - // this is a bit of a hack to force jit for the miners - config := env.config.VmConfig - if !(config.EnableJit && config.ForceJit) { - config.EnableJit = false - } - config.ForceJit = false // disable forcing jit - - receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) + receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) if err != nil { - env.state.Set(snap) + env.state.RevertToSnapshot(snap) return err, nil } env.txs = append(env.txs, tx) diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go new file mode 100644 index 000000000..41498b6f0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go @@ -0,0 +1,181 @@ +// 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 . + +// Contains all the wrappers from the accounts package to support client side key +// management on mobile platforms. + +package geth + +import ( + "errors" + "time" + + "github.com/ethereum/go-ethereum/accounts" +) + +const ( + // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptN = int(accounts.StandardScryptN) + + // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptP = int(accounts.StandardScryptP) + + // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptN = int(accounts.LightScryptN) + + // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptP = int(accounts.LightScryptP) +) + +// Account represents a stored key. +type Account struct{ account accounts.Account } + +// Accounts represents a slice of accounts. +type Accounts struct{ accounts []accounts.Account } + +// Size returns the number of accounts in the slice. +func (a *Accounts) Size() int { + return len(a.accounts) +} + +// Get returns the account at the given index from the slice. +func (a *Accounts) Get(index int) (*Account, error) { + if index < 0 || index >= len(a.accounts) { + return nil, errors.New("index out of bounds") + } + return &Account{a.accounts[index]}, nil +} + +// Set sets the account at the given index in the slice. +func (a *Accounts) Set(index int, account *Account) error { + if index < 0 || index >= len(a.accounts) { + return errors.New("index out of bounds") + } + a.accounts[index] = account.account + return nil +} + +// GetAddress retrieves the address associated with the account. +func (a *Account) GetAddress() *Address { + return &Address{a.account.Address} +} + +// GetFile retrieves the path of the file containing the account key. +func (a *Account) GetFile() string { + return a.account.File +} + +// AccountManager manages a key storage directory on disk. +type AccountManager struct{ manager *accounts.Manager } + +// NewAccountManager creates a manager for the given directory. +func NewAccountManager(keydir string, scryptN, scryptP int) *AccountManager { + return &AccountManager{manager: accounts.NewManager(keydir, scryptN, scryptP)} +} + +// HasAddress reports whether a key with the given address is present. +func (am *AccountManager) HasAddress(addr *Address) bool { + return am.manager.HasAddress(addr.address) +} + +// GetAccounts returns all key files present in the directory. +func (am *AccountManager) GetAccounts() *Accounts { + return &Accounts{am.manager.Accounts()} +} + +// DeleteAccount deletes the key matched by account if the passphrase is correct. +// If a contains no filename, the address must match a unique key. +func (am *AccountManager) DeleteAccount(a *Account, passphrase string) error { + return am.manager.DeleteAccount(accounts.Account{ + Address: a.account.Address, + File: a.account.File, + }, passphrase) +} + +// Sign signs hash with an unlocked private key matching the given address. +func (am *AccountManager) Sign(addr *Address, hash []byte) ([]byte, error) { + return am.manager.Sign(addr.address, hash) +} + +// SignWithPassphrase signs hash if the private key matching the given address can be +// decrypted with the given passphrase. +func (am *AccountManager) SignWithPassphrase(addr *Address, passphrase string, hash []byte) ([]byte, error) { + return am.manager.SignWithPassphrase(addr.address, passphrase, hash) +} + +// Unlock unlocks the given account indefinitely. +func (am *AccountManager) Unlock(a *Account, passphrase string) error { + return am.manager.TimedUnlock(a.account, passphrase, 0) +} + +// Lock removes the private key with the given address from memory. +func (am *AccountManager) Lock(addr *Address) error { + return am.manager.Lock(addr.address) +} + +// TimedUnlock unlocks the given account with the passphrase. The account +// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account +// until the program exits. The account must match a unique key file. +// +// If the account address is already unlocked for a duration, TimedUnlock extends or +// shortens the active unlock timeout. If the address was previously unlocked +// indefinitely the timeout is not altered. +func (am *AccountManager) TimedUnlock(a *Account, passphrase string, timeout int64) error { + return am.manager.TimedUnlock(a.account, passphrase, time.Duration(timeout)) +} + +// NewAccount generates a new key and stores it into the key directory, +// encrypting it with the passphrase. +func (am *AccountManager) NewAccount(passphrase string) (*Account, error) { + account, err := am.manager.NewAccount(passphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} + +// ExportKey exports as a JSON key, encrypted with newPassphrase. +func (am *AccountManager) ExportKey(a *Account, passphrase, newPassphrase string) ([]byte, error) { + return am.manager.Export(a.account, passphrase, newPassphrase) +} + +// ImportKey stores the given encrypted JSON key into the key directory. +func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (*Account, error) { + account, err := am.manager.Import(keyJSON, passphrase, newPassphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} + +// Update changes the passphrase of an existing account. +func (am *AccountManager) Update(a *Account, passphrase, newPassphrase string) error { + return am.manager.Update(a.account, passphrase, newPassphrase) +} + +// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores +// a key file in the key directory. The key file is encrypted with the same passphrase. +func (am *AccountManager) ImportPreSaleKey(keyJSON []byte, passphrase string) (*Account, error) { + account, err := am.manager.ImportPreSaleKey(keyJSON, passphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/big.go b/vendor/github.com/ethereum/go-ethereum/mobile/big.go new file mode 100644 index 000000000..6a358ba28 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/big.go @@ -0,0 +1,95 @@ +// 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 . + +// Contains all the wrappers from the math/big package. + +package geth + +import ( + "errors" + "math/big" +) + +// A BigInt represents a signed multi-precision integer. +type BigInt struct { + bigint *big.Int +} + +// NewBigInt allocates and returns a new BigInt set to x. +func NewBigInt(x int64) *BigInt { + return &BigInt{big.NewInt(x)} +} + +// GetBytes returns the absolute value of x as a big-endian byte slice. +func (bi *BigInt) GetBytes() []byte { + return bi.bigint.Bytes() +} + +// String returns the value of x as a formatted decimal string. +func (bi *BigInt) String() string { + return bi.bigint.String() +} + +// GetInt64 returns the int64 representation of x. If x cannot be represented in +// an int64, the result is undefined. +func (bi *BigInt) GetInt64() int64 { + return bi.bigint.Int64() +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets +// the big int to that value. +func (bi *BigInt) SetBytes(buf []byte) { + bi.bigint.SetBytes(buf) +} + +// SetInt64 sets the big int to x. +func (bi *BigInt) SetInt64(x int64) { + bi.bigint.SetInt64(x) +} + +// SetString sets the big int to x. +// +// The string prefix determines the actual conversion base. A prefix of "0x" or +// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix +// selects base 2. Otherwise the selected base is 10. +func (bi *BigInt) SetString(x string, base int) { + bi.bigint.SetString(x, base) +} + +// BigInts represents a slice of big ints. +type BigInts struct{ bigints []*big.Int } + +// Size returns the number of big ints in the slice. +func (bi *BigInts) Size() int { + return len(bi.bigints) +} + +// Get returns the bigint at the given index from the slice. +func (bi *BigInts) Get(index int) (*BigInt, error) { + if index < 0 || index >= len(bi.bigints) { + return nil, errors.New("index out of bounds") + } + return &BigInt{bi.bigints[index]}, nil +} + +// Set sets the big int at the given index in the slice. +func (bi *BigInts) Set(index int, bigint *BigInt) error { + if index < 0 || index >= len(bi.bigints) { + return errors.New("index out of bounds") + } + bi.bigints[index] = bigint.bigint + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go b/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go new file mode 100644 index 000000000..0447e1f66 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go @@ -0,0 +1,26 @@ +// 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 . + +// Contains the wrappers from the math/big package that require Go 1.7 and above. + +// +build go1.7 + +package geth + +// GetString returns the value of x as a formatted string in some number base. +func (bi *BigInt) GetString(base int) string { + return bi.bigint.Text(base) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/bind.go b/vendor/github.com/ethereum/go-ethereum/mobile/bind.go new file mode 100644 index 000000000..50adc6b0f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/bind.go @@ -0,0 +1,202 @@ +// 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 . + +// Contains all the wrappers from the bind package. + +package geth + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Signer is an interaface defining the callback when a contract requires a +// method to sign the transaction before submission. +type Signer interface { + Sign(*Address, *Transaction) (*Transaction, error) +} + +type signer struct { + sign bind.SignerFn +} + +func (s *signer) Sign(addr *Address, tx *Transaction) (*Transaction, error) { + sig, err := s.sign(types.HomesteadSigner{}, addr.address, tx.tx) + if err != nil { + return nil, err + } + return &Transaction{sig}, nil +} + +// CallOpts is the collection of options to fine tune a contract call request. +type CallOpts struct { + opts bind.CallOpts +} + +// NewCallOpts creates a new option set for contract calls. +func NewCallOpts() *CallOpts { + return new(CallOpts) +} + +func (opts *CallOpts) IsPending() bool { return opts.opts.Pending } +func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ } + +// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// Even then it's awkward to unpack the subtleties of a Go context out to Java. +// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} } + +func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending } +func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ } +func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context } + +// TransactOpts is the collection of authorization data required to create a +// valid Ethereum transaction. +type TransactOpts struct { + opts bind.TransactOpts +} + +func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} } +func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() } +func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} } +func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} } +func (opts *TransactOpts) GetGasLimit() int64 { return opts.opts.GasLimit.Int64() } + +// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} } + +// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// Even then it's awkward to unpack the subtleties of a Go context out to Java. +//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} } + +func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address } +func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) } +func (opts *TransactOpts) SetSigner(s Signer) { + opts.opts.Signer = func(signer types.Signer, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { + sig, err := s.Sign(&Address{addr}, &Transaction{tx}) + if err != nil { + return nil, err + } + return sig.tx, nil + } +} +func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint } +func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint } +func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = big.NewInt(limit) } +func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context } + +// BoundContract is the base wrapper object that reflects a contract on the +// Ethereum network. It contains a collection of methods that are used by the +// higher level contract bindings to operate. +type BoundContract struct { + contract *bind.BoundContract + address common.Address + deployer *types.Transaction +} + +// DeployContract deploys a contract onto the Ethereum blockchain and binds the +// deployment address with a wrapper. +func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (*BoundContract, error) { + // Convert all the deployment parameters to Go types + params := make([]interface{}, len(args.objects)) + for i, obj := range args.objects { + params[i] = obj + } + // Deploy the contract to the network + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + return nil, err + } + addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, bytecode, client.client, params...) + if err != nil { + return nil, err + } + return &BoundContract{ + contract: bound, + address: addr, + deployer: tx, + }, nil +} + +// BindContract creates a low level contract interface through which calls and +// transactions may be made through. +func BindContract(address *Address, abiJSON string, client *EthereumClient) (*BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + return nil, err + } + return &BoundContract{ + contract: bind.NewBoundContract(address.address, parsed, client.client, client.client), + address: address.address, + }, nil +} + +func (c *BoundContract) GetAddress() *Address { return &Address{c.address} } +func (c *BoundContract) GetDeployer() *Transaction { + if c.deployer == nil { + return nil + } + return &Transaction{c.deployer} +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. +func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { + // Convert all the input and output parameters to Go types + params := make([]interface{}, len(args.objects)) + for i, obj := range args.objects { + params[i] = obj + } + results := make([]interface{}, len(out.objects)) + for i, obj := range out.objects { + results[i] = obj + } + // Execute the call to the contract and wrap any results + if err := c.contract.Call(&opts.opts, &results, method, params...); err != nil { + return err + } + for i, res := range results { + out.objects[i] = res + } + return nil +} + +// Transact invokes the (paid) contract method with params as input values. +func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (*Transaction, error) { + params := make([]interface{}, len(args.objects)) + for i, obj := range args.objects { + params[i] = obj + } + tx, err := c.contract.Transact(&opts.opts, method, params) + if err != nil { + return nil, err + } + return &Transaction{tx}, nil +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (c *BoundContract) Transfer(opts *TransactOpts) (*Transaction, error) { + tx, err := c.contract.Transfer(&opts.opts) + if err != nil { + return nil, err + } + return &Transaction{tx}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/common.go b/vendor/github.com/ethereum/go-ethereum/mobile/common.go new file mode 100644 index 000000000..ab1810bf1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/common.go @@ -0,0 +1,187 @@ +// 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 . + +// Contains all the wrappers from the common package. + +package geth + +import ( + "encoding/hex" + "errors" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// Hash represents the 32 byte Keccak256 hash of arbitrary data. +type Hash struct { + hash common.Hash +} + +// NewHashFromBytes converts a slice of bytes to a hash value. +func NewHashFromBytes(hash []byte) (*Hash, error) { + h := new(Hash) + if err := h.SetBytes(hash); err != nil { + return nil, err + } + return h, nil +} + +// NewHashFromHex converts a hex string to a hash value. +func NewHashFromHex(hash string) (*Hash, error) { + h := new(Hash) + if err := h.SetHex(hash); err != nil { + return nil, err + } + return h, nil +} + +// SetBytes sets the specified slice of bytes as the hash value. +func (h *Hash) SetBytes(hash []byte) error { + if length := len(hash); length != common.HashLength { + return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength) + } + copy(h.hash[:], hash) + return nil +} + +// GetBytes retrieves the byte representation of the hash. +func (h *Hash) GetBytes() []byte { + return h.hash[:] +} + +// SetHex sets the specified hex string as the hash value. +func (h *Hash) SetHex(hash string) error { + hash = strings.ToLower(hash) + if len(hash) >= 2 && hash[:2] == "0x" { + hash = hash[2:] + } + if length := len(hash); length != 2*common.HashLength { + return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength) + } + bin, err := hex.DecodeString(hash) + if err != nil { + return err + } + copy(h.hash[:], bin) + return nil +} + +// GetHex retrieves the hex string representation of the hash. +func (h *Hash) GetHex() string { + return h.hash.Hex() +} + +// Hashes represents a slice of hashes. +type Hashes struct{ hashes []common.Hash } + +// Size returns the number of hashes in the slice. +func (h *Hashes) Size() int { + return len(h.hashes) +} + +// Get returns the hash at the given index from the slice. +func (h *Hashes) Get(index int) (*Hash, error) { + if index < 0 || index >= len(h.hashes) { + return nil, errors.New("index out of bounds") + } + return &Hash{h.hashes[index]}, nil +} + +// Address represents the 20 byte address of an Ethereum account. +type Address struct { + address common.Address +} + +// NewAddressFromBytes converts a slice of bytes to a hash value. +func NewAddressFromBytes(address []byte) (*Address, error) { + a := new(Address) + if err := a.SetBytes(address); err != nil { + return nil, err + } + return a, nil +} + +// NewAddressFromHex converts a hex string to a address value. +func NewAddressFromHex(address string) (*Address, error) { + a := new(Address) + if err := a.SetHex(address); err != nil { + return nil, err + } + return a, nil +} + +// SetBytes sets the specified slice of bytes as the address value. +func (a *Address) SetBytes(address []byte) error { + if length := len(address); length != common.AddressLength { + return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength) + } + copy(a.address[:], address) + return nil +} + +// GetBytes retrieves the byte representation of the address. +func (a *Address) GetBytes() []byte { + return a.address[:] +} + +// SetHex sets the specified hex string as the address value. +func (a *Address) SetHex(address string) error { + address = strings.ToLower(address) + if len(address) >= 2 && address[:2] == "0x" { + address = address[2:] + } + if length := len(address); length != 2*common.AddressLength { + return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength) + } + bin, err := hex.DecodeString(address) + if err != nil { + return err + } + copy(a.address[:], bin) + return nil +} + +// GetHex retrieves the hex string representation of the address. +func (a *Address) GetHex() string { + return a.address.Hex() +} + +// Addresses represents a slice of addresses. +type Addresses struct{ addresses []common.Address } + +// Size returns the number of addresses in the slice. +func (a *Addresses) Size() int { + return len(a.addresses) +} + +// Get returns the address at the given index from the slice. +func (a *Addresses) Get(index int) (*Address, error) { + if index < 0 || index >= len(a.addresses) { + return nil, errors.New("index out of bounds") + } + return &Address{a.addresses[index]}, nil +} + +// Set sets the address at the given index in the slice. +func (a *Addresses) Set(index int, address *Address) error { + if index < 0 || index >= len(a.addresses) { + return errors.New("index out of bounds") + } + a.addresses[index] = address.address + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/context.go b/vendor/github.com/ethereum/go-ethereum/mobile/context.go new file mode 100644 index 000000000..9df94b689 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/context.go @@ -0,0 +1,81 @@ +// 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 . + +// Contains all the wrappers from the golang.org/x/net/context package to support +// client side context management on mobile platforms. + +package geth + +import ( + "time" + + "golang.org/x/net/context" +) + +// Context carries a deadline, a cancelation signal, and other values across API +// boundaries. +type Context struct { + context context.Context + cancel context.CancelFunc +} + +// NewContext returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming requests. +func NewContext() *Context { + return &Context{ + context: context.Background(), + } +} + +// WithCancel returns a copy of the original context with cancellation mechanism +// included. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithCancel() *Context { + child, cancel := context.WithCancel(c.context) + return &Context{ + context: child, + cancel: cancel, + } +} + +// WithDeadline returns a copy of the original context with the deadline adjusted +// to be no later than the specified time. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithDeadline(sec int64, nsec int64) *Context { + child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec)) + return &Context{ + context: child, + cancel: cancel, + } +} + +// WithTimeout returns a copy of the original context with the deadline adjusted +// to be no later than now + the duration specified. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithTimeout(nsec int64) *Context { + child, cancel := context.WithTimeout(c.context, time.Duration(nsec)) + return &Context{ + context: child, + cancel: cancel, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/discover.go b/vendor/github.com/ethereum/go-ethereum/mobile/discover.go new file mode 100644 index 000000000..9df2d04c3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/discover.go @@ -0,0 +1,104 @@ +// 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 . + +// Contains all the wrappers from the accounts package to support client side enode +// management on mobile platforms. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/p2p/discv5" +) + +// Enode represents a host on the network. +type Enode struct { + node *discv5.Node +} + +// NewEnode parses a node designator. +// +// There are two basic forms of node designators +// - incomplete nodes, which only have the public key (node ID) +// - complete nodes, which contain the public key and IP/Port information +// +// For incomplete nodes, the designator must look like one of these +// +// enode:// +// +// +// For complete nodes, the node ID is encoded in the username portion +// of the URL, separated from the host by an @ sign. The hostname can +// only be given as an IP address, DNS domain names are not allowed. +// The port in the host name section is the TCP listening port. If the +// TCP and UDP (discovery) ports differ, the UDP port is specified as +// query parameter "discport". +// +// In the following example, the node URL describes +// a node with IP address 10.3.58.6, TCP listening port 30303 +// and UDP discovery port 30301. +// +// enode://@10.3.58.6:30303?discport=30301 +func NewEnode(rawurl string) (*Enode, error) { + node, err := discv5.ParseNode(rawurl) + if err != nil { + return nil, err + } + return &Enode{node}, nil +} + +// Enodes represents a slice of accounts. +type Enodes struct{ nodes []*discv5.Node } + +// NewEnodes creates a slice of uninitialized enodes. +func NewEnodes(size int) *Enodes { + return &Enodes{ + nodes: make([]*discv5.Node, size), + } +} + +// NewEnodesEmpty creates an empty slice of Enode values. +func NewEnodesEmpty() *Enodes { + return NewEnodes(0) +} + +// Size returns the number of enodes in the slice. +func (e *Enodes) Size() int { + return len(e.nodes) +} + +// Get returns the enode at the given index from the slice. +func (e *Enodes) Get(index int) (*Enode, error) { + if index < 0 || index >= len(e.nodes) { + return nil, errors.New("index out of bounds") + } + return &Enode{e.nodes[index]}, nil +} + +// Set sets the enode at the given index in the slice. +func (e *Enodes) Set(index int, enode *Enode) error { + if index < 0 || index >= len(e.nodes) { + return errors.New("index out of bounds") + } + e.nodes[index] = enode.node + return nil +} + +// Append adds a new enode element to the end of the slice. +func (e *Enodes) Append(enode *Enode) { + e.nodes = append(e.nodes, enode.node) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/doc.go b/vendor/github.com/ethereum/go-ethereum/mobile/doc.go new file mode 100644 index 000000000..50cc7f4f8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/doc.go @@ -0,0 +1,57 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package geth contains the simplified mobile APIs to go-ethereum. +// +// The scope of this package is *not* to allow writing a custom Ethereun client +// with pieces plucked from go-ethereum, rather to allow writing native dapps on +// mobile platforms. Keep this in mind when using or extending this package! +// +// API limitations +// +// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the +// exposed APIs need to be manually wrapped into simplified types, with custom +// constructors and getters/setters to ensure that they can be meaninfully used +// from Java/ObjC too. +// +// With this in mind, please try to limit the scope of this package and only add +// essentials without which mobile support cannot work, especially since manually +// syncing the code will be unwieldy otherwise. In the long term we might consider +// writing custom library generators, but those are out of scope now. +// +// Content wise each file in this package corresponds to an entire Go package +// from the go-ethereum repository. Please adhere to this scoping to prevent this +// package getting unmaintainable. +// +// Wrapping guidelines: +// +// Every type that is to be exposed should be wrapped into its own plain struct, +// which internally contains a single field: the original go-ethereum version. +// This is needed because gomobile cannot expose named types for now. +// +// Whenever a method argument or a return type is a custom struct, the pointer +// variant should always be used as value types crossing over between language +// boundaries might have strange behaviors. +// +// Slices of types should be converted into a single multiplicative type wrapping +// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations +// should not be provided to limit the remote code complexity. Arrays should be +// avoided as much as possible since they complicate bounds checking. +// +// Note, a panic *cannot* cross over language boundaries, instead will result in +// an undebuggable SEGFAULT in the process. For error handling only ever use error +// returns, which may be the only or the second return. +package geth diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go new file mode 100644 index 000000000..668d65e32 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go @@ -0,0 +1,305 @@ +// 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 . + +// Contains a wrapper for the Ethereum client. + +package geth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethclient" +) + +// EthereumClient provides access to the Ethereum APIs. +type EthereumClient struct { + client *ethclient.Client +} + +// NewEthereumClient connects a client to the given URL. +func NewEthereumClient(rawurl string) (*EthereumClient, error) { + client, err := ethclient.Dial(rawurl) + return &EthereumClient{client}, err +} + +// GetBlockByHash returns the given full block. +func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (*Block, error) { + block, err := ec.client.BlockByHash(ctx.context, hash.hash) + return &Block{block}, err +} + +// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the +// latest known block is returned. +func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (*Block, error) { + if number < 0 { + block, err := ec.client.BlockByNumber(ctx.context, nil) + return &Block{block}, err + } + block, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number)) + return &Block{block}, err +} + +// GetHeaderByHash returns the block header with the given hash. +func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (*Header, error) { + header, err := ec.client.HeaderByHash(ctx.context, hash.hash) + return &Header{header}, err +} + +// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0, +// the latest known header is returned. +func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (*Header, error) { + if number < 0 { + header, err := ec.client.HeaderByNumber(ctx.context, nil) + return &Header{header}, err + } + header, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number)) + return &Header{header}, err +} + +// 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) + return &Transaction{tx}, err +} + +// GetTransactionCount returns the total number of transactions in the given block. +func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (int, error) { + count, err := ec.client.TransactionCount(ctx.context, hash.hash) + return int(count), err +} + +// GetTransactionInBlock returns a single transaction at index in the given block. +func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (*Transaction, error) { + tx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index)) + return &Transaction{tx}, err + +} + +// GetTransactionReceipt returns the receipt of a transaction by transaction hash. +// Note that the receipt is not available for pending transactions. +func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (*Receipt, error) { + receipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash) + return &Receipt{receipt}, err +} + +// SyncProgress retrieves the current progress of the sync algorithm. If there's +// no sync currently running, it returns nil. +func (ec *EthereumClient) SyncProgress(ctx *Context) (*SyncProgress, error) { + progress, err := ec.client.SyncProgress(ctx.context) + if progress == nil { + return nil, err + } + return &SyncProgress{*progress}, err +} + +// NewHeadHandler is a client-side subscription callback to invoke on events and +// subscription failure. +type NewHeadHandler interface { + OnNewHead(header *Header) + OnError(failure string) +} + +// SubscribeNewHead subscribes to notifications about the current blockchain head +// on the given channel. +func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (*Subscription, error) { + // Subscribe to the event internally + ch := make(chan *types.Header, buffer) + sub, err := ec.client.SubscribeNewHead(ctx.context, ch) + if err != nil { + return nil, err + } + // Start up a dispatcher to feed into the callback + go func() { + for { + select { + case header := <-ch: + handler.OnNewHead(&Header{header}) + + case err := <-sub.Err(): + handler.OnError(err.Error()) + return + } + } + }() + return &Subscription{sub}, nil +} + +// State Access + +// GetBalanceAt returns the wei balance of the given account. +// The block number can be <0, in which case the balance is taken from the latest known block. +func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (*BigInt, error) { + if number < 0 { + balance, err := ec.client.BalanceAt(ctx.context, account.address, nil) + return &BigInt{balance}, err + } + balance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number)) + return &BigInt{balance}, err +} + +// GetStorageAt returns the value of key in the contract storage of the given account. +// The block number can be <0, in which case the value is taken from the latest known block. +func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) ([]byte, error) { + if number < 0 { + return ec.client.StorageAt(ctx.context, account.address, key.hash, nil) + } + return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number)) +} + +// GetCodeAt returns the contract code of the given account. +// The block number can be <0, in which case the code is taken from the latest known block. +func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) ([]byte, error) { + if number < 0 { + return ec.client.CodeAt(ctx.context, account.address, nil) + } + return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number)) +} + +// GetNonceAt returns the account nonce of the given account. +// The block number can be <0, in which case the nonce is taken from the latest known block. +func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (int64, error) { + if number < 0 { + nonce, err := ec.client.NonceAt(ctx.context, account.address, nil) + return int64(nonce), err + } + nonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number)) + return int64(nonce), err +} + +// Filters + +// FilterLogs executes a filter query. +func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (*Logs, error) { + logs, err := ec.client.FilterLogs(ctx.context, query.query) + if err != nil { + return nil, err + } + // Temp hack due to vm.Logs being []*vm.Log + res := make(vm.Logs, len(logs)) + for i, log := range logs { + res[i] = &log + } + return &Logs{res}, nil +} + +// FilterLogsHandler is a client-side subscription callback to invoke on events and +// subscription failure. +type FilterLogsHandler interface { + OnFilterLogs(log *Log) + OnError(failure string) +} + +// SubscribeFilterLogs subscribes to the results of a streaming filter query. +func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (*Subscription, error) { + // Subscribe to the event internally + ch := make(chan vm.Log, buffer) + sub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch) + if err != nil { + return nil, err + } + // Start up a dispatcher to feed into the callback + go func() { + for { + select { + case log := <-ch: + handler.OnFilterLogs(&Log{&log}) + + case err := <-sub.Err(): + handler.OnError(err.Error()) + return + } + } + }() + return &Subscription{sub}, nil +} + +// Pending State + +// GetPendingBalanceAt returns the wei balance of the given account in the pending state. +func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (*BigInt, error) { + balance, err := ec.client.PendingBalanceAt(ctx.context, account.address) + return &BigInt{balance}, err +} + +// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state. +func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) ([]byte, error) { + return ec.client.PendingStorageAt(ctx.context, account.address, key.hash) +} + +// GetPendingCodeAt returns the contract code of the given account in the pending state. +func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) ([]byte, error) { + return ec.client.PendingCodeAt(ctx.context, account.address) +} + +// GetPendingNonceAt 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 *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (int64, error) { + nonce, err := ec.client.PendingNonceAt(ctx.context, account.address) + return int64(nonce), err +} + +// GetPendingTransactionCount returns the total number of transactions in the pending state. +func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (int, error) { + count, err := ec.client.PendingTransactionCount(ctx.context) + return int(count), err +} + +// Contract Calling + +// CallContract executes a message call transaction, which is directly executed in the VM +// of the node, but never mined into the blockchain. +// +// blockNumber selects the block height at which the call runs. It can be <0, in which +// case the code is taken from the latest known block. Note that state from very old +// blocks might not be available. +func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) ([]byte, error) { + if number < 0 { + return ec.client.CallContract(ctx.context, msg.msg, nil) + } + return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number)) +} + +// PendingCallContract executes a message call transaction using the EVM. +// The state seen by the contract call is the pending state. +func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) ([]byte, error) { + return ec.client.PendingCallContract(ctx.context, msg.msg) +} + +// SuggestGasPrice retrieves the currently suggested gas price to allow a timely +// execution of a transaction. +func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (*BigInt, error) { + price, err := ec.client.SuggestGasPrice(ctx.context) + return &BigInt{price}, err +} + +// EstimateGas tries to estimate the gas needed to execute a specific transaction based on +// the current pending state of the backend blockchain. There is no guarantee that this is +// 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 *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (*BigInt, error) { + price, err := ec.client.EstimateGas(ctx.context, msg.msg) + return &BigInt{price}, err +} + +// SendTransaction injects a signed transaction into the pending pool for execution. +// +// If the transaction was a contract creation use the TransactionReceipt method to get the +// contract address after the transaction has been mined. +func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { + return ec.client.SendTransaction(ctx.context, tx.tx) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go b/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go new file mode 100644 index 000000000..6e8046ac9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go @@ -0,0 +1,125 @@ +// 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 . + +// Contains all the wrappers from the go-ethereum root package. + +package geth + +import ( + "errors" + "math/big" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" +) + +// Subscription represents an event subscription where events are +// delivered on a data channel. +type Subscription struct { + sub ethereum.Subscription +} + +// Unsubscribe cancels the sending of events to the data channel +// and closes the error channel. +func (s *Subscription) Unsubscribe() { + s.sub.Unsubscribe() +} + +// CallMsg contains parameters for contract calls. +type CallMsg struct { + msg ethereum.CallMsg +} + +// NewCallMsg creates an empty contract call parameter list. +func NewCallMsg() *CallMsg { + return new(CallMsg) +} + +func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} } +func (msg *CallMsg) GetGas() int64 { return msg.msg.Gas.Int64() } +func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} } +func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} } +func (msg *CallMsg) GetData() []byte { return msg.msg.Data } +func (msg *CallMsg) GetTo() *Address { + if to := msg.msg.To; to != nil { + return &Address{*msg.msg.To} + } + return nil +} + +func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address } +func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = big.NewInt(gas) } +func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint } +func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint } +func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = data } +func (msg *CallMsg) SetTo(address *Address) { + if address == nil { + msg.msg.To = nil + } + msg.msg.To = &address.address +} + +// SyncProgress gives progress indications when the node is synchronising with +// the Ethereum network. +type SyncProgress struct { + progress ethereum.SyncProgress +} + +func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } +func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } +func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } +func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) } +func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) } + +// Topics is a set of topic lists to filter events with. +type Topics struct{ topics [][]common.Hash } + +// Size returns the number of topic lists inside the set +func (t *Topics) Size() int { + return len(t.topics) +} + +// Get returns the topic list at the given index from the slice. +func (t *Topics) Get(index int) (*Hashes, error) { + if index < 0 || index >= len(t.topics) { + return nil, errors.New("index out of bounds") + } + return &Hashes{t.topics[index]}, nil +} + +// Set sets the topic list at the given index in the slice. +func (t *Topics) Set(index int, topics *Hashes) error { + if index < 0 || index >= len(t.topics) { + return errors.New("index out of bounds") + } + t.topics[index] = topics.hashes + return nil +} + +// FilterQuery contains options for contact log filtering. +type FilterQuery struct { + query ethereum.FilterQuery +} + +// NewFilterQuery creates an empty filter query for contact log filtering. +func NewFilterQuery() *FilterQuery { + return new(FilterQuery) +} + +func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} } +func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} } +func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} } +func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go new file mode 100644 index 000000000..738c0c548 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go @@ -0,0 +1,191 @@ +// 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 . + +// Contains all the wrappers from the node package to support client side node +// management on mobile platforms. + +package geth + +import ( + "fmt" + "math/big" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/whisper/whisperv2" +) + +// NodeConfig represents the collection of configuration values to fine tune the Geth +// node embedded into a mobile process. The available values are a subset of the +// entire API provided by go-ethereum to reduce the maintenance surface and dev +// complexity. +type NodeConfig struct { + // Bootstrap nodes used to establish connectivity with the rest of the network. + BootstrapNodes *Enodes + + // MaxPeers is the maximum number of peers that can be connected. If this is + // set to zero, then only the configured static and trusted peers can connect. + MaxPeers int + + // EthereumEnabled specifies whether the node should run the Ethereum protocol. + EthereumEnabled bool + + // EthereumNetworkID is the network identifier used by the Ethereum protocol to + // decide if remote peers should be accepted or not. + EthereumNetworkID int + + // EthereumChainConfig is the default parameters of the blockchain to use. If no + // configuration is specified, it defaults to the main network. + EthereumChainConfig *ChainConfig + + // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An + // empty genesis state is equivalent to using the mainnet's state. + EthereumGenesis string + + // EthereumDatabaseCache is the system memory in MB to allocate for database caching. + // A minimum of 16MB is always reserved. + EthereumDatabaseCache int + + // WhisperEnabled specifies whether the node should run the Whisper protocol. + WhisperEnabled bool +} + +// defaultNodeConfig contains the default node configuration values to use if all +// or some fields are missing from the user's specified list. +var defaultNodeConfig = &NodeConfig{ + BootstrapNodes: FoundationBootnodes(), + MaxPeers: 25, + EthereumEnabled: true, + EthereumNetworkID: 1, + EthereumChainConfig: MainnetChainConfig(), + EthereumDatabaseCache: 16, +} + +// NewNodeConfig creates a new node option set, initialized to the default values. +func NewNodeConfig() *NodeConfig { + config := *defaultNodeConfig + return &config +} + +// Node represents a Geth Ethereum node instance. +type Node struct { + node *node.Node +} + +// NewNode creates and configures a new Geth node. +func NewNode(datadir string, config *NodeConfig) (*Node, error) { + // If no or partial configurations were specified, use defaults + if config == nil { + config = NewNodeConfig() + } + if config.MaxPeers == 0 { + config.MaxPeers = defaultNodeConfig.MaxPeers + } + if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 { + config.BootstrapNodes = defaultNodeConfig.BootstrapNodes + } + // Create the empty networking stack + nodeConf := &node.Config{ + Name: clientIdentifier, + DataDir: datadir, + KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! + NoDiscovery: true, + DiscoveryV5: true, + DiscoveryV5Addr: ":0", + BootstrapNodesV5: config.BootstrapNodes.nodes, + ListenAddr: ":0", + NAT: nat.Any(), + MaxPeers: config.MaxPeers, + } + stack, err := node.New(nodeConf) + if err != nil { + return nil, err + } + // Register the Ethereum protocol if requested + if config.EthereumEnabled { + ethConf := ð.Config{ + ChainConfig: ¶ms.ChainConfig{ + ChainId: big.NewInt(config.EthereumChainConfig.ChainID), + HomesteadBlock: big.NewInt(config.EthereumChainConfig.HomesteadBlock), + DAOForkBlock: big.NewInt(config.EthereumChainConfig.DAOForkBlock), + DAOForkSupport: config.EthereumChainConfig.DAOForkSupport, + EIP150Block: big.NewInt(config.EthereumChainConfig.EIP150Block), + EIP150Hash: config.EthereumChainConfig.EIP150Hash.hash, + EIP155Block: big.NewInt(config.EthereumChainConfig.EIP155Block), + EIP158Block: big.NewInt(config.EthereumChainConfig.EIP158Block), + }, + Genesis: config.EthereumGenesis, + LightMode: true, + DatabaseCache: config.EthereumDatabaseCache, + NetworkId: config.EthereumNetworkID, + GasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), + GpoMinGasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), + GpoMaxGasPrice: new(big.Int).Mul(big.NewInt(500), common.Shannon), + GpoFullBlockRatio: 80, + GpobaseStepDown: 10, + GpobaseStepUp: 100, + GpobaseCorrectionFactor: 110, + } + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return les.New(ctx, ethConf) + }); err != nil { + return nil, fmt.Errorf("ethereum init: %v", err) + } + } + // Register the Whisper protocol if requested + if config.WhisperEnabled { + if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisperv2.New(), nil }); err != nil { + return nil, fmt.Errorf("whisper init: %v", err) + } + } + return &Node{stack}, nil +} + +// Start creates a live P2P node and starts running it. +func (n *Node) Start() error { + return n.node.Start() +} + +// Stop terminates a running node along with all it's services. In the node was +// not started, an error is returned. +func (n *Node) Stop() error { + return n.node.Stop() +} + +// GetEthereumClient retrieves a client to access the Ethereum subsystem. +func (n *Node) GetEthereumClient() (*EthereumClient, error) { + rpc, err := n.node.Attach() + if err != nil { + return nil, err + } + return &EthereumClient{ethclient.NewClient(rpc)}, nil +} + +// GetNodeInfo gathers and returns a collection of metadata known about the host. +func (n *Node) GetNodeInfo() *NodeInfo { + return &NodeInfo{n.node.Server().NodeInfo()} +} + +// GetPeersInfo returns an array of metadata objects describing connected peers. +func (n *Node) GetPeersInfo() *PeerInfos { + return &PeerInfos{n.node.Server().PeersInfo()} +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go new file mode 100644 index 000000000..8e4ebe638 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build android + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "GethDroid" diff --git a/vendor/github.com/ethereum/go-ethereum/release/generator.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go similarity index 85% rename from vendor/github.com/ethereum/go-ethereum/release/generator.go rename to vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go index 1553e0612..307cd0858 100644 --- a/vendor/github.com/ethereum/go-ethereum/release/generator.go +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go @@ -14,6 +14,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go +// +build ios -package release +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "iGeth" diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go new file mode 100644 index 000000000..6f0c5dda6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !android,!ios + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "GethMobile" diff --git a/vendor/github.com/ethereum/go-ethereum/eth/cpu_mining.go b/vendor/github.com/ethereum/go-ethereum/mobile/init.go similarity index 55% rename from vendor/github.com/ethereum/go-ethereum/eth/cpu_mining.go rename to vendor/github.com/ethereum/go-ethereum/mobile/init.go index 3469d394e..0fbc6bd3e 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/cpu_mining.go +++ b/vendor/github.com/ethereum/go-ethereum/mobile/init.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -14,41 +14,22 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// +build !opencl +// Contains initialization code for the mbile library. -package eth +package geth import ( - "errors" - "fmt" + "runtime" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) -const disabledInfo = "Set GO_OPENCL and re-build to enable." +func init() { + // Initialize the logger + glog.SetV(logger.Info) + glog.SetToStderr(true) -func (s *Ethereum) StartMining(threads int, gpus string) error { - eb, err := s.Etherbase() - if err != nil { - err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) - glog.V(logger.Error).Infoln(err) - return err - } - - if gpus != "" { - return errors.New("GPU mining disabled. " + disabledInfo) - } - - // CPU mining - go s.miner.Start(eb, threads) - return nil -} - -func GPUBench(gpuid uint64) { - fmt.Println("GPU mining disabled. " + disabledInfo) -} - -func PrintOpenCLDevices() { - fmt.Println("OpenCL disabled. " + disabledInfo) + // Initialize the goroutine count + runtime.GOMAXPROCS(runtime.NumCPU()) } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/interface.go b/vendor/github.com/ethereum/go-ethereum/mobile/interface.go new file mode 100644 index 000000000..b585b8642 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/interface.go @@ -0,0 +1,148 @@ +// 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 . + +// Contains perverted wrappers to allow crossing over empty interfaces. + +package geth + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// Interface represents a wrapped version of Go's interface{}, with the capacity +// to store arbitrary data types. +// +// Since it's impossible to get the arbitrary-ness converted between Go and mobile +// platforms, we're using explicit getters and setters for the conversions. There +// is of course no point in enumerating everything, just enough to support the +// contract bindins requiring client side generated code. +type Interface struct { + object interface{} +} + +// NewInterface creates a new empty interface that can be used to pass around +// generic types. +func NewInterface() *Interface { + return new(Interface) +} + +func (i *Interface) SetBool(b bool) { i.object = &b } +func (i *Interface) SetBools(bs []bool) { i.object = &bs } +func (i *Interface) SetString(str string) { i.object = &str } +func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs } +func (i *Interface) SetBinary(binary []byte) { i.object = &binary } +func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries } +func (i *Interface) SetAddress(address *Address) { i.object = &address.address } +func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses } +func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash } +func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes } +func (i *Interface) SetInt8(n int8) { i.object = &n } +func (i *Interface) SetInt16(n int16) { i.object = &n } +func (i *Interface) SetInt32(n int32) { i.object = &n } +func (i *Interface) SetInt64(n int64) { i.object = &n } +func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint64(bigint *BigInt) { n := uint64(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint } +func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints } + +func (i *Interface) SetDefaultBool() { i.object = new(bool) } +func (i *Interface) SetDefaultBools() { i.object = new([]bool) } +func (i *Interface) SetDefaultString() { i.object = new(string) } +func (i *Interface) SetDefaultStrings() { i.object = new([]string) } +func (i *Interface) SetDefaultBinary() { i.object = new([]byte) } +func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) } +func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) } +func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) } +func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) } +func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) } +func (i *Interface) SetDefaultInt8() { i.object = new(int8) } +func (i *Interface) SetDefaultInt16() { i.object = new(int16) } +func (i *Interface) SetDefaultInt32() { i.object = new(int32) } +func (i *Interface) SetDefaultInt64() { i.object = new(int64) } +func (i *Interface) SetDefaultUint8() { i.object = new(uint8) } +func (i *Interface) SetDefaultUint16() { i.object = new(uint16) } +func (i *Interface) SetDefaultUint32() { i.object = new(uint32) } +func (i *Interface) SetDefaultUint64() { i.object = new(uint64) } +func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) } +func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) } + +func (i *Interface) GetBool() bool { return *i.object.(*bool) } +func (i *Interface) GetBools() []bool { return *i.object.(*[]bool) } +func (i *Interface) GetString() string { return *i.object.(*string) } +func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} } +func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) } +func (i *Interface) GetBinaries() [][]byte { return *i.object.(*[][]byte) } +func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} } +func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} } +func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} } +func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} } +func (i *Interface) GetInt8() int8 { return *i.object.(*int8) } +func (i *Interface) GetInt16() int16 { return *i.object.(*int16) } +func (i *Interface) GetInt32() int32 { return *i.object.(*int32) } +func (i *Interface) GetInt64() int64 { return *i.object.(*int64) } +func (i *Interface) GetUint8() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))} +} +func (i *Interface) GetUint16() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))} +} +func (i *Interface) GetUint32() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))} +} +func (i *Interface) GetUint64() *BigInt { + return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))} +} +func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} } +func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} } + +// Interfaces is a slices of wrapped generic objects. +type Interfaces struct { + objects []interface{} +} + +// NewInterfaces creates a slice of uninitialized interfaces. +func NewInterfaces(size int) *Interfaces { + return &Interfaces{ + objects: make([]interface{}, size), + } +} + +// Size returns the number of interfaces in the slice. +func (i *Interfaces) Size() int { + return len(i.objects) +} + +// Get returns the bigint at the given index from the slice. +func (i *Interfaces) Get(index int) (*Interface, error) { + if index < 0 || index >= len(i.objects) { + return nil, errors.New("index out of bounds") + } + return &Interface{i.objects[index]}, nil +} + +// Set sets the big int at the given index in the slice. +func (i *Interfaces) Set(index int, object *Interface) error { + if index < 0 || index >= len(i.objects) { + return errors.New("index out of bounds") + } + i.objects[index] = object.object + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go b/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go new file mode 100644 index 000000000..97ae626dc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received pi copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains wrappers for the p2p package. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/p2p" +) + +// NodeInfo represents pi short summary of the information known about the host. +type NodeInfo struct { + info *p2p.NodeInfo +} + +func (ni *NodeInfo) GetID() string { return ni.info.ID } +func (ni *NodeInfo) GetName() string { return ni.info.Name } +func (ni *NodeInfo) GetEnode() string { return ni.info.Enode } +func (ni *NodeInfo) GetIP() string { return ni.info.IP } +func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery } +func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener } +func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr } +func (ni *NodeInfo) GetProtocols() *Strings { + protos := []string{} + for proto, _ := range ni.info.Protocols { + protos = append(protos, proto) + } + return &Strings{protos} +} + +// PeerInfo represents pi short summary of the information known about pi connected peer. +type PeerInfo struct { + info *p2p.PeerInfo +} + +func (pi *PeerInfo) GetID() string { return pi.info.ID } +func (pi *PeerInfo) GetName() string { return pi.info.Name } +func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} } +func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress } +func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress } + +// PeerInfos represents a slice of infos about remote peers. +type PeerInfos struct { + infos []*p2p.PeerInfo +} + +// Size returns the number of peer info entries in the slice. +func (pi *PeerInfos) Size() int { + return len(pi.infos) +} + +// Get returns the peer info at the given index from the slice. +func (pi *PeerInfos) Get(index int) (*PeerInfo, error) { + if index < 0 || index >= len(pi.infos) { + return nil, errors.New("index out of bounds") + } + return &PeerInfo{pi.infos[index]}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/params.go b/vendor/github.com/ethereum/go-ethereum/mobile/params.go new file mode 100644 index 000000000..507c15349 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/params.go @@ -0,0 +1,92 @@ +// 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 . + +// Contains all the wrappers from the params package. + +package geth + +import ( + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/params" +) + +// MainnetChainConfig returns the chain configurations for the main Ethereum network. +func MainnetChainConfig() *ChainConfig { + return &ChainConfig{ + ChainID: params.MainNetChainID.Int64(), + HomesteadBlock: params.MainNetHomesteadBlock.Int64(), + DAOForkBlock: params.MainNetDAOForkBlock.Int64(), + DAOForkSupport: true, + EIP150Block: params.MainNetHomesteadGasRepriceBlock.Int64(), + EIP150Hash: Hash{params.MainNetHomesteadGasRepriceHash}, + EIP155Block: params.MainNetSpuriousDragon.Int64(), + EIP158Block: params.MainNetSpuriousDragon.Int64(), + } +} + +// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It +// is actually empty since that defaults to the hard coded binary genesis block. +func MainnetGenesis() string { + return "" +} + +// TestnetChainConfig returns the chain configurations for the Ethereum test network. +func TestnetChainConfig() *ChainConfig { + return &ChainConfig{ + ChainID: params.TestNetChainID.Int64(), + HomesteadBlock: params.TestNetHomesteadBlock.Int64(), + DAOForkBlock: 0, + DAOForkSupport: true, + EIP150Block: params.TestNetHomesteadGasRepriceBlock.Int64(), + EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash}, + EIP155Block: params.TestNetSpuriousDragon.Int64(), + EIP158Block: params.TestNetSpuriousDragon.Int64(), + } +} + +// TestnetGenesis returns the JSON spec to use for the Ethereum test network. +func TestnetGenesis() string { + return core.DefaultTestnetGenesisBlock() +} + +// ChainConfig is the core config which determines the blockchain settings. +type ChainConfig struct { + ChainID int64 // Chain ID for replay protection + HomesteadBlock int64 // Homestead switch block + DAOForkBlock int64 // TheDAO hard-fork switch block + DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork + EIP150Block int64 // Homestead gas reprice switch block + EIP150Hash Hash // Homestead gas reprice switch block hash + EIP155Block int64 // Replay protection switch block + EIP158Block int64 // Empty account pruning switch block +} + +// NewChainConfig creates a new chain configuration that transitions immediately +// to homestead and has no notion of the DAO fork (ideal for a private network). +func NewChainConfig() *ChainConfig { + return new(ChainConfig) +} + +// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated +// by the foundation running the V5 discovery protocol. +func FoundationBootnodes() *Enodes { + nodes := &Enodes{nodes: make([]*discv5.Node, len(params.DiscoveryV5Bootnodes))} + for i, node := range params.DiscoveryV5Bootnodes { + nodes.nodes[i] = node + } + return nodes +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go b/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go new file mode 100644 index 000000000..28f402d4f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go @@ -0,0 +1,54 @@ +// 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 s copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains various wrappers for primitive types. + +package geth + +import ( + "errors" + "fmt" +) + +// Strings represents s slice of strs. +type Strings struct{ strs []string } + +// Size returns the number of strs in the slice. +func (s *Strings) Size() int { + return len(s.strs) +} + +// Get returns the string at the given index from the slice. +func (s *Strings) Get(index int) (string, error) { + if index < 0 || index >= len(s.strs) { + return "", errors.New("index out of bounds") + } + return s.strs[index], nil +} + +// Set sets the string at the given index in the slice. +func (s *Strings) Set(index int, str string) error { + if index < 0 || index >= len(s.strs) { + return errors.New("index out of bounds") + } + s.strs[index] = str + return nil +} + +// String implements the Stringer interface. +func (s *Strings) String() string { + return fmt.Sprintf("%v", s.strs) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/types.go b/vendor/github.com/ethereum/go-ethereum/mobile/types.go new file mode 100644 index 000000000..bb5ccc625 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/types.go @@ -0,0 +1,189 @@ +// 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 . + +// Contains all the wrappers from the core/types package. + +package geth + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/core/types" +) + +// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that +// a sufficient amount of computation has been carried out on a block. +type Nonce struct { + nonce types.BlockNonce +} + +// GetBytes retrieves the byte representation of the block nonce. +func (n *Nonce) GetBytes() []byte { + return n.nonce[:] +} + +// GetHex retrieves the hex string representation of the block nonce. +func (n *Nonce) GetHex() string { + return fmt.Sprintf("0x%x", n.nonce[:]) +} + +// Bloom represents a 256 bit bloom filter. +type Bloom struct { + bloom types.Bloom +} + +// GetBytes retrieves the byte representation of the bloom filter. +func (b *Bloom) GetBytes() []byte { + return b.bloom[:] +} + +// GetHex retrieves the hex string representation of the bloom filter. +func (b *Bloom) GetHex() string { + return fmt.Sprintf("0x%x", b.bloom[:]) +} + +// Header represents a block header in the Ethereum blockchain. +type Header struct { + header *types.Header +} + +func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} } +func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} } +func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} } +func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} } +func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} } +func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} } +func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} } +func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} } +func (h *Header) GetNumber() int64 { return h.header.Number.Int64() } +func (h *Header) GetGasLimit() int64 { return h.header.GasLimit.Int64() } +func (h *Header) GetGasUsed() int64 { return h.header.GasUsed.Int64() } +func (h *Header) GetTime() int64 { return h.header.Time.Int64() } +func (h *Header) GetExtra() []byte { return h.header.Extra } +func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} } +func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} } + +func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} } +func (h *Header) GetHashNoNonce() *Hash { return &Hash{h.header.HashNoNonce()} } + +// Headers represents a slice of headers. +type Headers struct{ headers []*types.Header } + +// Size returns the number of headers in the slice. +func (h *Headers) Size() int { + return len(h.headers) +} + +// Get returns the header at the given index from the slice. +func (h *Headers) Get(index int) (*Header, error) { + if index < 0 || index >= len(h.headers) { + return nil, errors.New("index out of bounds") + } + return &Header{h.headers[index]}, nil +} + +// Block represents an entire block in the Ethereum blockchain. +type Block struct { + block *types.Block +} + +func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} } +func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} } +func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} } +func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} } +func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} } +func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} } +func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} } +func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} } +func (b *Block) GetNumber() int64 { return b.block.Number().Int64() } +func (b *Block) GetGasLimit() int64 { return b.block.GasLimit().Int64() } +func (b *Block) GetGasUsed() int64 { return b.block.GasUsed().Int64() } +func (b *Block) GetTime() int64 { return b.block.Time().Int64() } +func (b *Block) GetExtra() []byte { return b.block.Extra() } +func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} } +func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) } + +func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} } +func (b *Block) GetHashNoNonce() *Hash { return &Hash{b.block.HashNoNonce()} } + +func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} } +func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} } +func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} } +func (b *Block) GetTransaction(hash *Hash) *Transaction { + return &Transaction{b.block.Transaction(hash.hash)} +} + +// Transaction represents a single Ethereum transaction. +type Transaction struct { + tx *types.Transaction +} + +func (tx *Transaction) GetData() []byte { return tx.tx.Data() } +func (tx *Transaction) GetGas() int64 { return tx.tx.Gas().Int64() } +func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} } +func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} } +func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) } + +func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} } +func (tx *Transaction) GetSigHash() *Hash { return &Hash{tx.tx.SigHash(types.HomesteadSigner{})} } +func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} } + +func (tx *Transaction) GetFrom() (*Address, error) { + from, err := types.Sender(types.HomesteadSigner{}, tx.tx) + return &Address{from}, err +} + +func (tx *Transaction) GetTo() *Address { + if to := tx.tx.To(); to != nil { + return &Address{*to} + } + return nil +} + +func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) { + t, err := tx.tx.WithSignature(types.HomesteadSigner{}, sig) + return &Transaction{t}, err +} + +// Transactions represents a slice of transactions. +type Transactions struct{ txs types.Transactions } + +// Size returns the number of transactions in the slice. +func (t *Transactions) Size() int { + return len(t.txs) +} + +// Get returns the transaction at the given index from the slice. +func (t *Transactions) Get(index int) (*Transaction, error) { + if index < 0 || index >= len(t.txs) { + return nil, errors.New("index out of bounds") + } + return &Transaction{t.txs[index]}, nil +} + +// Receipt represents the results of a transaction. +type Receipt struct { + receipt *types.Receipt +} + +func (r *Receipt) GetPostState() []byte { return r.receipt.PostState } +func (r *Receipt) GetCumulativeGasUsed() *BigInt { return &BigInt{r.receipt.CumulativeGasUsed} } +func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} } +func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} } +func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} } +func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} } +func (r *Receipt) GetGasUsed() *BigInt { return &BigInt{r.receipt.GasUsed} } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/vm.go b/vendor/github.com/ethereum/go-ethereum/mobile/vm.go new file mode 100644 index 000000000..a68917ca6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/vm.go @@ -0,0 +1,56 @@ +// 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 . + +// Contains all the wrappers from the core/types package. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/core/vm" +) + +// Log represents a contract log event. These events are generated by the LOG +// opcode and stored/indexed by the node. +type Log struct { + log *vm.Log +} + +func (l *Log) GetAddress() *Address { return &Address{l.log.Address} } +func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} } +func (l *Log) GetData() []byte { return l.log.Data } +func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) } +func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} } +func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) } +func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} } +func (l *Log) GetIndex() int { return int(l.log.Index) } + +// Logs represents a slice of VM logs. +type Logs struct{ logs vm.Logs } + +// Size returns the number of logs in the slice. +func (l *Logs) Size() int { + return len(l.logs) +} + +// Get returns the log at the given index from the slice. +func (l *Logs) Get(index int) (*Log, error) { + if index < 0 || index >= len(l.logs) { + return nil, errors.New("index out of bounds") + } + return &Log{l.logs[index]}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/api.go b/vendor/github.com/ethereum/go-ethereum/node/api.go index 3523874ab..631e92c8e 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/api.go +++ b/vendor/github.com/ethereum/go-ethereum/node/api.go @@ -84,17 +84,17 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *rpc.HexNumber, cors *st } if host == nil { - h := common.DefaultHTTPHost - if api.node.httpHost != "" { - h = api.node.httpHost + h := DefaultHTTPHost + if api.node.config.HTTPHost != "" { + h = api.node.config.HTTPHost } host = &h } if port == nil { - port = rpc.NewHexNumber(api.node.httpPort) + port = rpc.NewHexNumber(api.node.config.HTTPPort) } if cors == nil { - cors = &api.node.httpCors + cors = &api.node.config.HTTPCors } modules := api.node.httpWhitelist @@ -133,20 +133,20 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *rpc.HexNumber, allowedOr } if host == nil { - h := common.DefaultWSHost - if api.node.wsHost != "" { - h = api.node.wsHost + h := DefaultWSHost + if api.node.config.WSHost != "" { + h = api.node.config.WSHost } host = &h } if port == nil { - port = rpc.NewHexNumber(api.node.wsPort) + port = rpc.NewHexNumber(api.node.config.WSPort) } if allowedOrigins == nil { - allowedOrigins = &api.node.wsOrigins + allowedOrigins = &api.node.config.WSOrigins } - modules := api.node.wsWhitelist + modules := api.node.config.WSModules if apis != nil { modules = nil for _, m := range strings.Split(*apis, ",") { diff --git a/vendor/github.com/ethereum/go-ethereum/node/config.go b/vendor/github.com/ethereum/go-ethereum/node/config.go index 432da7015..8d85b7ff8 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/config.go +++ b/vendor/github.com/ethereum/go-ethereum/node/config.go @@ -18,7 +18,6 @@ package node import ( "crypto/ecdsa" - "encoding/json" "fmt" "io/ioutil" "net" @@ -33,6 +32,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/discv5" "github.com/ethereum/go-ethereum/p2p/nat" ) @@ -48,6 +48,18 @@ var ( // P2P network layer of a protocol stack. These values can be further extended by // all registered services. type Config struct { + // Name sets the instance name of the node. It must not contain the / character and is + // used in the devp2p node identifier. The instance name of geth is "geth". If no + // value is specified, the basename of the current executable is used. + Name string + + // UserIdent, if set, is used as an additional component in the devp2p node identifier. + UserIdent string + + // Version should be set to the version number of the program. It is used + // in the devp2p node identifier. + Version string + // DataDir is the file system folder the node should use for any data storage // requirements. The configured data directory will not be directly shared with // registered services, instead those can use utility methods to create/access @@ -80,17 +92,24 @@ type Config struct { // needed. PrivateKey *ecdsa.PrivateKey - // Name sets the node name of this server. Use common.MakeName to create a name - // that follows existing conventions. - Name string - // NoDiscovery specifies whether the peer discovery mechanism should be started // or not. Disabling is usually useful for protocol debugging (manual topology). NoDiscovery bool - // Bootstrap nodes used to establish connectivity with the rest of the network. + // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery + // protocol should be started or not. + DiscoveryV5 bool + + // Listener address for the V5 discovery protocol UDP traffic. + DiscoveryV5Addr string + + // BootstrapNodes used to establish connectivity with the rest of the network. BootstrapNodes []*discover.Node + // BootstrapNodesV5 used to establish connectivity with the rest of the network + // using the V5 discovery protocol. + BootstrapNodesV5 []*discv5.Node + // Network interface address on which the node should listen for inbound peers. ListenAddr string @@ -178,9 +197,23 @@ func (c *Config) IPCEndpoint() string { return c.IPCPath } +// NodeDB returns the path to the discovery node database. +func (c *Config) NodeDB() string { + if c.DataDir == "" { + return "" // ephemeral + } + return c.resolvePath("nodes") +} + // DefaultIPCEndpoint returns the IPC path used by default. -func DefaultIPCEndpoint() string { - config := &Config{DataDir: common.DefaultDataDir(), IPCPath: common.DefaultIPCSocket} +func DefaultIPCEndpoint(clientIdentifier string) string { + if clientIdentifier == "" { + clientIdentifier = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") + if clientIdentifier == "" { + panic("empty executable name") + } + } + config := &Config{DataDir: DefaultDataDir(), IPCPath: clientIdentifier + ".ipc"} return config.IPCEndpoint() } @@ -195,7 +228,7 @@ func (c *Config) HTTPEndpoint() string { // DefaultHTTPEndpoint returns the HTTP endpoint used by default. func DefaultHTTPEndpoint() string { - config := &Config{HTTPHost: common.DefaultHTTPHost, HTTPPort: common.DefaultHTTPPort} + config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort} return config.HTTPEndpoint() } @@ -210,19 +243,87 @@ func (c *Config) WSEndpoint() string { // DefaultWSEndpoint returns the websocket endpoint used by default. func DefaultWSEndpoint() string { - config := &Config{WSHost: common.DefaultWSHost, WSPort: common.DefaultWSPort} + config := &Config{WSHost: DefaultWSHost, WSPort: DefaultWSPort} return config.WSEndpoint() } +// NodeName returns the devp2p node identifier. +func (c *Config) NodeName() string { + name := c.name() + // Backwards compatibility: previous versions used title-cased "Geth", keep that. + if name == "geth" || name == "geth-testnet" { + name = "Geth" + } + if c.UserIdent != "" { + name += "/" + c.UserIdent + } + if c.Version != "" { + name += "/v" + c.Version + } + name += "/" + runtime.GOOS + name += "/" + runtime.Version() + return name +} + +func (c *Config) name() string { + if c.Name == "" { + progname := strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") + if progname == "" { + panic("empty executable name, set Config.Name") + } + return progname + } + return c.Name +} + +// These resources are resolved differently for "geth" instances. +var isOldGethResource = map[string]bool{ + "chaindata": true, + "nodes": true, + "nodekey": true, + "static-nodes.json": true, + "trusted-nodes.json": true, +} + +// resolvePath resolves path in the instance directory. +func (c *Config) resolvePath(path string) string { + if filepath.IsAbs(path) { + return path + } + if c.DataDir == "" { + return "" + } + // Backwards-compatibility: ensure that data directory files created + // by geth 1.4 are used if they exist. + if c.name() == "geth" && isOldGethResource[path] { + oldpath := "" + if c.Name == "geth" { + oldpath = filepath.Join(c.DataDir, path) + } + if oldpath != "" && common.FileExist(oldpath) { + // TODO: print warning + return oldpath + } + } + return filepath.Join(c.instanceDir(), path) +} + +func (c *Config) instanceDir() string { + if c.DataDir == "" { + return "" + } + return filepath.Join(c.DataDir, c.name()) +} + // NodeKey retrieves the currently configured private key of the node, checking // first any manually set key, falling back to the one found in the configured // data folder. If no key can be found, a new one is generated. func (c *Config) NodeKey() *ecdsa.PrivateKey { - // Use any specifically configured key + // Use any specifically configured key. if c.PrivateKey != nil { return c.PrivateKey } - // Generate ephemeral key if no datadir is being used + // Generate ephemeral key if no datadir is being used. if c.DataDir == "" { key, err := crypto.GenerateKey() if err != nil { @@ -230,16 +331,22 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey { } return key } - // Fall back to persistent key from the data directory - keyfile := filepath.Join(c.DataDir, datadirPrivateKey) + + keyfile := c.resolvePath(datadirPrivateKey) if key, err := crypto.LoadECDSA(keyfile); err == nil { return key } - // No persistent key found, generate and store a new one + // No persistent key found, generate and store a new one. key, err := crypto.GenerateKey() if err != nil { glog.Fatalf("Failed to generate node key: %v", err) } + instanceDir := filepath.Join(c.DataDir, c.name()) + if err := os.MkdirAll(instanceDir, 0700); err != nil { + glog.V(logger.Error).Infof("Failed to persist node key: %v", err) + return key + } + keyfile = filepath.Join(instanceDir, datadirPrivateKey) if err := crypto.SaveECDSA(keyfile, key); err != nil { glog.V(logger.Error).Infof("Failed to persist node key: %v", err) } @@ -248,34 +355,28 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey { // StaticNodes returns a list of node enode URLs configured as static nodes. func (c *Config) StaticNodes() []*discover.Node { - return c.parsePersistentNodes(datadirStaticNodes) + return c.parsePersistentNodes(c.resolvePath(datadirStaticNodes)) } // TrusterNodes returns a list of node enode URLs configured as trusted nodes. func (c *Config) TrusterNodes() []*discover.Node { - return c.parsePersistentNodes(datadirTrustedNodes) + return c.parsePersistentNodes(c.resolvePath(datadirTrustedNodes)) } // parsePersistentNodes parses a list of discovery node URLs loaded from a .json // file from within the data directory. -func (c *Config) parsePersistentNodes(file string) []*discover.Node { +func (c *Config) parsePersistentNodes(path string) []*discover.Node { // Short circuit if no node config is present if c.DataDir == "" { return nil } - path := filepath.Join(c.DataDir, file) if _, err := os.Stat(path); err != nil { return nil } - // Load the nodes from the config file - blob, err := ioutil.ReadFile(path) - if err != nil { - glog.V(logger.Error).Infof("Failed to access nodes: %v", err) - return nil - } - nodelist := []string{} - if err := json.Unmarshal(blob, &nodelist); err != nil { - glog.V(logger.Error).Infof("Failed to load nodes: %v", err) + // Load the nodes from the config file. + var nodelist []string + if err := common.LoadJSON(path, &nodelist); err != nil { + glog.V(logger.Error).Infof("Can't load node file %s: %v", path, err) return nil } // Interpret the list as a discovery node array diff --git a/vendor/github.com/ethereum/go-ethereum/common/defaults.go b/vendor/github.com/ethereum/go-ethereum/node/defaults.go similarity index 89% rename from vendor/github.com/ethereum/go-ethereum/common/defaults.go rename to vendor/github.com/ethereum/go-ethereum/node/defaults.go index 8a136fa80..bfe257c8e 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/defaults.go +++ b/vendor/github.com/ethereum/go-ethereum/node/defaults.go @@ -14,9 +14,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package common +package node import ( + "os" + "os/user" "path/filepath" "runtime" ) @@ -33,7 +35,7 @@ const ( // persistence requirements. func DefaultDataDir() string { // Try to place the data folder in the user's home dir - home := HomeDir() + home := homeDir() if home != "" { if runtime.GOOS == "darwin" { return filepath.Join(home, "Library", "Ethereum") @@ -46,3 +48,13 @@ func DefaultDataDir() string { // As we cannot guess a stable location, return empty and handle later return "" } + +func homeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/doc.go b/vendor/github.com/ethereum/go-ethereum/node/doc.go new file mode 100644 index 000000000..f009e6f85 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/doc.go @@ -0,0 +1,90 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +Package node sets up multi-protocol Ethereum nodes. + +In the model exposed by this package, a node is a collection of services which use shared +resources to provide RPC APIs. Services can also offer devp2p protocols, which are wired +up to the devp2p network when the node instance is started. + + +Resources Managed By Node + +All file-system resources used by a node instance are located in a directory called the +data directory. The location of each resource can be overridden through additional node +configuration. The data directory is optional. If it is not set and the location of a +resource is otherwise unspecified, package node will create the resource in memory. + +To access to the devp2p network, Node configures and starts p2p.Server. Each host on the +devp2p network has a unique identifier, the node key. The Node instance persists this key +across restarts. Node also loads static and trusted node lists and ensures that knowledge +about other hosts is persisted. + +JSON-RPC servers which run HTTP, WebSocket or IPC can be started on a Node. RPC modules +offered by registered services will be offered on those endpoints. Users can restrict any +endpoint to a subset of RPC modules. Node itself offers the "debug", "admin" and "web3" +modules. + +Service implementations can open LevelDB databases through the service context. Package +node chooses the file system location of each database. If the node is configured to run +without a data directory, databases are opened in memory instead. + +Node also creates the shared store of encrypted Ethereum account keys. Services can access +the account manager through the service context. + + +Sharing Data Directory Among Instances + +Multiple node instances can share a single data directory if they have distinct instance +names (set through the Name config option). Sharing behaviour depends on the type of +resource. + +devp2p-related resources (node key, static/trusted node lists, known hosts database) are +stored in a directory with the same name as the instance. Thus, multiple node instances +using the same data directory will store this information in different subdirectories of +the data directory. + +LevelDB databases are also stored within the instance subdirectory. If multiple node +instances use the same data directory, openening the databases with identical names will +create one database for each instance. + +The account key store is shared among all node instances using the same data directory +unless its location is changed through the KeyStoreDir configuration option. + + +Data Directory Sharing Example + +In this exanple, two node instances named A and B are started with the same data +directory. Mode instance A opens the database "db", node instance B opens the databases +"db" and "db-2". The following files will be created in the data directory: + + data-directory/ + A/ + nodekey -- devp2p node key of instance A + nodes/ -- devp2p discovery knowledge database of instance A + db/ -- LevelDB content for "db" + A.ipc -- JSON-RPC UNIX domain socket endpoint of instance A + B/ + nodekey -- devp2p node key of node B + nodes/ -- devp2p discovery knowledge database of instance B + static-nodes.json -- devp2p static node list of instance B + db/ -- LevelDB content for "db" + db-2/ -- LevelDB content for "db-2" + B.ipc -- JSON-RPC UNIX domain socket endpoint of instance A + keystore/ -- account key store, used by both instances +*/ +package node diff --git a/vendor/github.com/ethereum/go-ethereum/node/node.go b/vendor/github.com/ethereum/go-ethereum/node/node.go index f3be2f763..d49ae3a45 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/node.go +++ b/vendor/github.com/ethereum/go-ethereum/node/node.go @@ -14,7 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package node represents the Ethereum protocol stack container. package node import ( @@ -23,16 +22,19 @@ import ( "os" "path/filepath" "reflect" + "strings" "sync" "syscall" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "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/p2p" "github.com/ethereum/go-ethereum/rpc" + "github.com/syndtr/goleveldb/leveldb/storage" ) var ( @@ -44,14 +46,14 @@ var ( datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true} ) -// Node represents a P2P node into which arbitrary (uniquely typed) services might -// be registered. +// Node is a container on which services can be registered. type Node struct { - datadir string // Path to the currently used data directory eventmux *event.TypeMux // Event multiplexer used between the services of a stack + config *Config + accman *accounts.Manager - accman *accounts.Manager - ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop + ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop + instanceDirLock storage.Storage // prevents concurrent use of instance directory serverConfig p2p.Config server *p2p.Server // Currently running P2P networking layer @@ -66,21 +68,14 @@ type Node struct { ipcListener net.Listener // IPC RPC listener socket to serve API requests ipcHandler *rpc.Server // IPC RPC request handler to process the API requests - httpHost string // HTTP hostname - httpPort int // HTTP post httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled) httpWhitelist []string // HTTP RPC modules to allow through this endpoint - httpCors string // HTTP RPC Cross-Origin Resource Sharing header httpListener net.Listener // HTTP RPC listener socket to server API requests httpHandler *rpc.Server // HTTP RPC request handler to process the API requests - wsHost string // Websocket host - wsPort int // Websocket post - wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled) - wsWhitelist []string // Websocket RPC modules to allow through this endpoint - wsOrigins string // Websocket RPC allowed origin domains - wsListener net.Listener // Websocket RPC listener socket to server API requests - wsHandler *rpc.Server // Websocket RPC request handler to process the API requests + wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled) + wsListener net.Listener // Websocket RPC listener socket to server API requests + wsHandler *rpc.Server // Websocket RPC request handler to process the API requests stop chan struct{} // Channel to wait for termination notifications lock sync.RWMutex @@ -88,54 +83,45 @@ type Node struct { // New creates a new P2P node, ready for protocol registration. func New(conf *Config) (*Node, error) { - // Ensure the data directory exists, failing if it cannot be created + // Copy config and resolve the datadir so future changes to the current + // working directory don't affect the node. + confCopy := *conf + conf = &confCopy if conf.DataDir != "" { - if err := os.MkdirAll(conf.DataDir, 0700); err != nil { + absdatadir, err := filepath.Abs(conf.DataDir) + if err != nil { return nil, err } + conf.DataDir = absdatadir } + // Ensure that the instance name doesn't cause weird conflicts with + // other files in the data directory. + if strings.ContainsAny(conf.Name, `/\`) { + return nil, errors.New(`Config.Name must not contain '/' or '\'`) + } + if conf.Name == datadirDefaultKeyStore { + return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`) + } + if strings.HasSuffix(conf.Name, ".ipc") { + return nil, errors.New(`Config.Name cannot end in ".ipc"`) + } + // Ensure that the AccountManager method works before the node has started. + // We rely on this in cmd/geth. am, ephemeralKeystore, err := makeAccountManager(conf) if err != nil { return nil, err } - - // Assemble the networking layer and the node itself - nodeDbPath := "" - if conf.DataDir != "" { - nodeDbPath = filepath.Join(conf.DataDir, datadirNodeDatabase) - } + // Note: any interaction with Config that would create/touch files + // in the data directory or instance directory is delayed until Start. return &Node{ - datadir: conf.DataDir, accman: am, ephemeralKeystore: ephemeralKeystore, - serverConfig: p2p.Config{ - PrivateKey: conf.NodeKey(), - Name: conf.Name, - Discovery: !conf.NoDiscovery, - BootstrapNodes: conf.BootstrapNodes, - StaticNodes: conf.StaticNodes(), - TrustedNodes: conf.TrusterNodes(), - NodeDatabase: nodeDbPath, - ListenAddr: conf.ListenAddr, - NAT: conf.NAT, - Dialer: conf.Dialer, - NoDial: conf.NoDial, - MaxPeers: conf.MaxPeers, - MaxPendingPeers: conf.MaxPendingPeers, - }, - serviceFuncs: []ServiceConstructor{}, - ipcEndpoint: conf.IPCEndpoint(), - httpHost: conf.HTTPHost, - httpPort: conf.HTTPPort, - httpEndpoint: conf.HTTPEndpoint(), - httpWhitelist: conf.HTTPModules, - httpCors: conf.HTTPCors, - wsHost: conf.WSHost, - wsPort: conf.WSPort, - wsEndpoint: conf.WSEndpoint(), - wsWhitelist: conf.WSModules, - wsOrigins: conf.WSOrigins, - eventmux: new(event.TypeMux), + config: conf, + serviceFuncs: []ServiceConstructor{}, + ipcEndpoint: conf.IPCEndpoint(), + httpEndpoint: conf.HTTPEndpoint(), + wsEndpoint: conf.WSEndpoint(), + eventmux: new(event.TypeMux), }, nil } @@ -161,13 +147,39 @@ func (n *Node) Start() error { if n.server != nil { return ErrNodeRunning } - // Otherwise copy and specialize the P2P configuration + if err := n.openDataDir(); err != nil { + return err + } + + // Initialize the p2p server. This creates the node key and + // discovery databases. + n.serverConfig = p2p.Config{ + PrivateKey: n.config.NodeKey(), + Name: n.config.NodeName(), + Discovery: !n.config.NoDiscovery, + DiscoveryV5: n.config.DiscoveryV5, + DiscoveryV5Addr: n.config.DiscoveryV5Addr, + BootstrapNodes: n.config.BootstrapNodes, + BootstrapNodesV5: n.config.BootstrapNodesV5, + StaticNodes: n.config.StaticNodes(), + TrustedNodes: n.config.TrusterNodes(), + NodeDatabase: n.config.NodeDB(), + ListenAddr: n.config.ListenAddr, + NAT: n.config.NAT, + Dialer: n.config.Dialer, + NoDial: n.config.NoDial, + MaxPeers: n.config.MaxPeers, + MaxPendingPeers: n.config.MaxPendingPeers, + } running := &p2p.Server{Config: n.serverConfig} + glog.V(logger.Info).Infoln("instance:", n.serverConfig.Name) + + // Otherwise copy and specialize the P2P configuration services := make(map[reflect.Type]Service) for _, constructor := range n.serviceFuncs { // Create a new context for the particular service ctx := &ServiceContext{ - datadir: n.datadir, + config: n.config, services: make(map[reflect.Type]Service), EventMux: n.eventmux, AccountManager: n.accman, @@ -227,6 +239,26 @@ func (n *Node) Start() error { return nil } +func (n *Node) openDataDir() error { + if n.config.DataDir == "" { + return nil // ephemeral + } + + instdir := filepath.Join(n.config.DataDir, n.config.name()) + if err := os.MkdirAll(instdir, 0700); err != nil { + return err + } + // Try to open the instance directory as LevelDB storage. This creates a lock file + // which prevents concurrent use by another instance as well as accidental use of the + // instance directory as a database. + storage, err := storage.OpenFile(instdir, true) + if err != nil { + return err + } + n.instanceDirLock = storage + return nil +} + // startRPC is a helper method to start all the various RPC endpoint during node // startup. It's not meant to be called at any time afterwards as it makes certain // assumptions about the state of the node. @@ -244,12 +276,12 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error { n.stopInProc() return err } - if err := n.startHTTP(n.httpEndpoint, apis, n.httpWhitelist, n.httpCors); err != nil { + if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors); err != nil { n.stopIPC() n.stopInProc() return err } - if err := n.startWS(n.wsEndpoint, apis, n.wsWhitelist, n.wsOrigins); err != nil { + if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins); err != nil { n.stopHTTP() n.stopIPC() n.stopInProc() @@ -381,7 +413,6 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors n.httpEndpoint = endpoint n.httpListener = listener n.httpHandler = handler - n.httpCors = cors return nil } @@ -436,7 +467,6 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig n.wsEndpoint = endpoint n.wsListener = listener n.wsHandler = handler - n.wsOrigins = wsOrigins return nil } @@ -465,12 +495,12 @@ func (n *Node) Stop() error { if n.server == nil { return ErrNodeStopped } - // Otherwise terminate the API, all services and the P2P server too + + // Terminate the API, services and the p2p server. n.stopWS() n.stopHTTP() n.stopIPC() n.rpcAPIs = nil - failure := &StopError{ Services: make(map[reflect.Type]error), } @@ -480,9 +510,16 @@ func (n *Node) Stop() error { } } n.server.Stop() - n.services = nil n.server = nil + + // Release instance directory lock. + if n.instanceDirLock != nil { + n.instanceDirLock.Close() + n.instanceDirLock = nil + } + + // unblock n.Wait close(n.stop) // Remove the keystore if it was created ephemerally. @@ -565,8 +602,14 @@ func (n *Node) Service(service interface{}) error { } // DataDir retrieves the current datadir used by the protocol stack. +// Deprecated: No files should be stored in this directory, use InstanceDir instead. func (n *Node) DataDir() string { - return n.datadir + return n.config.DataDir +} + +// InstanceDir retrieves the instance directory used by the protocol stack. +func (n *Node) InstanceDir() string { + return n.config.instanceDir() } // AccountManager retrieves the account manager used by the protocol stack. @@ -595,6 +638,21 @@ func (n *Node) EventMux() *event.TypeMux { return n.eventmux } +// OpenDatabase opens an existing database with the given name (or creates one if no +// previous can be found) from within the node's instance directory. If the node is +// ephemeral, a memory database is returned. +func (n *Node) OpenDatabase(name string, cache, handles int) (ethdb.Database, error) { + if n.config.DataDir == "" { + return ethdb.NewMemDatabase() + } + return ethdb.NewLDBDatabase(n.config.resolvePath(name), cache, handles) +} + +// ResolvePath returns the absolute path of a resource in the instance directory. +func (n *Node) ResolvePath(x string) string { + return n.config.resolvePath(x) +} + // apis returns the collection of RPC descriptors this node offers. func (n *Node) apis() []rpc.API { return []rpc.API{ diff --git a/vendor/github.com/ethereum/go-ethereum/node/service.go b/vendor/github.com/ethereum/go-ethereum/node/service.go index 51531466b..1cd1fe808 100644 --- a/vendor/github.com/ethereum/go-ethereum/node/service.go +++ b/vendor/github.com/ethereum/go-ethereum/node/service.go @@ -17,7 +17,6 @@ package node import ( - "path/filepath" "reflect" "github.com/ethereum/go-ethereum/accounts" @@ -31,7 +30,7 @@ import ( // the protocol stack, that is passed to all constructors to be optionally used; // as well as utility methods to operate on the service environment. type ServiceContext struct { - datadir string // Data directory for protocol persistence + config *Config services map[reflect.Type]Service // Index of the already constructed services EventMux *event.TypeMux // Event multiplexer used for decoupled notifications AccountManager *accounts.Manager // Account manager created by the node. @@ -41,10 +40,10 @@ type ServiceContext struct { // if no previous can be found) from within the node's data directory. If the // node is an ephemeral one, a memory database is returned. func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int) (ethdb.Database, error) { - if ctx.datadir == "" { + if ctx.config.DataDir == "" { return ethdb.NewMemDatabase() } - return ethdb.NewLDBDatabase(filepath.Join(ctx.datadir, name), cache, handles) + return ethdb.NewLDBDatabase(ctx.config.resolvePath(name), cache, handles) } // Service retrieves a currently running service registered of a specific type. @@ -64,11 +63,13 @@ type ServiceConstructor func(ctx *ServiceContext) (Service, error) // Service is an individual protocol that can be registered into a node. // // Notes: -// - Service life-cycle management is delegated to the node. The service is -// allowed to initialize itself upon creation, but no goroutines should be -// spun up outside of the Start method. -// - Restart logic is not required as the node will create a fresh instance -// every time a service is started. +// +// • Service life-cycle management is delegated to the node. The service is allowed to +// initialize itself upon creation, but no goroutines should be spun up outside of the +// Start method. +// +// • Restart logic is not required as the node will create a fresh instance +// every time a service is started. type Service interface { // Protocols retrieves the P2P protocols the service wishes to start. Protocols() []p2p.Protocol diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go index 139a95d80..eec0bae0c 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go @@ -35,7 +35,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/secp256k1" ) -const nodeIDBits = 512 +const NodeIDBits = 512 // Node represents a host on the network. // The fields of Node may not be modified. @@ -209,7 +209,7 @@ func MustParseNode(rawurl string) *Node { // NodeID is a unique identifier for each node. // The node identifier is a marshaled elliptic curve public key. -type NodeID [nodeIDBits / 8]byte +type NodeID [NodeIDBits / 8]byte // NodeID prints as a long hexadecimal number. func (n NodeID) String() string { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/crypto.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/crypto.go new file mode 100644 index 000000000..48b2a8a72 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/crypto.go @@ -0,0 +1,31 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + //"github.com/btcsuite/btcd/btcec" + "github.com/ethereum/go-ethereum/crypto/secp256k1" +) + +func S256() *secp256k1.BitCurve { + return secp256k1.S256() +} + +// This version should be used for NaCl compilation +/*func S256() *btcec.KoblitzCurve { + return S256() +}*/ diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database.go new file mode 100644 index 000000000..7c47c27fd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database.go @@ -0,0 +1,413 @@ +// 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 . + +// Contains the node database, storing previously seen nodes and any collected +// metadata about them for QoS purposes. + +package discv5 + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +var ( + nodeDBNilNodeID = NodeID{} // Special node ID to use as a nil element. + nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped. + nodeDBCleanupCycle = time.Hour // Time period for running the expiration task. +) + +// nodeDB stores all nodes we know about. +type nodeDB struct { + lvl *leveldb.DB // Interface to the database itself + self NodeID // Own node id to prevent adding it into the database + runner sync.Once // Ensures we can start at most one expirer + quit chan struct{} // Channel to signal the expiring thread to stop +} + +// Schema layout for the node database +var ( + nodeDBVersionKey = []byte("version") // Version of the database to flush if changes + nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with + + nodeDBDiscoverRoot = ":discover" + nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping" + nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong" + nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail" + nodeDBDiscoverLocalEndpoint = nodeDBDiscoverRoot + ":localendpoint" + nodeDBTopicRegTickets = ":tickets" +) + +// newNodeDB creates a new node database for storing and retrieving infos about +// known peers in the network. If no path is given, an in-memory, temporary +// database is constructed. +func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) { + if path == "" { + return newMemoryNodeDB(self) + } + return newPersistentNodeDB(path, version, self) +} + +// newMemoryNodeDB creates a new in-memory node database without a persistent +// backend. +func newMemoryNodeDB(self NodeID) (*nodeDB, error) { + db, err := leveldb.Open(storage.NewMemStorage(), nil) + if err != nil { + return nil, err + } + return &nodeDB{ + lvl: db, + self: self, + quit: make(chan struct{}), + }, nil +} + +// newPersistentNodeDB creates/opens a leveldb backed persistent node database, +// also flushing its contents in case of a version mismatch. +func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) { + opts := &opt.Options{OpenFilesCacheCapacity: 5} + db, err := leveldb.OpenFile(path, opts) + if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { + db, err = leveldb.RecoverFile(path, nil) + } + if err != nil { + return nil, err + } + // The nodes contained in the cache correspond to a certain protocol version. + // Flush all nodes if the version doesn't match. + currentVer := make([]byte, binary.MaxVarintLen64) + currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))] + + blob, err := db.Get(nodeDBVersionKey, nil) + switch err { + case leveldb.ErrNotFound: + // Version not found (i.e. empty cache), insert it + if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil { + db.Close() + return nil, err + } + + case nil: + // Version present, flush if different + if !bytes.Equal(blob, currentVer) { + db.Close() + if err = os.RemoveAll(path); err != nil { + return nil, err + } + return newPersistentNodeDB(path, version, self) + } + } + return &nodeDB{ + lvl: db, + self: self, + quit: make(chan struct{}), + }, nil +} + +// makeKey generates the leveldb key-blob from a node id and its particular +// field of interest. +func makeKey(id NodeID, field string) []byte { + if bytes.Equal(id[:], nodeDBNilNodeID[:]) { + return []byte(field) + } + return append(nodeDBItemPrefix, append(id[:], field...)...) +} + +// splitKey tries to split a database key into a node id and a field part. +func splitKey(key []byte) (id NodeID, field string) { + // If the key is not of a node, return it plainly + if !bytes.HasPrefix(key, nodeDBItemPrefix) { + return NodeID{}, string(key) + } + // Otherwise split the id and field + item := key[len(nodeDBItemPrefix):] + copy(id[:], item[:len(id)]) + field = string(item[len(id):]) + + return id, field +} + +// fetchInt64 retrieves an integer instance associated with a particular +// database key. +func (db *nodeDB) fetchInt64(key []byte) int64 { + blob, err := db.lvl.Get(key, nil) + if err != nil { + return 0 + } + val, read := binary.Varint(blob) + if read <= 0 { + return 0 + } + return val +} + +// storeInt64 update a specific database entry to the current time instance as a +// unix timestamp. +func (db *nodeDB) storeInt64(key []byte, n int64) error { + blob := make([]byte, binary.MaxVarintLen64) + blob = blob[:binary.PutVarint(blob, n)] + return db.lvl.Put(key, blob, nil) +} + +func (db *nodeDB) storeRLP(key []byte, val interface{}) error { + blob, err := rlp.EncodeToBytes(val) + if err != nil { + return err + } + return db.lvl.Put(key, blob, nil) +} + +func (db *nodeDB) fetchRLP(key []byte, val interface{}) error { + blob, err := db.lvl.Get(key, nil) + if err != nil { + return err + } + err = rlp.DecodeBytes(blob, val) + if err != nil { + glog.V(logger.Warn).Infof("key %x (%T) %v", key, val, err) + } + return err +} + +// node retrieves a node with a given id from the database. +func (db *nodeDB) node(id NodeID) *Node { + var node Node + if err := db.fetchRLP(makeKey(id, nodeDBDiscoverRoot), &node); err != nil { + return nil + } + node.sha = crypto.Keccak256Hash(node.ID[:]) + return &node +} + +// updateNode inserts - potentially overwriting - a node into the peer database. +func (db *nodeDB) updateNode(node *Node) error { + return db.storeRLP(makeKey(node.ID, nodeDBDiscoverRoot), node) +} + +// deleteNode deletes all information/keys associated with a node. +func (db *nodeDB) deleteNode(id NodeID) error { + deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil) + for deleter.Next() { + if err := db.lvl.Delete(deleter.Key(), nil); err != nil { + return err + } + } + return nil +} + +// ensureExpirer is a small helper method ensuring that the data expiration +// mechanism is running. If the expiration goroutine is already running, this +// method simply returns. +// +// The goal is to start the data evacuation only after the network successfully +// bootstrapped itself (to prevent dumping potentially useful seed nodes). Since +// it would require significant overhead to exactly trace the first successful +// convergence, it's simpler to "ensure" the correct state when an appropriate +// condition occurs (i.e. a successful bonding), and discard further events. +func (db *nodeDB) ensureExpirer() { + db.runner.Do(func() { go db.expirer() }) +} + +// expirer should be started in a go routine, and is responsible for looping ad +// infinitum and dropping stale data from the database. +func (db *nodeDB) expirer() { + tick := time.Tick(nodeDBCleanupCycle) + for { + select { + case <-tick: + if err := db.expireNodes(); err != nil { + glog.V(logger.Error).Infof("Failed to expire nodedb items: %v", err) + } + + case <-db.quit: + return + } + } +} + +// expireNodes iterates over the database and deletes all nodes that have not +// been seen (i.e. received a pong from) for some allotted time. +func (db *nodeDB) expireNodes() error { + threshold := time.Now().Add(-nodeDBNodeExpiration) + + // Find discovered nodes that are older than the allowance + it := db.lvl.NewIterator(nil, nil) + defer it.Release() + + for it.Next() { + // Skip the item if not a discovery node + id, field := splitKey(it.Key()) + if field != nodeDBDiscoverRoot { + continue + } + // Skip the node if not expired yet (and not self) + if bytes.Compare(id[:], db.self[:]) != 0 { + if seen := db.lastPong(id); seen.After(threshold) { + continue + } + } + // Otherwise delete all associated information + db.deleteNode(id) + } + return nil +} + +// lastPing retrieves the time of the last ping packet send to a remote node, +// requesting binding. +func (db *nodeDB) lastPing(id NodeID) time.Time { + return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0) +} + +// updateLastPing updates the last time we tried contacting a remote node. +func (db *nodeDB) updateLastPing(id NodeID, instance time.Time) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix()) +} + +// lastPong retrieves the time of the last successful contact from remote node. +func (db *nodeDB) lastPong(id NodeID) time.Time { + return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0) +} + +// updateLastPong updates the last time a remote node successfully contacted. +func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix()) +} + +// findFails retrieves the number of findnode failures since bonding. +func (db *nodeDB) findFails(id NodeID) int { + return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails))) +} + +// updateFindFails updates the number of findnode failures since bonding. +func (db *nodeDB) updateFindFails(id NodeID, fails int) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails)) +} + +// localEndpoint returns the last local endpoint communicated to the +// given remote node. +func (db *nodeDB) localEndpoint(id NodeID) *rpcEndpoint { + var ep rpcEndpoint + if err := db.fetchRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep); err != nil { + return nil + } + return &ep +} + +func (db *nodeDB) updateLocalEndpoint(id NodeID, ep rpcEndpoint) error { + return db.storeRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep) +} + +// querySeeds retrieves random nodes to be used as potential seed nodes +// for bootstrapping. +func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node { + var ( + now = time.Now() + nodes = make([]*Node, 0, n) + it = db.lvl.NewIterator(nil, nil) + id NodeID + ) + defer it.Release() + +seek: + for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { + // Seek to a random entry. The first byte is incremented by a + // random amount each time in order to increase the likelihood + // of hitting all existing nodes in very small databases. + ctr := id[0] + rand.Read(id[:]) + id[0] = ctr + id[0]%16 + it.Seek(makeKey(id, nodeDBDiscoverRoot)) + + n := nextNode(it) + if n == nil { + id[0] = 0 + continue seek // iterator exhausted + } + if n.ID == db.self { + continue seek + } + if now.Sub(db.lastPong(n.ID)) > maxAge { + continue seek + } + for i := range nodes { + if nodes[i].ID == n.ID { + continue seek // duplicate + } + } + nodes = append(nodes, n) + } + return nodes +} + +func (db *nodeDB) fetchTopicRegTickets(id NodeID) (issued, used uint32) { + key := makeKey(id, nodeDBTopicRegTickets) + blob, _ := db.lvl.Get(key, nil) + if len(blob) != 8 { + return 0, 0 + } + issued = binary.BigEndian.Uint32(blob[0:4]) + used = binary.BigEndian.Uint32(blob[4:8]) + return +} + +func (db *nodeDB) updateTopicRegTickets(id NodeID, issued, used uint32) error { + key := makeKey(id, nodeDBTopicRegTickets) + blob := make([]byte, 8) + binary.BigEndian.PutUint32(blob[0:4], issued) + binary.BigEndian.PutUint32(blob[4:8], used) + return db.lvl.Put(key, blob, nil) +} + +// reads the next node record from the iterator, skipping over other +// database entries. +func nextNode(it iterator.Iterator) *Node { + for end := false; !end; end = !it.Next() { + id, field := splitKey(it.Key()) + if field != nodeDBDiscoverRoot { + continue + } + var n Node + if err := rlp.DecodeBytes(it.Value(), &n); err != nil { + if glog.V(logger.Warn) { + glog.Errorf("invalid node %x: %v", id, err) + } + continue + } + return &n + } + return nil +} + +// close flushes and closes the database files. +func (db *nodeDB) close() { + close(db.quit) + db.lvl.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go new file mode 100644 index 000000000..7ad6f1e5b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go @@ -0,0 +1,1212 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "crypto/ecdsa" + "errors" + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/sha3" + "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/rlp" +) + +var ( + errInvalidEvent = errors.New("invalid in current state") + errNoQuery = errors.New("no pending query") + errWrongAddress = errors.New("unknown sender address") +) + +const ( + autoRefreshInterval = 1 * time.Hour + bucketRefreshInterval = 1 * time.Minute + seedCount = 30 + seedMaxAge = 5 * 24 * time.Hour +) + +const testTopic = "foo" + +const ( + printDebugLogs = false + printTestImgLogs = false +) + +func debugLog(s string) { + if printDebugLogs { + fmt.Println(s) + } +} + +// Network manages the table and all protocol interaction. +type Network struct { + db *nodeDB // database of known nodes + conn transport + + closed chan struct{} // closed when loop is done + closeReq chan struct{} // 'request to close' + refreshReq chan []*Node // lookups ask for refresh on this channel + refreshResp chan (<-chan struct{}) // ...and get the channel to block on from this one + read chan ingressPacket // ingress packets arrive here + timeout chan timeoutEvent + queryReq chan *findnodeQuery // lookups submit findnode queries on this channel + tableOpReq chan func() + tableOpResp chan struct{} + topicRegisterReq chan topicRegisterReq + topicSearchReq chan topicSearchReq + + // State of the main loop. + tab *Table + topictab *topicTable + ticketStore *ticketStore + nursery []*Node + nodes map[NodeID]*Node // tracks active nodes with state != known + timeoutTimers map[timeoutEvent]*time.Timer + + // Revalidation queues. + // Nodes put on these queues will be pinged eventually. + slowRevalidateQueue []*Node + fastRevalidateQueue []*Node + + // Buffers for state transition. + sendBuf []*ingressPacket +} + +// transport is implemented by the UDP transport. +// it is an interface so we can test without opening lots of UDP +// sockets and without generating a private key. +type transport interface { + sendPing(remote *Node, remoteAddr *net.UDPAddr, topics []Topic) (hash []byte) + sendNeighbours(remote *Node, nodes []*Node) + sendFindnodeHash(remote *Node, target common.Hash) + sendTopicRegister(remote *Node, topics []Topic, topicIdx int, pong []byte) + sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) + + send(remote *Node, ptype nodeEvent, p interface{}) (hash []byte) + + localAddr() *net.UDPAddr + Close() +} + +type findnodeQuery struct { + remote *Node + target common.Hash + reply chan<- []*Node + nresults int // counter for received nodes +} + +type topicRegisterReq struct { + add bool + topic Topic +} + +type topicSearchReq struct { + topic Topic + found chan<- string +} + +type timeoutEvent struct { + ev nodeEvent + node *Node +} + +func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, dbPath string) (*Network, error) { + ourID := PubkeyID(&ourPubkey) + + var db *nodeDB + if dbPath != "" { + var err error + if db, err = newNodeDB(dbPath, Version, ourID); err != nil { + return nil, err + } + } + + tab := newTable(ourID, conn.localAddr()) + net := &Network{ + db: db, + conn: conn, + tab: tab, + topictab: newTopicTable(db, tab.self), + ticketStore: newTicketStore(), + refreshReq: make(chan []*Node), + refreshResp: make(chan (<-chan struct{})), + closed: make(chan struct{}), + closeReq: make(chan struct{}), + read: make(chan ingressPacket, 100), + timeout: make(chan timeoutEvent), + timeoutTimers: make(map[timeoutEvent]*time.Timer), + tableOpReq: make(chan func()), + tableOpResp: make(chan struct{}), + queryReq: make(chan *findnodeQuery), + topicRegisterReq: make(chan topicRegisterReq), + topicSearchReq: make(chan topicSearchReq), + nodes: make(map[NodeID]*Node), + } + go net.loop() + return net, nil +} + +// Close terminates the network listener and flushes the node database. +func (net *Network) Close() { + net.conn.Close() + select { + case <-net.closed: + case net.closeReq <- struct{}{}: + <-net.closed + } +} + +// Self returns the local node. +// The returned node should not be modified by the caller. +func (net *Network) Self() *Node { + return net.tab.self +} + +// ReadRandomNodes fills the given slice with random nodes from the +// table. It will not write the same node more than once. The nodes in +// the slice are copies and can be modified by the caller. +func (net *Network) ReadRandomNodes(buf []*Node) (n int) { + net.reqTableOp(func() { n = net.tab.readRandomNodes(buf) }) + return n +} + +// SetFallbackNodes sets the initial points of contact. These nodes +// are used to connect to the network if the table is empty and there +// are no known nodes in the database. +func (net *Network) SetFallbackNodes(nodes []*Node) error { + nursery := make([]*Node, 0, len(nodes)) + for _, n := range nodes { + if err := n.validateComplete(); err != nil { + return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) + } + // Recompute cpy.sha because the node might not have been + // created by NewNode or ParseNode. + cpy := *n + cpy.sha = crypto.Keccak256Hash(n.ID[:]) + nursery = append(nursery, &cpy) + } + net.reqRefresh(nursery) + return nil +} + +// Resolve searches for a specific node with the given ID. +// It returns nil if the node could not be found. +func (net *Network) Resolve(targetID NodeID) *Node { + result := net.lookup(crypto.Keccak256Hash(targetID[:]), true) + for _, n := range result { + if n.ID == targetID { + return n + } + } + return nil +} + +// Lookup performs a network search for nodes close +// to the given target. It approaches the target by querying +// nodes that are closer to it on each iteration. +// The given target does not need to be an actual node +// identifier. +// +// The local node may be included in the result. +func (net *Network) Lookup(targetID NodeID) []*Node { + return net.lookup(crypto.Keccak256Hash(targetID[:]), false) +} + +func (net *Network) lookup(target common.Hash, stopOnMatch bool) []*Node { + var ( + asked = make(map[NodeID]bool) + seen = make(map[NodeID]bool) + reply = make(chan []*Node, alpha) + result = nodesByDistance{target: target} + pendingQueries = 0 + ) + // Get initial answers from the local node. + result.push(net.tab.self, bucketSize) + for { + // Ask the α closest nodes that we haven't asked yet. + for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { + n := result.entries[i] + if !asked[n.ID] { + asked[n.ID] = true + pendingQueries++ + net.reqQueryFindnode(n, target, reply) + } + } + if pendingQueries == 0 { + // We have asked all closest nodes, stop the search. + break + } + // Wait for the next reply. + for _, n := range <-reply { + if n != nil && !seen[n.ID] { + seen[n.ID] = true + result.push(n, bucketSize) + if stopOnMatch && n.sha == target { + return result.entries + } + } + } + pendingQueries-- + } + return result.entries +} + +func (net *Network) RegisterTopic(topic Topic, stop <-chan struct{}) { + select { + case net.topicRegisterReq <- topicRegisterReq{true, topic}: + case <-net.closed: + return + } + select { + case <-net.closed: + case <-stop: + select { + case net.topicRegisterReq <- topicRegisterReq{false, topic}: + case <-net.closed: + } + } +} + +func (net *Network) SearchTopic(topic Topic, stop <-chan struct{}, found chan<- string) { + select { + case net.topicSearchReq <- topicSearchReq{topic, found}: + case <-net.closed: + return + } + select { + case <-net.closed: + case <-stop: + select { + case net.topicSearchReq <- topicSearchReq{topic, nil}: + case <-net.closed: + } + } +} + +func (net *Network) reqRefresh(nursery []*Node) <-chan struct{} { + select { + case net.refreshReq <- nursery: + return <-net.refreshResp + case <-net.closed: + return net.closed + } +} + +func (net *Network) reqQueryFindnode(n *Node, target common.Hash, reply chan []*Node) bool { + q := &findnodeQuery{remote: n, target: target, reply: reply} + select { + case net.queryReq <- q: + return true + case <-net.closed: + return false + } +} + +func (net *Network) reqReadPacket(pkt ingressPacket) { + select { + case net.read <- pkt: + case <-net.closed: + } +} + +func (net *Network) reqTableOp(f func()) (called bool) { + select { + case net.tableOpReq <- f: + <-net.tableOpResp + return true + case <-net.closed: + return false + } +} + +// TODO: external address handling. + +func (net *Network) loop() { + var ( + refreshTimer = time.NewTicker(autoRefreshInterval) + bucketRefreshTimer = time.NewTimer(bucketRefreshInterval) + refreshDone chan struct{} // closed when the 'refresh' lookup has ended + ) + + // Tracking the next ticket to register. + var ( + nextTicket *ticketRef + nextRegisterTimer *time.Timer + nextRegisterTime <-chan time.Time + ) + defer func() { + if nextRegisterTimer != nil { + nextRegisterTimer.Stop() + } + }() + resetNextTicket := func() { + t, timeout := net.ticketStore.nextFilteredTicket() + if t != nextTicket { + nextTicket = t + if nextRegisterTimer != nil { + nextRegisterTimer.Stop() + nextRegisterTime = nil + } + if t != nil { + nextRegisterTimer = time.NewTimer(timeout) + nextRegisterTime = nextRegisterTimer.C + } + } + } + + // Tracking registration and search lookups. + var ( + topicRegisterLookupTarget lookupInfo + topicRegisterLookupDone chan []*Node + topicRegisterLookupTick = time.NewTimer(0) + topicSearchLookupTarget lookupInfo + searchReqWhenRefreshDone []topicSearchReq + ) + topicSearchLookupDone := make(chan []*Node, 1) + <-topicRegisterLookupTick.C + + statsDump := time.NewTicker(10 * time.Second) + +loop: + for { + resetNextTicket() + + select { + case <-net.closeReq: + debugLog("<-net.closeReq") + break loop + + // Ingress packet handling. + case pkt := <-net.read: + //fmt.Println("read", pkt.ev) + debugLog("<-net.read") + n := net.internNode(&pkt) + prestate := n.state + status := "ok" + if err := net.handle(n, pkt.ev, &pkt); err != nil { + status = err.Error() + } + if glog.V(logger.Detail) { + glog.Infof("<<< (%d) %v from %x@%v: %v -> %v (%v)", + net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status) + } + // TODO: persist state if n.state goes >= known, delete if it goes <= known + + // State transition timeouts. + case timeout := <-net.timeout: + debugLog("<-net.timeout") + if net.timeoutTimers[timeout] == nil { + // Stale timer (was aborted). + continue + } + delete(net.timeoutTimers, timeout) + prestate := timeout.node.state + status := "ok" + if err := net.handle(timeout.node, timeout.ev, nil); err != nil { + status = err.Error() + } + if glog.V(logger.Detail) { + glog.Infof("--- (%d) %v for %x@%v: %v -> %v (%v)", + net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status) + } + + // Querying. + case q := <-net.queryReq: + debugLog("<-net.queryReq") + if !q.start(net) { + q.remote.deferQuery(q) + } + + // Interacting with the table. + case f := <-net.tableOpReq: + debugLog("<-net.tableOpReq") + f() + net.tableOpResp <- struct{}{} + + // Topic registration stuff. + case req := <-net.topicRegisterReq: + debugLog("<-net.topicRegisterReq") + if !req.add { + net.ticketStore.removeRegisterTopic(req.topic) + continue + } + net.ticketStore.addTopic(req.topic, true) + // If we're currently waiting idle (nothing to look up), give the ticket store a + // chance to start it sooner. This should speed up convergence of the radius + // determination for new topics. + // if topicRegisterLookupDone == nil { + if topicRegisterLookupTarget.target == (common.Hash{}) { + debugLog("topicRegisterLookupTarget == null") + if topicRegisterLookupTick.Stop() { + <-topicRegisterLookupTick.C + } + target, delay := net.ticketStore.nextRegisterLookup() + topicRegisterLookupTarget = target + topicRegisterLookupTick.Reset(delay) + } + + case nodes := <-topicRegisterLookupDone: + debugLog("<-topicRegisterLookupDone") + net.ticketStore.registerLookupDone(topicRegisterLookupTarget, nodes, func(n *Node) []byte { + net.ping(n, n.addr()) + return n.pingEcho + }) + target, delay := net.ticketStore.nextRegisterLookup() + topicRegisterLookupTarget = target + topicRegisterLookupTick.Reset(delay) + topicRegisterLookupDone = nil + + case <-topicRegisterLookupTick.C: + debugLog("<-topicRegisterLookupTick") + if (topicRegisterLookupTarget.target == common.Hash{}) { + target, delay := net.ticketStore.nextRegisterLookup() + topicRegisterLookupTarget = target + topicRegisterLookupTick.Reset(delay) + topicRegisterLookupDone = nil + } else { + topicRegisterLookupDone = make(chan []*Node) + target := topicRegisterLookupTarget.target + go func() { topicRegisterLookupDone <- net.lookup(target, false) }() + } + + case <-nextRegisterTime: + debugLog("<-nextRegisterTime") + net.ticketStore.ticketRegistered(*nextTicket) + //fmt.Println("sendTopicRegister", nextTicket.t.node.addr().String(), nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) + net.conn.sendTopicRegister(nextTicket.t.node, nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) + + case req := <-net.topicSearchReq: + if refreshDone == nil { + debugLog("<-net.topicSearchReq") + if req.found == nil { + net.ticketStore.removeSearchTopic(req.topic) + continue + } + net.ticketStore.addSearchTopic(req.topic, req.found) + if (topicSearchLookupTarget.target == common.Hash{}) { + topicSearchLookupDone <- nil + } + } else { + searchReqWhenRefreshDone = append(searchReqWhenRefreshDone, req) + } + + case nodes := <-topicSearchLookupDone: + debugLog("<-topicSearchLookupDone") + net.ticketStore.searchLookupDone(topicSearchLookupTarget, nodes, func(n *Node) []byte { + net.ping(n, n.addr()) + return n.pingEcho + }, func(n *Node, topic Topic) []byte { + if n.state == known { + return net.conn.send(n, topicQueryPacket, topicQuery{Topic: topic}) // TODO: set expiration + } else { + if n.state == unknown { + net.ping(n, n.addr()) + } + return nil + } + }) + topicSearchLookupTarget = net.ticketStore.nextSearchLookup() + target := topicSearchLookupTarget.target + if (target != common.Hash{}) { + go func() { topicSearchLookupDone <- net.lookup(target, false) }() + } + + case <-statsDump.C: + debugLog("<-statsDump.C") + /*r, ok := net.ticketStore.radius[testTopic] + if !ok { + fmt.Printf("(%x) no radius @ %v\n", net.tab.self.ID[:8], time.Now()) + } else { + topics := len(net.ticketStore.tickets) + tickets := len(net.ticketStore.nodes) + rad := r.radius / (maxRadius/10000+1) + fmt.Printf("(%x) topics:%d radius:%d tickets:%d @ %v\n", net.tab.self.ID[:8], topics, rad, tickets, time.Now()) + }*/ + + tm := mclock.Now() + for topic, r := range net.ticketStore.radius { + if printTestImgLogs { + rad := r.radius / (maxRadius/1000000 + 1) + minrad := r.minRadius / (maxRadius/1000000 + 1) + fmt.Printf("*R %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], rad) + fmt.Printf("*MR %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], minrad) + } + } + for topic, t := range net.topictab.topics { + wp := t.wcl.nextWaitPeriod(tm) + if printTestImgLogs { + fmt.Printf("*W %d %v %016x %d\n", tm/1000000, topic, net.tab.self.sha[:8], wp/1000000) + } + } + + // Periodic / lookup-initiated bucket refresh. + case <-refreshTimer.C: + debugLog("<-refreshTimer.C") + // TODO: ideally we would start the refresh timer after + // fallback nodes have been set for the first time. + if refreshDone == nil { + refreshDone = make(chan struct{}) + net.refresh(refreshDone) + } + case <-bucketRefreshTimer.C: + target := net.tab.chooseBucketRefreshTarget() + go func() { + net.lookup(target, false) + bucketRefreshTimer.Reset(bucketRefreshInterval) + }() + case newNursery := <-net.refreshReq: + debugLog("<-net.refreshReq") + if newNursery != nil { + net.nursery = newNursery + } + if refreshDone == nil { + refreshDone = make(chan struct{}) + net.refresh(refreshDone) + } + net.refreshResp <- refreshDone + case <-refreshDone: + debugLog("<-net.refreshDone") + refreshDone = nil + list := searchReqWhenRefreshDone + searchReqWhenRefreshDone = nil + go func() { + for _, req := range list { + net.topicSearchReq <- req + } + }() + } + } + debugLog("loop stopped") + + glog.V(logger.Debug).Infof("shutting down") + if net.conn != nil { + net.conn.Close() + } + if refreshDone != nil { + // TODO: wait for pending refresh. + //<-refreshResults + } + // Cancel all pending timeouts. + for _, timer := range net.timeoutTimers { + timer.Stop() + } + if net.db != nil { + net.db.close() + } + close(net.closed) +} + +// Everything below runs on the Network.loop goroutine +// and can modify Node, Table and Network at any time without locking. + +func (net *Network) refresh(done chan<- struct{}) { + var seeds []*Node + if net.db != nil { + seeds = net.db.querySeeds(seedCount, seedMaxAge) + } + if len(seeds) == 0 { + seeds = net.nursery + } + if len(seeds) == 0 { + glog.V(logger.Detail).Info("no seed nodes found") + close(done) + return + } + for _, n := range seeds { + if glog.V(logger.Debug) { + var age string + if net.db != nil { + age = time.Since(net.db.lastPong(n.ID)).String() + } else { + age = "unknown" + } + glog.Infof("seed node (age %s): %v", age, n) + } + n = net.internNodeFromDB(n) + if n.state == unknown { + net.transition(n, verifyinit) + } + // Force-add the seed node so Lookup does something. + // It will be deleted again if verification fails. + net.tab.add(n) + } + // Start self lookup to fill up the buckets. + go func() { + net.Lookup(net.tab.self.ID) + close(done) + }() +} + +// Node Interning. + +func (net *Network) internNode(pkt *ingressPacket) *Node { + if n := net.nodes[pkt.remoteID]; n != nil { + n.IP = pkt.remoteAddr.IP + n.UDP = uint16(pkt.remoteAddr.Port) + n.TCP = uint16(pkt.remoteAddr.Port) + return n + } + n := NewNode(pkt.remoteID, pkt.remoteAddr.IP, uint16(pkt.remoteAddr.Port), uint16(pkt.remoteAddr.Port)) + n.state = unknown + net.nodes[pkt.remoteID] = n + return n +} + +func (net *Network) internNodeFromDB(dbn *Node) *Node { + if n := net.nodes[dbn.ID]; n != nil { + return n + } + n := NewNode(dbn.ID, dbn.IP, dbn.UDP, dbn.TCP) + n.state = unknown + net.nodes[n.ID] = n + return n +} + +func (net *Network) internNodeFromNeighbours(rn rpcNode) (n *Node, err error) { + if rn.ID == net.tab.self.ID { + return nil, errors.New("is self") + } + n = net.nodes[rn.ID] + if n == nil { + // We haven't seen this node before. + n, err = nodeFromRPC(rn) + n.state = unknown + if err == nil { + net.nodes[n.ID] = n + } + return n, err + } + if !bytes.Equal(n.IP, rn.IP) || n.UDP != rn.UDP || n.TCP != rn.TCP { + err = fmt.Errorf("metadata mismatch: got %v, want %v", rn, n) + } + return n, err +} + +// nodeNetGuts is embedded in Node and contains fields. +type nodeNetGuts struct { + // This is a cached copy of sha3(ID) which is used for node + // distance calculations. This is part of Node in order to make it + // possible to write tests that need a node at a certain distance. + // In those tests, the content of sha will not actually correspond + // with ID. + sha common.Hash + + // State machine fields. Access to these fields + // is restricted to the Network.loop goroutine. + state *nodeState + pingEcho []byte // hash of last ping sent by us + pingTopics []Topic // topic set sent by us in last ping + deferredQueries []*findnodeQuery // queries that can't be sent yet + pendingNeighbours *findnodeQuery // current query, waiting for reply + queryTimeouts int +} + +func (n *nodeNetGuts) deferQuery(q *findnodeQuery) { + n.deferredQueries = append(n.deferredQueries, q) +} + +func (n *nodeNetGuts) startNextQuery(net *Network) { + if len(n.deferredQueries) == 0 { + return + } + nextq := n.deferredQueries[0] + if nextq.start(net) { + n.deferredQueries = append(n.deferredQueries[:0], n.deferredQueries[1:]...) + } +} + +func (q *findnodeQuery) start(net *Network) bool { + // Satisfy queries against the local node directly. + if q.remote == net.tab.self { + closest := net.tab.closest(crypto.Keccak256Hash(q.target[:]), bucketSize) + q.reply <- closest.entries + return true + } + if q.remote.state.canQuery && q.remote.pendingNeighbours == nil { + net.conn.sendFindnodeHash(q.remote, q.target) + net.timedEvent(respTimeout, q.remote, neighboursTimeout) + q.remote.pendingNeighbours = q + return true + } + // If the node is not known yet, it won't accept queries. + // Initiate the transition to known. + // The request will be sent later when the node reaches known state. + if q.remote.state == unknown { + net.transition(q.remote, verifyinit) + } + return false +} + +// Node Events (the input to the state machine). + +type nodeEvent uint + +//go:generate stringer -type=nodeEvent + +const ( + invalidEvent nodeEvent = iota // zero is reserved + + // Packet type events. + // These correspond to packet types in the UDP protocol. + pingPacket + pongPacket + findnodePacket + neighborsPacket + findnodeHashPacket + topicRegisterPacket + topicQueryPacket + topicNodesPacket + + // Non-packet events. + // Event values in this category are allocated outside + // the packet type range (packet types are encoded as a single byte). + pongTimeout nodeEvent = iota + 256 + pingTimeout + neighboursTimeout +) + +// Node State Machine. + +type nodeState struct { + name string + handle func(*Network, *Node, nodeEvent, *ingressPacket) (next *nodeState, err error) + enter func(*Network, *Node) + canQuery bool +} + +func (s *nodeState) String() string { + return s.name +} + +var ( + unknown *nodeState + verifyinit *nodeState + verifywait *nodeState + remoteverifywait *nodeState + known *nodeState + contested *nodeState + unresponsive *nodeState +) + +func init() { + unknown = &nodeState{ + name: "unknown", + enter: func(net *Network, n *Node) { + net.tab.delete(n) + n.pingEcho = nil + // Abort active queries. + for _, q := range n.deferredQueries { + q.reply <- nil + } + n.deferredQueries = nil + if n.pendingNeighbours != nil { + n.pendingNeighbours.reply <- nil + n.pendingNeighbours = nil + } + n.queryTimeouts = 0 + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + net.ping(n, pkt.remoteAddr) + return verifywait, nil + default: + return unknown, errInvalidEvent + } + }, + } + + verifyinit = &nodeState{ + name: "verifyinit", + enter: func(net *Network, n *Node) { + net.ping(n, n.addr()) + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return verifywait, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return remoteverifywait, err + case pongTimeout: + return unknown, nil + default: + return verifyinit, errInvalidEvent + } + }, + } + + verifywait = &nodeState{ + name: "verifywait", + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return verifywait, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return known, err + case pongTimeout: + return unknown, nil + default: + return verifywait, errInvalidEvent + } + }, + } + + remoteverifywait = &nodeState{ + name: "remoteverifywait", + enter: func(net *Network, n *Node) { + net.timedEvent(respTimeout, n, pingTimeout) + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return remoteverifywait, nil + case pingTimeout: + return known, nil + default: + return remoteverifywait, errInvalidEvent + } + }, + } + + known = &nodeState{ + name: "known", + canQuery: true, + enter: func(net *Network, n *Node) { + n.queryTimeouts = 0 + n.startNextQuery(net) + // Insert into the table and start revalidation of the last node + // in the bucket if it is full. + last := net.tab.add(n) + if last != nil && last.state == known { + // TODO: do this asynchronously + net.transition(last, contested) + } + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return known, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return known, err + default: + return net.handleQueryEvent(n, ev, pkt) + } + }, + } + + contested = &nodeState{ + name: "contested", + canQuery: true, + enter: func(net *Network, n *Node) { + net.ping(n, n.addr()) + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pongPacket: + // Node is still alive. + err := net.handleKnownPong(n, pkt) + return known, err + case pongTimeout: + net.tab.deleteReplace(n) + return unresponsive, nil + case pingPacket: + net.handlePing(n, pkt) + return contested, nil + default: + return net.handleQueryEvent(n, ev, pkt) + } + }, + } + + unresponsive = &nodeState{ + name: "unresponsive", + canQuery: true, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return known, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return known, err + default: + return net.handleQueryEvent(n, ev, pkt) + } + }, + } +} + +// handle processes packets sent by n and events related to n. +func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error { + //fmt.Println("handle", n.addr().String(), n.state, ev) + if pkt != nil { + if err := net.checkPacket(n, ev, pkt); err != nil { + //fmt.Println("check err:", err) + return err + } + // Start the background expiration goroutine after the first + // successful communication. Subsequent calls have no effect if it + // is already running. We do this here instead of somewhere else + // so that the search for seed nodes also considers older nodes + // that would otherwise be removed by the expirer. + if net.db != nil { + net.db.ensureExpirer() + } + } + if n.state == nil { + n.state = unknown //??? + } + next, err := n.state.handle(net, n, ev, pkt) + net.transition(n, next) + //fmt.Println("new state:", n.state) + return err +} + +func (net *Network) checkPacket(n *Node, ev nodeEvent, pkt *ingressPacket) error { + // Replay prevention checks. + switch ev { + case pingPacket, findnodeHashPacket, neighborsPacket: + // TODO: check date is > last date seen + // TODO: check ping version + case pongPacket: + if !bytes.Equal(pkt.data.(*pong).ReplyTok, n.pingEcho) { + // fmt.Println("pong reply token mismatch") + return fmt.Errorf("pong reply token mismatch") + } + n.pingEcho = nil + } + // Address validation. + // TODO: Ideally we would do the following: + // - reject all packets with wrong address except ping. + // - for ping with new address, transition to verifywait but keep the + // previous node (with old address) around. if the new one reaches known, + // swap it out. + return nil +} + +func (net *Network) transition(n *Node, next *nodeState) { + if n.state != next { + n.state = next + if next.enter != nil { + next.enter(net, n) + } + } + + // TODO: persist/unpersist node +} + +func (net *Network) timedEvent(d time.Duration, n *Node, ev nodeEvent) { + timeout := timeoutEvent{ev, n} + net.timeoutTimers[timeout] = time.AfterFunc(d, func() { + select { + case net.timeout <- timeout: + case <-net.closed: + } + }) +} + +func (net *Network) abortTimedEvent(n *Node, ev nodeEvent) { + timer := net.timeoutTimers[timeoutEvent{ev, n}] + if timer != nil { + timer.Stop() + delete(net.timeoutTimers, timeoutEvent{ev, n}) + } +} + +func (net *Network) ping(n *Node, addr *net.UDPAddr) { + //fmt.Println("ping", n.addr().String(), n.ID.String(), n.sha.Hex()) + if n.pingEcho != nil || n.ID == net.tab.self.ID { + //fmt.Println(" not sent") + return + } + debugLog(fmt.Sprintf("ping(node = %x)", n.ID[:8])) + n.pingTopics = net.ticketStore.regTopicSet() + n.pingEcho = net.conn.sendPing(n, addr, n.pingTopics) + net.timedEvent(respTimeout, n, pongTimeout) +} + +func (net *Network) handlePing(n *Node, pkt *ingressPacket) { + debugLog(fmt.Sprintf("handlePing(node = %x)", n.ID[:8])) + ping := pkt.data.(*ping) + n.TCP = ping.From.TCP + t := net.topictab.getTicket(n, ping.Topics) + + pong := &pong{ + To: makeEndpoint(n.addr(), n.TCP), // TODO: maybe use known TCP port from DB + ReplyTok: pkt.hash, + Expiration: uint64(time.Now().Add(expiration).Unix()), + } + ticketToPong(t, pong) + net.conn.send(n, pongPacket, pong) +} + +func (net *Network) handleKnownPong(n *Node, pkt *ingressPacket) error { + debugLog(fmt.Sprintf("handleKnownPong(node = %x)", n.ID[:8])) + net.abortTimedEvent(n, pongTimeout) + now := mclock.Now() + ticket, err := pongToTicket(now, n.pingTopics, n, pkt) + if err == nil { + // fmt.Printf("(%x) ticket: %+v\n", net.tab.self.ID[:8], pkt.data) + net.ticketStore.addTicket(now, pkt.data.(*pong).ReplyTok, ticket) + } else { + debugLog(fmt.Sprintf(" error: %v", err)) + } + + n.pingEcho = nil + n.pingTopics = nil + return err +} + +func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case findnodePacket: + target := crypto.Keccak256Hash(pkt.data.(*findnode).Target[:]) + results := net.tab.closest(target, bucketSize).entries + net.conn.sendNeighbours(n, results) + return n.state, nil + case neighborsPacket: + err := net.handleNeighboursPacket(n, pkt.data.(*neighbors)) + return n.state, err + case neighboursTimeout: + if n.pendingNeighbours != nil { + n.pendingNeighbours.reply <- nil + n.pendingNeighbours = nil + } + n.queryTimeouts++ + if n.queryTimeouts > maxFindnodeFailures && n.state == known { + return contested, errors.New("too many timeouts") + } + return n.state, nil + + // v5 + + case findnodeHashPacket: + results := net.tab.closest(pkt.data.(*findnodeHash).Target, bucketSize).entries + net.conn.sendNeighbours(n, results) + return n.state, nil + case topicRegisterPacket: + //fmt.Println("got topicRegisterPacket") + regdata := pkt.data.(*topicRegister) + pong, err := net.checkTopicRegister(regdata) + if err != nil { + //fmt.Println(err) + return n.state, fmt.Errorf("bad waiting ticket: %v", err) + } + net.topictab.useTicket(n, pong.TicketSerial, regdata.Topics, int(regdata.Idx), pong.Expiration, pong.WaitPeriods) + return n.state, nil + case topicQueryPacket: + // TODO: handle expiration + topic := pkt.data.(*topicQuery).Topic + results := net.topictab.getEntries(topic) + if _, ok := net.ticketStore.tickets[topic]; ok { + results = append(results, net.tab.self) // we're not registering in our own table but if we're advertising, return ourselves too + } + if len(results) > 10 { + results = results[:10] + } + var hash common.Hash + copy(hash[:], pkt.hash) + net.conn.sendTopicNodes(n, hash, results) + return n.state, nil + case topicNodesPacket: + p := pkt.data.(*topicNodes) + if net.ticketStore.gotTopicNodes(n, p.Echo, p.Nodes) { + n.queryTimeouts++ + if n.queryTimeouts > maxFindnodeFailures && n.state == known { + return contested, errors.New("too many timeouts") + } + } + return n.state, nil + + default: + return n.state, errInvalidEvent + } +} + +func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { + var pongpkt ingressPacket + if err := decodePacket(data.Pong, &pongpkt); err != nil { + return nil, err + } + if pongpkt.ev != pongPacket { + return nil, errors.New("is not pong packet") + } + if pongpkt.remoteID != net.tab.self.ID { + return nil, errors.New("not signed by us") + } + // check that we previously authorised all topics + // that the other side is trying to register. + if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash { + return nil, errors.New("topic hash mismatch") + } + if data.Idx < 0 || int(data.Idx) >= len(data.Topics) { + return nil, errors.New("topic index out of range") + } + return pongpkt.data.(*pong), nil +} + +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h +} + +func (net *Network) handleNeighboursPacket(n *Node, req *neighbors) error { + if n.pendingNeighbours == nil { + return errNoQuery + } + net.abortTimedEvent(n, neighboursTimeout) + + nodes := make([]*Node, len(req.Nodes)) + for i, rn := range req.Nodes { + nn, err := net.internNodeFromNeighbours(rn) + if err != nil { + glog.V(logger.Debug).Infof("invalid neighbour from %x: %v", n.ID[:8], err) + continue + } + nodes[i] = nn + // Start validation of query results immediately. + // This fills the table quickly. + // TODO: generates way too many packets, maybe do it via queue. + if nn.state == unknown { + net.transition(nn, verifyinit) + } + } + // TODO: don't ignore second packet + n.pendingNeighbours.reply <- nodes + n.pendingNeighbours = nil + // Now that this query is done, start the next one. + n.startNextQuery(net) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node.go new file mode 100644 index 000000000..b6b6f149d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node.go @@ -0,0 +1,423 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "encoding/hex" + "errors" + "fmt" + "math/big" + "math/rand" + "net" + "net/url" + "regexp" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// Node represents a host on the network. +// The public fields of Node may not be modified. +type Node struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP, TCP uint16 // port numbers + ID NodeID // the node's public key + + // Network-related fields are contained in nodeNetGuts. + // These fields are not supposed to be used off the + // Network.loop goroutine. + nodeNetGuts +} + +// NewNode creates a new node. It is mostly meant to be used for +// testing purposes. +func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + return &Node{ + IP: ip, + UDP: udpPort, + TCP: tcpPort, + ID: id, + nodeNetGuts: nodeNetGuts{sha: crypto.Keccak256Hash(id[:])}, + } +} + +func (n *Node) addr() *net.UDPAddr { + return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} +} + +func (n *Node) setAddr(a *net.UDPAddr) { + n.IP = a.IP + if ipv4 := a.IP.To4(); ipv4 != nil { + n.IP = ipv4 + } + n.UDP = uint16(a.Port) +} + +// compares the given address against the stored values. +func (n *Node) addrEqual(a *net.UDPAddr) bool { + ip := a.IP + if ipv4 := a.IP.To4(); ipv4 != nil { + ip = ipv4 + } + return n.UDP == uint16(a.Port) && bytes.Equal(n.IP, ip) +} + +// Incomplete returns true for nodes with no IP address. +func (n *Node) Incomplete() bool { + return n.IP == nil +} + +// checks whether n is a valid complete node. +func (n *Node) validateComplete() error { + if n.Incomplete() { + return errors.New("incomplete node") + } + if n.UDP == 0 { + return errors.New("missing UDP port") + } + if n.TCP == 0 { + return errors.New("missing TCP port") + } + if n.IP.IsMulticast() || n.IP.IsUnspecified() { + return errors.New("invalid IP (multicast/unspecified)") + } + _, err := n.ID.Pubkey() // validate the key (on curve, etc.) + return err +} + +// The string representation of a Node is a URL. +// Please see ParseNode for a description of the format. +func (n *Node) String() string { + u := url.URL{Scheme: "enode"} + if n.Incomplete() { + u.Host = fmt.Sprintf("%x", n.ID[:]) + } else { + addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)} + u.User = url.User(fmt.Sprintf("%x", n.ID[:])) + u.Host = addr.String() + if n.UDP != n.TCP { + u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP)) + } + } + return u.String() +} + +var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") + +// ParseNode parses a node designator. +// +// There are two basic forms of node designators +// - incomplete nodes, which only have the public key (node ID) +// - complete nodes, which contain the public key and IP/Port information +// +// For incomplete nodes, the designator must look like one of these +// +// enode:// +// +// +// For complete nodes, the node ID is encoded in the username portion +// of the URL, separated from the host by an @ sign. The hostname can +// only be given as an IP address, DNS domain names are not allowed. +// The port in the host name section is the TCP listening port. If the +// TCP and UDP (discovery) ports differ, the UDP port is specified as +// query parameter "discport". +// +// In the following example, the node URL describes +// a node with IP address 10.3.58.6, TCP listening port 30303 +// and UDP discovery port 30301. +// +// enode://@10.3.58.6:30303?discport=30301 +func ParseNode(rawurl string) (*Node, error) { + if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { + id, err := HexID(m[1]) + if err != nil { + return nil, fmt.Errorf("invalid node ID (%v)", err) + } + return NewNode(id, nil, 0, 0), nil + } + return parseComplete(rawurl) +} + +func parseComplete(rawurl string) (*Node, error) { + var ( + id NodeID + ip net.IP + tcpPort, udpPort uint64 + ) + u, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + if u.Scheme != "enode" { + return nil, errors.New("invalid URL scheme, want \"enode\"") + } + // Parse the Node ID from the user portion. + if u.User == nil { + return nil, errors.New("does not contain node ID") + } + if id, err = HexID(u.User.String()); err != nil { + return nil, fmt.Errorf("invalid node ID (%v)", err) + } + // Parse the IP address. + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + return nil, fmt.Errorf("invalid host: %v", err) + } + if ip = net.ParseIP(host); ip == nil { + return nil, errors.New("invalid IP address") + } + // Ensure the IP is 4 bytes long for IPv4 addresses. + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + // Parse the port numbers. + if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { + return nil, errors.New("invalid port") + } + udpPort = tcpPort + qv := u.Query() + if qv.Get("discport") != "" { + udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) + if err != nil { + return nil, errors.New("invalid discport in query") + } + } + return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil +} + +// MustParseNode parses a node URL. It panics if the URL is not valid. +func MustParseNode(rawurl string) *Node { + n, err := ParseNode(rawurl) + if err != nil { + panic("invalid node URL: " + err.Error()) + } + return n +} + +// type nodeQueue []*Node +// +// // pushNew adds n to the end if it is not present. +// func (nl *nodeList) appendNew(n *Node) { +// for _, entry := range n { +// if entry == n { +// return +// } +// } +// *nq = append(*nq, n) +// } +// +// // popRandom removes a random node. Nodes closer to +// // to the head of the beginning of the have a slightly higher probability. +// func (nl *nodeList) popRandom() *Node { +// ix := rand.Intn(len(*nq)) +// //TODO: probability as mentioned above. +// nl.removeIndex(ix) +// } +// +// func (nl *nodeList) removeIndex(i int) *Node { +// slice = *nl +// if len(*slice) <= i { +// return nil +// } +// *nl = append(slice[:i], slice[i+1:]...) +// } + +const nodeIDBits = 512 + +// NodeID is a unique identifier for each node. +// The node identifier is a marshaled elliptic curve public key. +type NodeID [nodeIDBits / 8]byte + +// NodeID prints as a long hexadecimal number. +func (n NodeID) String() string { + return fmt.Sprintf("%x", n[:]) +} + +// The Go syntax representation of a NodeID is a call to HexID. +func (n NodeID) GoString() string { + return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) +} + +// HexID converts a hex string to a NodeID. +// The string may be prefixed with 0x. +func HexID(in string) (NodeID, error) { + if strings.HasPrefix(in, "0x") { + in = in[2:] + } + var id NodeID + b, err := hex.DecodeString(in) + if err != nil { + return id, err + } else if len(b) != len(id) { + return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) + } + copy(id[:], b) + return id, nil +} + +// MustHexID converts a hex string to a NodeID. +// It panics if the string is not a valid NodeID. +func MustHexID(in string) NodeID { + id, err := HexID(in) + if err != nil { + panic(err) + } + return id +} + +// PubkeyID returns a marshaled representation of the given public key. +func PubkeyID(pub *ecdsa.PublicKey) NodeID { + var id NodeID + pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) + if len(pbytes)-1 != len(id) { + panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) + } + copy(id[:], pbytes[1:]) + return id +} + +// Pubkey returns the public key represented by the node ID. +// It returns an error if the ID is not a point on the curve. +func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { + p := &ecdsa.PublicKey{Curve: S256(), X: new(big.Int), Y: new(big.Int)} + half := len(id) / 2 + p.X.SetBytes(id[:half]) + p.Y.SetBytes(id[half:]) + if !p.Curve.IsOnCurve(p.X, p.Y) { + return nil, errors.New("id is invalid secp256k1 curve point") + } + return p, nil +} + +func (id NodeID) mustPubkey() ecdsa.PublicKey { + pk, err := id.Pubkey() + if err != nil { + panic(err) + } + return *pk +} + +// recoverNodeID computes the public key used to sign the +// given hash from the signature. +func recoverNodeID(hash, sig []byte) (id NodeID, err error) { + pubkey, err := crypto.Ecrecover(hash, sig) + if err != nil { + return id, err + } + if len(pubkey)-1 != len(id) { + return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) + } + for i := range id { + id[i] = pubkey[i+1] + } + return id, nil +} + +// distcmp compares the distances a->target and b->target. +// Returns -1 if a is closer to target, 1 if b is closer to target +// and 0 if they are equal. +func distcmp(target, a, b common.Hash) int { + for i := range target { + da := a[i] ^ target[i] + db := b[i] ^ target[i] + if da > db { + return 1 + } else if da < db { + return -1 + } + } + return 0 +} + +// table of leading zero counts for bytes [0..255] +var lzcount = [256]int{ + 8, 7, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +} + +// logdist returns the logarithmic distance between a and b, log2(a ^ b). +func logdist(a, b common.Hash) int { + lz := 0 + for i := range a { + x := a[i] ^ b[i] + if x == 0 { + lz += 8 + } else { + lz += lzcount[x] + break + } + } + return len(a)*8 - lz +} + +// hashAtDistance returns a random hash such that logdist(a, b) == n +func hashAtDistance(a common.Hash, n int) (b common.Hash) { + if n == 0 { + return a + } + // flip bit at position n, fill the rest with random bits + b = a + pos := len(a) - n/8 - 1 + bit := byte(0x01) << (byte(n%8) - 1) + if bit == 0 { + pos++ + bit = 0x80 + } + b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits + for i := pos + 1; i < len(a); i++ { + b[i] = byte(rand.Intn(255)) + } + return b +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/nodeevent_string.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/nodeevent_string.go new file mode 100644 index 000000000..fde9045c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/nodeevent_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type nodeEvent"; DO NOT EDIT + +package discv5 + +import "fmt" + +const ( + _nodeEvent_name_0 = "invalidEventpingPacketpongPacketfindnodePacketneighborsPacketfindnodeHashPackettopicRegisterPackettopicQueryPackettopicNodesPacket" + _nodeEvent_name_1 = "pongTimeoutpingTimeoutneighboursTimeout" +) + +var ( + _nodeEvent_index_0 = [...]uint8{0, 12, 22, 32, 46, 61, 79, 98, 114, 130} + _nodeEvent_index_1 = [...]uint8{0, 11, 22, 39} +) + +func (i nodeEvent) String() string { + switch { + case 0 <= i && i <= 8: + return _nodeEvent_name_0[_nodeEvent_index_0[i]:_nodeEvent_index_0[i+1]] + case 265 <= i && i <= 267: + i -= 265 + return _nodeEvent_name_1[_nodeEvent_index_1[i]:_nodeEvent_index_1[i+1]] + default: + return fmt.Sprintf("nodeEvent(%d)", i) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ntp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ntp.go new file mode 100644 index 000000000..81c0e6336 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ntp.go @@ -0,0 +1,127 @@ +// 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 . + +// Contains the NTP time drift detection via the SNTP protocol: +// https://tools.ietf.org/html/rfc4330 + +package discv5 + +import ( + "fmt" + "net" + "sort" + "strings" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + ntpPool = "pool.ntp.org" // ntpPool is the NTP server to query for the current time + ntpChecks = 3 // Number of measurements to do against the NTP server +) + +// durationSlice attaches the methods of sort.Interface to []time.Duration, +// sorting in increasing order. +type durationSlice []time.Duration + +func (s durationSlice) Len() int { return len(s) } +func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] } +func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// checkClockDrift queries an NTP server for clock drifts and warns the user if +// one large enough is detected. +func checkClockDrift() { + drift, err := sntpDrift(ntpChecks) + if err != nil { + return + } + if drift < -driftThreshold || drift > driftThreshold { + warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift) + howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings") + separator := strings.Repeat("-", len(warning)) + + glog.V(logger.Warn).Info(separator) + glog.V(logger.Warn).Info(warning) + glog.V(logger.Warn).Info(howtofix) + glog.V(logger.Warn).Info(separator) + } else { + glog.V(logger.Debug).Infof("Sanity NTP check reported %v drift, all ok", drift) + } +} + +// sntpDrift does a naive time resolution against an NTP server and returns the +// measured drift. This method uses the simple version of NTP. It's not precise +// but should be fine for these purposes. +// +// Note, it executes two extra measurements compared to the number of requested +// ones to be able to discard the two extremes as outliers. +func sntpDrift(measurements int) (time.Duration, error) { + // Resolve the address of the NTP server + addr, err := net.ResolveUDPAddr("udp", ntpPool+":123") + if err != nil { + return 0, err + } + // Construct the time request (empty package with only 2 fields set): + // Bits 3-5: Protocol version, 3 + // Bits 6-8: Mode of operation, client, 3 + request := make([]byte, 48) + request[0] = 3<<3 | 3 + + // Execute each of the measurements + drifts := []time.Duration{} + for i := 0; i < measurements+2; i++ { + // Dial the NTP server and send the time retrieval request + conn, err := net.DialUDP("udp", nil, addr) + if err != nil { + return 0, err + } + defer conn.Close() + + sent := time.Now() + if _, err = conn.Write(request); err != nil { + return 0, err + } + // Retrieve the reply and calculate the elapsed time + conn.SetDeadline(time.Now().Add(5 * time.Second)) + + reply := make([]byte, 48) + if _, err = conn.Read(reply); err != nil { + return 0, err + } + elapsed := time.Since(sent) + + // Reconstruct the time from the reply data + sec := uint64(reply[43]) | uint64(reply[42])<<8 | uint64(reply[41])<<16 | uint64(reply[40])<<24 + frac := uint64(reply[47]) | uint64(reply[46])<<8 | uint64(reply[45])<<16 | uint64(reply[44])<<24 + + nanosec := sec*1e9 + (frac*1e9)>>32 + + t := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanosec)).Local() + + // Calculate the drift based on an assumed answer time of RRT/2 + drifts = append(drifts, sent.Sub(t)+elapsed/2) + } + // Calculate average drif (drop two extremities to avoid outliers) + sort.Sort(durationSlice(drifts)) + + drift := time.Duration(0) + for i := 1; i < len(drifts)-1; i++ { + drift += drifts[i] + } + return drift / time.Duration(measurements), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go new file mode 100644 index 000000000..2cf05009c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go @@ -0,0 +1,323 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package discv5 implements the RLPx v5 Topic Discovery Protocol. +// +// The Topic Discovery protocol provides a way to find RLPx nodes that +// can be connected to. It uses a Kademlia-like protocol to maintain a +// distributed database of the IDs and endpoints of all listening +// nodes. +package discv5 + +import ( + "crypto/rand" + "encoding/binary" + "fmt" + "net" + "sort" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + alpha = 3 // Kademlia concurrency factor + bucketSize = 16 // Kademlia bucket size + hashBits = len(common.Hash{}) * 8 + nBuckets = hashBits + 1 // Number of buckets + + maxBondingPingPongs = 16 + maxFindnodeFailures = 5 +) + +type Table struct { + count int // number of nodes + buckets [nBuckets]*bucket // index of known nodes by distance + nodeAddedHook func(*Node) // for testing + self *Node // metadata of the local node +} + +// bucket contains nodes, ordered by their last activity. the entry +// that was most recently active is the first element in entries. +type bucket struct { + entries []*Node + replacements []*Node +} + +func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table { + self := NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)) + tab := &Table{self: self} + for i := range tab.buckets { + tab.buckets[i] = new(bucket) + } + return tab +} + +const printTable = false + +// chooseBucketRefreshTarget selects random refresh targets to keep all Kademlia +// buckets filled with live connections and keep the network topology healthy. +// This requires selecting addresses closer to our own with a higher probability +// in order to refresh closer buckets too. +// +// This algorithm approximates the distance distribution of existing nodes in the +// table by selecting a random node from the table and selecting a target address +// with a distance less than twice of that of the selected node. +// This algorithm will be improved later to specifically target the least recently +// used buckets. +func (tab *Table) chooseBucketRefreshTarget() common.Hash { + entries := 0 + if printTable { + fmt.Println() + } + for i, b := range tab.buckets { + entries += len(b.entries) + if printTable { + for _, e := range b.entries { + fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex()) + } + } + } + + prefix := binary.BigEndian.Uint64(tab.self.sha[0:8]) + dist := ^uint64(0) + entry := int(randUint(uint32(entries + 1))) + for _, b := range tab.buckets { + if entry < len(b.entries) { + n := b.entries[entry] + dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix + break + } + entry -= len(b.entries) + } + + ddist := ^uint64(0) + if dist+dist > dist { + ddist = dist + } + targetPrefix := prefix ^ randUint64n(ddist) + + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], targetPrefix) + rand.Read(target[8:]) + return target +} + +// readRandomNodes fills the given slice with random nodes from the +// table. It will not write the same node more than once. The nodes in +// the slice are copies and can be modified by the caller. +func (tab *Table) readRandomNodes(buf []*Node) (n int) { + // TODO: tree-based buckets would help here + // Find all non-empty buckets and get a fresh slice of their entries. + var buckets [][]*Node + for _, b := range tab.buckets { + if len(b.entries) > 0 { + buckets = append(buckets, b.entries[:]) + } + } + if len(buckets) == 0 { + return 0 + } + // Shuffle the buckets. + for i := uint32(len(buckets)) - 1; i > 0; i-- { + j := randUint(i) + buckets[i], buckets[j] = buckets[j], buckets[i] + } + // Move head of each bucket into buf, removing buckets that become empty. + var i, j int + for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { + b := buckets[j] + buf[i] = &(*b[0]) + buckets[j] = b[1:] + if len(b) == 1 { + buckets = append(buckets[:j], buckets[j+1:]...) + } + if len(buckets) == 0 { + break + } + } + return i + 1 +} + +func randUint(max uint32) uint32 { + if max < 2 { + return 0 + } + var b [4]byte + rand.Read(b[:]) + return binary.BigEndian.Uint32(b[:]) % max +} + +func randUint64n(max uint64) uint64 { + if max < 2 { + return 0 + } + var b [8]byte + rand.Read(b[:]) + return binary.BigEndian.Uint64(b[:]) % max +} + +// closest returns the n nodes in the table that are closest to the +// given id. The caller must hold tab.mutex. +func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { + // This is a very wasteful way to find the closest nodes but + // obviously correct. I believe that tree-based buckets would make + // this easier to implement efficiently. + close := &nodesByDistance{target: target} + for _, b := range tab.buckets { + for _, n := range b.entries { + close.push(n, nresults) + } + } + return close +} + +// add attempts to add the given node its corresponding bucket. If the +// bucket has space available, adding the node succeeds immediately. +// Otherwise, the node is added to the replacement cache for the bucket. +func (tab *Table) add(n *Node) (contested *Node) { + //fmt.Println("add", n.addr().String(), n.ID.String(), n.sha.Hex()) + if n.ID == tab.self.ID { + return + } + b := tab.buckets[logdist(tab.self.sha, n.sha)] + switch { + case b.bump(n): + // n exists in b. + return nil + case len(b.entries) < bucketSize: + // b has space available. + b.addFront(n) + tab.count++ + if tab.nodeAddedHook != nil { + tab.nodeAddedHook(n) + } + return nil + default: + // b has no space left, add to replacement cache + // and revalidate the last entry. + // TODO: drop previous node + b.replacements = append(b.replacements, n) + if len(b.replacements) > bucketSize { + copy(b.replacements, b.replacements[1:]) + b.replacements = b.replacements[:len(b.replacements)-1] + } + return b.entries[len(b.entries)-1] + } +} + +// stuff adds nodes the table to the end of their corresponding bucket +// if the bucket is not full. +func (tab *Table) stuff(nodes []*Node) { +outer: + for _, n := range nodes { + if n.ID == tab.self.ID { + continue // don't add self + } + bucket := tab.buckets[logdist(tab.self.sha, n.sha)] + for i := range bucket.entries { + if bucket.entries[i].ID == n.ID { + continue outer // already in bucket + } + } + if len(bucket.entries) < bucketSize { + bucket.entries = append(bucket.entries, n) + tab.count++ + if tab.nodeAddedHook != nil { + tab.nodeAddedHook(n) + } + } + } +} + +// delete removes an entry from the node table (used to evacuate +// failed/non-bonded discovery peers). +func (tab *Table) delete(node *Node) { + //fmt.Println("delete", node.addr().String(), node.ID.String(), node.sha.Hex()) + bucket := tab.buckets[logdist(tab.self.sha, node.sha)] + for i := range bucket.entries { + if bucket.entries[i].ID == node.ID { + bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...) + tab.count-- + return + } + } +} + +func (tab *Table) deleteReplace(node *Node) { + b := tab.buckets[logdist(tab.self.sha, node.sha)] + i := 0 + for i < len(b.entries) { + if b.entries[i].ID == node.ID { + b.entries = append(b.entries[:i], b.entries[i+1:]...) + tab.count-- + } else { + i++ + } + } + // refill from replacement cache + // TODO: maybe use random index + if len(b.entries) < bucketSize && len(b.replacements) > 0 { + ri := len(b.replacements) - 1 + b.addFront(b.replacements[ri]) + tab.count++ + b.replacements[ri] = nil + b.replacements = b.replacements[:ri] + } +} + +func (b *bucket) addFront(n *Node) { + b.entries = append(b.entries, nil) + copy(b.entries[1:], b.entries) + b.entries[0] = n +} + +func (b *bucket) bump(n *Node) bool { + for i := range b.entries { + if b.entries[i].ID == n.ID { + // move it to the front + copy(b.entries[1:], b.entries[:i]) + b.entries[0] = n + return true + } + } + return false +} + +// nodesByDistance is a list of nodes, ordered by +// distance to target. +type nodesByDistance struct { + entries []*Node + target common.Hash +} + +// push adds the given node to the list, keeping the total size below maxElems. +func (h *nodesByDistance) push(n *Node, maxElems int) { + ix := sort.Search(len(h.entries), func(i int) bool { + return distcmp(h.target, h.entries[i].sha, n.sha) > 0 + }) + if len(h.entries) < maxElems { + h.entries = append(h.entries, n) + } + if ix == len(h.entries) { + // farther away than all nodes we already have. + // if there was room for it, the node is now the last element. + } else { + // slide existing entries down to make room + // this will overwrite the entry we just appended. + copy(h.entries[ix+1:], h.entries[ix:]) + h.entries[ix] = n + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go new file mode 100644 index 000000000..202504314 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go @@ -0,0 +1,971 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + "math/rand" + "sort" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/crypto" +) + +const ( + ticketTimeBucketLen = time.Minute + timeWindow = 10 // * ticketTimeBucketLen + wantTicketsInWindow = 10 + collectFrequency = time.Second * 30 + registerFrequency = time.Second * 60 + maxCollectDebt = 10 + maxRegisterDebt = 5 + keepTicketConst = time.Minute * 10 + keepTicketExp = time.Minute * 5 + targetWaitTime = time.Minute * 10 + topicQueryTimeout = time.Second * 5 + topicQueryResend = time.Minute + // topic radius detection + maxRadius = 0xffffffffffffffff + radiusTC = time.Minute * 20 + radiusBucketsPerBit = 8 + minSlope = 1 + minPeakSize = 40 + maxNoAdjust = 20 + lookupWidth = 8 + minRightSum = 20 + searchForceQuery = 4 +) + +// timeBucket represents absolute monotonic time in minutes. +// It is used as the index into the per-topic ticket buckets. +type timeBucket int + +type ticket struct { + topics []Topic + regTime []mclock.AbsTime // Per-topic local absolute time when the ticket can be used. + + // The serial number that was issued by the server. + serial uint32 + // Used by registrar, tracks absolute time when the ticket was created. + issueTime mclock.AbsTime + + // Fields used only by registrants + node *Node // the registrar node that signed this ticket + refCnt int // tracks number of topics that will be registered using this ticket + pong []byte // encoded pong packet signed by the registrar +} + +// ticketRef refers to a single topic in a ticket. +type ticketRef struct { + t *ticket + idx int // index of the topic in t.topics and t.regTime +} + +func (ref ticketRef) topic() Topic { + return ref.t.topics[ref.idx] +} + +func (ref ticketRef) topicRegTime() mclock.AbsTime { + return ref.t.regTime[ref.idx] +} + +func pongToTicket(localTime mclock.AbsTime, topics []Topic, node *Node, p *ingressPacket) (*ticket, error) { + wps := p.data.(*pong).WaitPeriods + if len(topics) != len(wps) { + return nil, fmt.Errorf("bad wait period list: got %d values, want %d", len(topics), len(wps)) + } + if rlpHash(topics) != p.data.(*pong).TopicHash { + return nil, fmt.Errorf("bad topic hash") + } + t := &ticket{ + issueTime: localTime, + node: node, + topics: topics, + pong: p.rawData, + regTime: make([]mclock.AbsTime, len(wps)), + } + // Convert wait periods to local absolute time. + for i, wp := range wps { + t.regTime[i] = localTime + mclock.AbsTime(time.Second*time.Duration(wp)) + } + return t, nil +} + +func ticketToPong(t *ticket, pong *pong) { + pong.Expiration = uint64(t.issueTime / mclock.AbsTime(time.Second)) + pong.TopicHash = rlpHash(t.topics) + pong.TicketSerial = t.serial + pong.WaitPeriods = make([]uint32, len(t.regTime)) + for i, regTime := range t.regTime { + pong.WaitPeriods[i] = uint32(time.Duration(regTime-t.issueTime) / time.Second) + } +} + +type ticketStore struct { + // radius detector and target address generator + // exists for both searched and registered topics + radius map[Topic]*topicRadius + + // Contains buckets (for each absolute minute) of tickets + // that can be used in that minute. + // This is only set if the topic is being registered. + tickets map[Topic]topicTickets + regtopics []Topic + nodes map[*Node]*ticket + nodeLastReq map[*Node]reqInfo + + lastBucketFetched timeBucket + nextTicketCached *ticketRef + nextTicketReg mclock.AbsTime + + searchTopicMap map[Topic]searchTopic + searchTopicList []Topic + searchTopicPtr int + nextTopicQueryCleanup mclock.AbsTime + queriesSent map[*Node]map[common.Hash]sentQuery + radiusLookupCnt int +} + +type searchTopic struct { + foundChn chan<- string + listIdx int +} + +type sentQuery struct { + sent mclock.AbsTime + lookup lookupInfo +} + +type topicTickets struct { + buckets map[timeBucket][]ticketRef + nextLookup, nextReg mclock.AbsTime +} + +func newTicketStore() *ticketStore { + return &ticketStore{ + radius: make(map[Topic]*topicRadius), + tickets: make(map[Topic]topicTickets), + nodes: make(map[*Node]*ticket), + nodeLastReq: make(map[*Node]reqInfo), + searchTopicMap: make(map[Topic]searchTopic), + queriesSent: make(map[*Node]map[common.Hash]sentQuery), + } +} + +// addTopic starts tracking a topic. If register is true, +// the local node will register the topic and tickets will be collected. +func (s *ticketStore) addTopic(t Topic, register bool) { + debugLog(fmt.Sprintf(" addTopic(%v, %v)", t, register)) + if s.radius[t] == nil { + s.radius[t] = newTopicRadius(t) + } + if register && s.tickets[t].buckets == nil { + s.tickets[t] = topicTickets{buckets: make(map[timeBucket][]ticketRef)} + } +} + +func (s *ticketStore) addSearchTopic(t Topic, foundChn chan<- string) { + s.addTopic(t, false) + if s.searchTopicMap[t].foundChn == nil { + s.searchTopicList = append(s.searchTopicList, t) + s.searchTopicMap[t] = searchTopic{foundChn: foundChn, listIdx: len(s.searchTopicList) - 1} + } +} + +func (s *ticketStore) removeSearchTopic(t Topic) { + if st := s.searchTopicMap[t]; st.foundChn != nil { + lastIdx := len(s.searchTopicList) - 1 + lastTopic := s.searchTopicList[lastIdx] + s.searchTopicList[st.listIdx] = lastTopic + sl := s.searchTopicMap[lastTopic] + sl.listIdx = st.listIdx + s.searchTopicMap[lastTopic] = sl + s.searchTopicList = s.searchTopicList[:lastIdx] + delete(s.searchTopicMap, t) + } +} + +// removeRegisterTopic deletes all tickets for the given topic. +func (s *ticketStore) removeRegisterTopic(topic Topic) { + debugLog(fmt.Sprintf(" removeRegisterTopic(%v)", topic)) + for _, list := range s.tickets[topic].buckets { + for _, ref := range list { + ref.t.refCnt-- + if ref.t.refCnt == 0 { + delete(s.nodes, ref.t.node) + delete(s.nodeLastReq, ref.t.node) + } + } + } + delete(s.tickets, topic) +} + +func (s *ticketStore) regTopicSet() []Topic { + topics := make([]Topic, 0, len(s.tickets)) + for topic := range s.tickets { + topics = append(topics, topic) + } + return topics +} + +// nextRegisterLookup returns the target of the next lookup for ticket collection. +func (s *ticketStore) nextRegisterLookup() (lookup lookupInfo, delay time.Duration) { + debugLog("nextRegisterLookup()") + firstTopic, ok := s.iterRegTopics() + for topic := firstTopic; ok; { + debugLog(fmt.Sprintf(" checking topic %v, len(s.tickets[topic]) = %d", topic, len(s.tickets[topic].buckets))) + if s.tickets[topic].buckets != nil && s.needMoreTickets(topic) { + next := s.radius[topic].nextTarget(false) + debugLog(fmt.Sprintf(" %x 1s", next.target[:8])) + return next, 100 * time.Millisecond + } + topic, ok = s.iterRegTopics() + if topic == firstTopic { + break // We have checked all topics. + } + } + debugLog(" null, 40s") + return lookupInfo{}, 40 * time.Second +} + +func (s *ticketStore) nextSearchLookup() lookupInfo { + if len(s.searchTopicList) == 0 { + return lookupInfo{} + } + if s.searchTopicPtr >= len(s.searchTopicList) { + s.searchTopicPtr = 0 + } + topic := s.searchTopicList[s.searchTopicPtr] + s.searchTopicPtr++ + target := s.radius[topic].nextTarget(s.radiusLookupCnt >= searchForceQuery) + if target.radiusLookup { + s.radiusLookupCnt++ + } else { + s.radiusLookupCnt = 0 + } + return target +} + +// iterRegTopics returns topics to register in arbitrary order. +// The second return value is false if there are no topics. +func (s *ticketStore) iterRegTopics() (Topic, bool) { + debugLog("iterRegTopics()") + if len(s.regtopics) == 0 { + if len(s.tickets) == 0 { + debugLog(" false") + return "", false + } + // Refill register list. + for t := range s.tickets { + s.regtopics = append(s.regtopics, t) + } + } + topic := s.regtopics[len(s.regtopics)-1] + s.regtopics = s.regtopics[:len(s.regtopics)-1] + debugLog(" " + string(topic) + " true") + return topic, true +} + +func (s *ticketStore) needMoreTickets(t Topic) bool { + return s.tickets[t].nextLookup < mclock.Now() +} + +// ticketsInWindow returns the tickets of a given topic in the registration window. +func (s *ticketStore) ticketsInWindow(t Topic) []ticketRef { + ltBucket := s.lastBucketFetched + var res []ticketRef + tickets := s.tickets[t].buckets + for g := ltBucket; g < ltBucket+timeWindow; g++ { + res = append(res, tickets[g]...) + } + debugLog(fmt.Sprintf("ticketsInWindow(%v) = %v", t, len(res))) + return res +} + +func (s *ticketStore) removeExcessTickets(t Topic) { + tickets := s.ticketsInWindow(t) + if len(tickets) <= wantTicketsInWindow { + return + } + sort.Sort(ticketRefByWaitTime(tickets)) + for _, r := range tickets[wantTicketsInWindow:] { + s.removeTicketRef(r) + } +} + +type ticketRefByWaitTime []ticketRef + +// Len is the number of elements in the collection. +func (s ticketRefByWaitTime) Len() int { + return len(s) +} + +func (r ticketRef) waitTime() mclock.AbsTime { + return r.t.regTime[r.idx] - r.t.issueTime +} + +// Less reports whether the element with +// index i should sort before the element with index j. +func (s ticketRefByWaitTime) Less(i, j int) bool { + return s[i].waitTime() < s[j].waitTime() +} + +// Swap swaps the elements with indexes i and j. +func (s ticketRefByWaitTime) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s *ticketStore) addTicketRef(r ticketRef) { + topic := r.t.topics[r.idx] + t := s.tickets[topic] + if t.buckets == nil { + return + } + bucket := timeBucket(r.t.regTime[r.idx] / mclock.AbsTime(ticketTimeBucketLen)) + t.buckets[bucket] = append(t.buckets[bucket], r) + r.t.refCnt++ + + min := mclock.Now() - mclock.AbsTime(collectFrequency)*maxCollectDebt + if t.nextLookup < min { + t.nextLookup = min + } + t.nextLookup += mclock.AbsTime(collectFrequency) + s.tickets[topic] = t + + //s.removeExcessTickets(topic) +} + +func (s *ticketStore) nextFilteredTicket() (t *ticketRef, wait time.Duration) { + now := mclock.Now() + for { + t, wait = s.nextRegisterableTicket() + if t == nil { + return + } + regTime := now + mclock.AbsTime(wait) + topic := t.t.topics[t.idx] + if regTime >= s.tickets[topic].nextReg { + return + } + s.removeTicketRef(*t) + } +} + +func (s *ticketStore) ticketRegistered(t ticketRef) { + now := mclock.Now() + + topic := t.t.topics[t.idx] + tt := s.tickets[topic] + min := now - mclock.AbsTime(registerFrequency)*maxRegisterDebt + if min > tt.nextReg { + tt.nextReg = min + } + tt.nextReg += mclock.AbsTime(registerFrequency) + s.tickets[topic] = tt + + s.removeTicketRef(t) +} + +// nextRegisterableTicket returns the next ticket that can be used +// to register. +// +// If the returned wait time <= zero the ticket can be used. For a positive +// wait time, the caller should requery the next ticket later. +// +// A ticket can be returned more than once with <= zero wait time in case +// the ticket contains multiple topics. +func (s *ticketStore) nextRegisterableTicket() (t *ticketRef, wait time.Duration) { + defer func() { + if t == nil { + debugLog(" nil") + } else { + debugLog(fmt.Sprintf(" node = %x sn = %v wait = %v", t.t.node.ID[:8], t.t.serial, wait)) + } + }() + + debugLog("nextRegisterableTicket()") + now := mclock.Now() + if s.nextTicketCached != nil { + return s.nextTicketCached, time.Duration(s.nextTicketCached.topicRegTime() - now) + } + + for bucket := s.lastBucketFetched; ; bucket++ { + var ( + empty = true // true if there are no tickets + nextTicket ticketRef // uninitialized if this bucket is empty + ) + for _, tickets := range s.tickets { + //s.removeExcessTickets(topic) + if len(tickets.buckets) != 0 { + empty = false + if list := tickets.buckets[bucket]; list != nil { + for _, ref := range list { + //debugLog(fmt.Sprintf(" nrt bucket = %d node = %x sn = %v wait = %v", bucket, ref.t.node.ID[:8], ref.t.serial, time.Duration(ref.topicRegTime()-now))) + if nextTicket.t == nil || ref.topicRegTime() < nextTicket.topicRegTime() { + nextTicket = ref + } + } + } + } + } + if empty { + return nil, 0 + } + if nextTicket.t != nil { + wait = time.Duration(nextTicket.topicRegTime() - now) + s.nextTicketCached = &nextTicket + return &nextTicket, wait + } + s.lastBucketFetched = bucket + } +} + +// removeTicket removes a ticket from the ticket store +func (s *ticketStore) removeTicketRef(ref ticketRef) { + debugLog(fmt.Sprintf("removeTicketRef(node = %x sn = %v)", ref.t.node.ID[:8], ref.t.serial)) + topic := ref.topic() + tickets := s.tickets[topic].buckets + if tickets == nil { + return + } + bucket := timeBucket(ref.t.regTime[ref.idx] / mclock.AbsTime(ticketTimeBucketLen)) + list := tickets[bucket] + idx := -1 + for i, bt := range list { + if bt.t == ref.t { + idx = i + break + } + } + if idx == -1 { + panic(nil) + } + list = append(list[:idx], list[idx+1:]...) + if len(list) != 0 { + tickets[bucket] = list + } else { + delete(tickets, bucket) + } + ref.t.refCnt-- + if ref.t.refCnt == 0 { + delete(s.nodes, ref.t.node) + delete(s.nodeLastReq, ref.t.node) + } + + // Make nextRegisterableTicket return the next available ticket. + s.nextTicketCached = nil +} + +type lookupInfo struct { + target common.Hash + topic Topic + radiusLookup bool +} + +type reqInfo struct { + pingHash []byte + lookup lookupInfo + time mclock.AbsTime +} + +// returns -1 if not found +func (t *ticket) findIdx(topic Topic) int { + for i, tt := range t.topics { + if tt == topic { + return i + } + } + return -1 +} + +func (s *ticketStore) registerLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte) { + now := mclock.Now() + for i, n := range nodes { + if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { + if lookup.radiusLookup { + if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } + } else { + if s.nodes[n] == nil { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } + } + } + } +} + +func (s *ticketStore) searchLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte, query func(n *Node, topic Topic) []byte) { + now := mclock.Now() + for i, n := range nodes { + if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { + if lookup.radiusLookup { + if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } + } // else { + if s.canQueryTopic(n, lookup.topic) { + hash := query(n, lookup.topic) + if hash != nil { + s.addTopicQuery(common.BytesToHash(hash), n, lookup) + } + } + //} + } + } +} + +func (s *ticketStore) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t *ticket) { + for i, topic := range t.topics { + if tt, ok := s.radius[topic]; ok { + tt.adjustWithTicket(now, targetHash, ticketRef{t, i}) + } + } +} + +func (s *ticketStore) addTicket(localTime mclock.AbsTime, pingHash []byte, t *ticket) { + debugLog(fmt.Sprintf("add(node = %x sn = %v)", t.node.ID[:8], t.serial)) + + lastReq, ok := s.nodeLastReq[t.node] + if !(ok && bytes.Equal(pingHash, lastReq.pingHash)) { + return + } + s.adjustWithTicket(localTime, lastReq.lookup.target, t) + + if lastReq.lookup.radiusLookup || s.nodes[t.node] != nil { + return + } + + topic := lastReq.lookup.topic + topicIdx := t.findIdx(topic) + if topicIdx == -1 { + return + } + + bucket := timeBucket(localTime / mclock.AbsTime(ticketTimeBucketLen)) + if s.lastBucketFetched == 0 || bucket < s.lastBucketFetched { + s.lastBucketFetched = bucket + } + + if _, ok := s.tickets[topic]; ok { + wait := t.regTime[topicIdx] - localTime + rnd := rand.ExpFloat64() + if rnd > 10 { + rnd = 10 + } + if float64(wait) < float64(keepTicketConst)+float64(keepTicketExp)*rnd { + // use the ticket to register this topic + //fmt.Println("addTicket", t.node.ID[:8], t.node.addr().String(), t.serial, t.pong) + s.addTicketRef(ticketRef{t, topicIdx}) + } + } + + if t.refCnt > 0 { + s.nextTicketCached = nil + s.nodes[t.node] = t + } +} + +func (s *ticketStore) getNodeTicket(node *Node) *ticket { + if s.nodes[node] == nil { + debugLog(fmt.Sprintf("getNodeTicket(%x) sn = nil", node.ID[:8])) + } else { + debugLog(fmt.Sprintf("getNodeTicket(%x) sn = %v", node.ID[:8], s.nodes[node].serial)) + } + return s.nodes[node] +} + +func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool { + qq := s.queriesSent[node] + if qq != nil { + now := mclock.Now() + for _, sq := range qq { + if sq.lookup.topic == topic && sq.sent > now-mclock.AbsTime(topicQueryResend) { + return false + } + } + } + return true +} + +func (s *ticketStore) addTopicQuery(hash common.Hash, node *Node, lookup lookupInfo) { + now := mclock.Now() + qq := s.queriesSent[node] + if qq == nil { + qq = make(map[common.Hash]sentQuery) + s.queriesSent[node] = qq + } + qq[hash] = sentQuery{sent: now, lookup: lookup} + s.cleanupTopicQueries(now) +} + +func (s *ticketStore) cleanupTopicQueries(now mclock.AbsTime) { + if s.nextTopicQueryCleanup > now { + return + } + exp := now - mclock.AbsTime(topicQueryResend) + for n, qq := range s.queriesSent { + for h, q := range qq { + if q.sent < exp { + delete(qq, h) + } + } + if len(qq) == 0 { + delete(s.queriesSent, n) + } + } + s.nextTopicQueryCleanup = now + mclock.AbsTime(topicQueryTimeout) +} + +func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNode) (timeout bool) { + now := mclock.Now() + //fmt.Println("got", from.addr().String(), hash, len(nodes)) + qq := s.queriesSent[from] + if qq == nil { + return true + } + q, ok := qq[hash] + if !ok || now > q.sent+mclock.AbsTime(topicQueryTimeout) { + return true + } + inside := float64(0) + if len(nodes) > 0 { + inside = 1 + } + s.radius[q.lookup.topic].adjust(now, q.lookup.target, from.sha, inside) + chn := s.searchTopicMap[q.lookup.topic].foundChn + if chn == nil { + //fmt.Println("no channel") + return false + } + for _, node := range nodes { + ip := node.IP + if ip.IsUnspecified() || ip.IsLoopback() { + ip = from.IP + } + enode := NewNode(node.ID, ip, node.UDP-1, node.TCP-1).String() // subtract one from port while discv5 is running in test mode on UDPport+1 + select { + case chn <- enode: + default: + return false + } + } + return false +} + +type topicRadius struct { + topic Topic + topicHashPrefix uint64 + radius, minRadius uint64 + buckets []topicRadiusBucket +} + +type topicRadiusEvent int + +const ( + trOutside topicRadiusEvent = iota + trInside + trNoAdjust + trCount +) + +type topicRadiusBucket struct { + weights [trCount]float64 + lastTime mclock.AbsTime + value float64 + lookupSent map[common.Hash]mclock.AbsTime +} + +func (b *topicRadiusBucket) update(now mclock.AbsTime) { + if now == b.lastTime { + return + } + exp := math.Exp(-float64(now-b.lastTime) / float64(radiusTC)) + for i, w := range b.weights { + b.weights[i] = w * exp + } + b.lastTime = now + + for target, tm := range b.lookupSent { + if now-tm > mclock.AbsTime(pingTimeout) { + b.weights[trNoAdjust] += 1 + delete(b.lookupSent, target) + } + } +} + +func (b *topicRadiusBucket) adjust(now mclock.AbsTime, inside float64) { + b.update(now) + if inside <= 0 { + b.weights[trOutside] += 1 + } else { + if inside >= 1 { + b.weights[trInside] += 1 + } else { + b.weights[trInside] += inside + b.weights[trOutside] += 1 - inside + } + } +} + +func newTopicRadius(t Topic) *topicRadius { + topicHash := crypto.Keccak256Hash([]byte(t)) + topicHashPrefix := binary.BigEndian.Uint64(topicHash[0:8]) + + return &topicRadius{ + topic: t, + topicHashPrefix: topicHashPrefix, + radius: maxRadius, + minRadius: maxRadius, + } +} + +func (r *topicRadius) getBucketIdx(addrHash common.Hash) int { + prefix := binary.BigEndian.Uint64(addrHash[0:8]) + var log2 float64 + if prefix != r.topicHashPrefix { + log2 = math.Log2(float64(prefix ^ r.topicHashPrefix)) + } + bucket := int((64 - log2) * radiusBucketsPerBit) + max := 64*radiusBucketsPerBit - 1 + if bucket > max { + return max + } + if bucket < 0 { + return 0 + } + return bucket +} + +func (r *topicRadius) targetForBucket(bucket int) common.Hash { + min := math.Pow(2, 64-float64(bucket+1)/radiusBucketsPerBit) + max := math.Pow(2, 64-float64(bucket)/radiusBucketsPerBit) + a := uint64(min) + b := randUint64n(uint64(max - min)) + xor := a + b + if xor < a { + xor = ^uint64(0) + } + prefix := r.topicHashPrefix ^ xor + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], prefix) + globalRandRead(target[8:]) + return target +} + +// package rand provides a Read function in Go 1.6 and later, but +// we can't use it yet because we still support Go 1.5. +func globalRandRead(b []byte) { + pos := 0 + val := 0 + for n := 0; n < len(b); n++ { + if pos == 0 { + val = rand.Int() + pos = 7 + } + b[n] = byte(val) + val >>= 8 + pos-- + } +} + +func (r *topicRadius) isInRadius(addrHash common.Hash) bool { + nodePrefix := binary.BigEndian.Uint64(addrHash[0:8]) + dist := nodePrefix ^ r.topicHashPrefix + return dist < r.radius +} + +func (r *topicRadius) chooseLookupBucket(a, b int) int { + if a < 0 { + a = 0 + } + if a > b { + return -1 + } + c := 0 + for i := a; i <= b; i++ { + if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { + c++ + } + } + if c == 0 { + return -1 + } + rnd := randUint(uint32(c)) + for i := a; i <= b; i++ { + if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { + if rnd == 0 { + return i + } + rnd-- + } + } + panic(nil) // should never happen +} + +func (r *topicRadius) needMoreLookups(a, b int, maxValue float64) bool { + var max float64 + if a < 0 { + a = 0 + } + if b >= len(r.buckets) { + b = len(r.buckets) - 1 + if r.buckets[b].value > max { + max = r.buckets[b].value + } + } + if b >= a { + for i := a; i <= b; i++ { + if r.buckets[i].value > max { + max = r.buckets[i].value + } + } + } + return maxValue-max < minPeakSize +} + +func (r *topicRadius) recalcRadius() (radius uint64, radiusLookup int) { + maxBucket := 0 + maxValue := float64(0) + now := mclock.Now() + v := float64(0) + for i, _ := range r.buckets { + r.buckets[i].update(now) + v += r.buckets[i].weights[trOutside] - r.buckets[i].weights[trInside] + r.buckets[i].value = v + //fmt.Printf("%v %v | ", v, r.buckets[i].weights[trNoAdjust]) + } + //fmt.Println() + slopeCross := -1 + for i, b := range r.buckets { + v := b.value + if v < float64(i)*minSlope { + slopeCross = i + break + } + if v > maxValue { + maxValue = v + maxBucket = i + 1 + } + } + + minRadBucket := len(r.buckets) + sum := float64(0) + for minRadBucket > 0 && sum < minRightSum { + minRadBucket-- + b := r.buckets[minRadBucket] + sum += b.weights[trInside] + b.weights[trOutside] + } + r.minRadius = uint64(math.Pow(2, 64-float64(minRadBucket)/radiusBucketsPerBit)) + + lookupLeft := -1 + if r.needMoreLookups(0, maxBucket-lookupWidth-1, maxValue) { + lookupLeft = r.chooseLookupBucket(maxBucket-lookupWidth, maxBucket-1) + } + lookupRight := -1 + if slopeCross != maxBucket && (minRadBucket <= maxBucket || r.needMoreLookups(maxBucket+lookupWidth, len(r.buckets)-1, maxValue)) { + for len(r.buckets) <= maxBucket+lookupWidth { + r.buckets = append(r.buckets, topicRadiusBucket{lookupSent: make(map[common.Hash]mclock.AbsTime)}) + } + lookupRight = r.chooseLookupBucket(maxBucket, maxBucket+lookupWidth-1) + } + if lookupLeft == -1 { + radiusLookup = lookupRight + } else { + if lookupRight == -1 { + radiusLookup = lookupLeft + } else { + if randUint(2) == 0 { + radiusLookup = lookupLeft + } else { + radiusLookup = lookupRight + } + } + } + + //fmt.Println("mb", maxBucket, "sc", slopeCross, "mrb", minRadBucket, "ll", lookupLeft, "lr", lookupRight, "mv", maxValue) + + if radiusLookup == -1 { + // no more radius lookups needed at the moment, return a radius + rad := maxBucket + if minRadBucket < rad { + rad = minRadBucket + } + radius = ^uint64(0) + if rad > 0 { + radius = uint64(math.Pow(2, 64-float64(rad)/radiusBucketsPerBit)) + } + r.radius = radius + } + + return +} + +func (r *topicRadius) nextTarget(forceRegular bool) lookupInfo { + if !forceRegular { + _, radiusLookup := r.recalcRadius() + if radiusLookup != -1 { + target := r.targetForBucket(radiusLookup) + r.buckets[radiusLookup].lookupSent[target] = mclock.Now() + return lookupInfo{target: target, topic: r.topic, radiusLookup: true} + } + } + + radExt := r.radius / 2 + if radExt > maxRadius-r.radius { + radExt = maxRadius - r.radius + } + rnd := randUint64n(r.radius) + randUint64n(2*radExt) + if rnd > radExt { + rnd -= radExt + } else { + rnd = radExt - rnd + } + + prefix := r.topicHashPrefix ^ rnd + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], prefix) + globalRandRead(target[8:]) + return lookupInfo{target: target, topic: r.topic, radiusLookup: false} +} + +func (r *topicRadius) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t ticketRef) { + wait := t.t.regTime[t.idx] - t.t.issueTime + inside := float64(wait)/float64(targetWaitTime) - 0.5 + if inside > 1 { + inside = 1 + } + if inside < 0 { + inside = 0 + } + r.adjust(now, targetHash, t.t.node.sha, inside) +} + +func (r *topicRadius) adjust(now mclock.AbsTime, targetHash, addrHash common.Hash, inside float64) { + bucket := r.getBucketIdx(addrHash) + //fmt.Println("adjust", bucket, len(r.buckets), inside) + if bucket >= len(r.buckets) { + return + } + r.buckets[bucket].adjust(now, inside) + delete(r.buckets[bucket].lookupSent, targetHash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go new file mode 100644 index 000000000..625921e84 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go @@ -0,0 +1,406 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "container/heap" + "fmt" + "math" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" +) + +const ( + maxEntries = 10000 + maxEntriesPerTopic = 50 + + fallbackRegistrationExpiry = 1 * time.Hour +) + +type Topic string + +type topicEntry struct { + topic Topic + fifoIdx uint64 + node *Node + expire mclock.AbsTime +} + +type topicInfo struct { + entries map[uint64]*topicEntry + fifoHead, fifoTail uint64 + rqItem *topicRequestQueueItem + wcl waitControlLoop +} + +// removes tail element from the fifo +func (t *topicInfo) getFifoTail() *topicEntry { + for t.entries[t.fifoTail] == nil { + t.fifoTail++ + } + tail := t.entries[t.fifoTail] + t.fifoTail++ + return tail +} + +type nodeInfo struct { + entries map[Topic]*topicEntry + lastIssuedTicket, lastUsedTicket uint32 + // you can't register a ticket newer than lastUsedTicket before noRegUntil (absolute time) + noRegUntil mclock.AbsTime +} + +type topicTable struct { + db *nodeDB + self *Node + nodes map[*Node]*nodeInfo + topics map[Topic]*topicInfo + globalEntries uint64 + requested topicRequestQueue + requestCnt uint64 + lastGarbageCollection mclock.AbsTime +} + +func newTopicTable(db *nodeDB, self *Node) *topicTable { + if printTestImgLogs { + fmt.Printf("*N %016x\n", self.sha[:8]) + } + return &topicTable{ + db: db, + nodes: make(map[*Node]*nodeInfo), + topics: make(map[Topic]*topicInfo), + self: self, + } +} + +func (t *topicTable) getOrNewTopic(topic Topic) *topicInfo { + ti := t.topics[topic] + if ti == nil { + rqItem := &topicRequestQueueItem{ + topic: topic, + priority: t.requestCnt, + } + ti = &topicInfo{ + entries: make(map[uint64]*topicEntry), + rqItem: rqItem, + } + t.topics[topic] = ti + heap.Push(&t.requested, rqItem) + } + return ti +} + +func (t *topicTable) checkDeleteTopic(topic Topic) { + ti := t.topics[topic] + if ti == nil { + return + } + if len(ti.entries) == 0 && ti.wcl.hasMinimumWaitPeriod() { + delete(t.topics, topic) + heap.Remove(&t.requested, ti.rqItem.index) + } +} + +func (t *topicTable) getOrNewNode(node *Node) *nodeInfo { + n := t.nodes[node] + if n == nil { + //fmt.Printf("newNode %016x %016x\n", t.self.sha[:8], node.sha[:8]) + var issued, used uint32 + if t.db != nil { + issued, used = t.db.fetchTopicRegTickets(node.ID) + } + n = &nodeInfo{ + entries: make(map[Topic]*topicEntry), + lastIssuedTicket: issued, + lastUsedTicket: used, + } + t.nodes[node] = n + } + return n +} + +func (t *topicTable) checkDeleteNode(node *Node) { + if n, ok := t.nodes[node]; ok && len(n.entries) == 0 && n.noRegUntil < mclock.Now() { + //fmt.Printf("deleteNode %016x %016x\n", t.self.sha[:8], node.sha[:8]) + delete(t.nodes, node) + } +} + +func (t *topicTable) storeTicketCounters(node *Node) { + n := t.getOrNewNode(node) + if t.db != nil { + t.db.updateTopicRegTickets(node.ID, n.lastIssuedTicket, n.lastUsedTicket) + } +} + +func (t *topicTable) getEntries(topic Topic) []*Node { + t.collectGarbage() + + te := t.topics[topic] + if te == nil { + return nil + } + nodes := make([]*Node, len(te.entries)) + i := 0 + for _, e := range te.entries { + nodes[i] = e.node + i++ + } + t.requestCnt++ + t.requested.update(te.rqItem, t.requestCnt) + return nodes +} + +func (t *topicTable) addEntry(node *Node, topic Topic) { + n := t.getOrNewNode(node) + // clear previous entries by the same node + for _, e := range n.entries { + t.deleteEntry(e) + } + // *** + n = t.getOrNewNode(node) + + tm := mclock.Now() + te := t.getOrNewTopic(topic) + + if len(te.entries) == maxEntriesPerTopic { + t.deleteEntry(te.getFifoTail()) + } + + if t.globalEntries == maxEntries { + t.deleteEntry(t.leastRequested()) // not empty, no need to check for nil + } + + fifoIdx := te.fifoHead + te.fifoHead++ + entry := &topicEntry{ + topic: topic, + fifoIdx: fifoIdx, + node: node, + expire: tm + mclock.AbsTime(fallbackRegistrationExpiry), + } + if printTestImgLogs { + fmt.Printf("*+ %d %v %016x %016x\n", tm/1000000, topic, t.self.sha[:8], node.sha[:8]) + } + te.entries[fifoIdx] = entry + n.entries[topic] = entry + t.globalEntries++ + te.wcl.registered(tm) +} + +// removes least requested element from the fifo +func (t *topicTable) leastRequested() *topicEntry { + for t.requested.Len() > 0 && t.topics[t.requested[0].topic] == nil { + heap.Pop(&t.requested) + } + if t.requested.Len() == 0 { + return nil + } + return t.topics[t.requested[0].topic].getFifoTail() +} + +// entry should exist +func (t *topicTable) deleteEntry(e *topicEntry) { + if printTestImgLogs { + fmt.Printf("*- %d %v %016x %016x\n", mclock.Now()/1000000, e.topic, t.self.sha[:8], e.node.sha[:8]) + } + ne := t.nodes[e.node].entries + delete(ne, e.topic) + if len(ne) == 0 { + t.checkDeleteNode(e.node) + } + te := t.topics[e.topic] + delete(te.entries, e.fifoIdx) + if len(te.entries) == 0 { + t.checkDeleteTopic(e.topic) + } + t.globalEntries-- +} + +// It is assumed that topics and waitPeriods have the same length. +func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx int, issueTime uint64, waitPeriods []uint32) (registered bool) { + debugLog(fmt.Sprintf("useTicket %v %v %v", serialNo, topics, waitPeriods)) + //fmt.Println("useTicket", serialNo, topics, waitPeriods) + t.collectGarbage() + + n := t.getOrNewNode(node) + if serialNo < n.lastUsedTicket { + return false + } + + tm := mclock.Now() + if serialNo > n.lastUsedTicket && tm < n.noRegUntil { + return false + } + if serialNo != n.lastUsedTicket { + n.lastUsedTicket = serialNo + n.noRegUntil = tm + mclock.AbsTime(noRegTimeout()) + t.storeTicketCounters(node) + } + + currTime := uint64(tm / mclock.AbsTime(time.Second)) + regTime := issueTime + uint64(waitPeriods[idx]) + relTime := int64(currTime - regTime) + if relTime >= -1 && relTime <= regTimeWindow+1 { // give clients a little security margin on both ends + if e := n.entries[topics[idx]]; e == nil { + t.addEntry(node, topics[idx]) + } else { + // if there is an active entry, don't move to the front of the FIFO but prolong expire time + e.expire = tm + mclock.AbsTime(fallbackRegistrationExpiry) + } + return true + } + + return false +} + +func (topictab *topicTable) getTicket(node *Node, topics []Topic) *ticket { + topictab.collectGarbage() + + now := mclock.Now() + n := topictab.getOrNewNode(node) + n.lastIssuedTicket++ + topictab.storeTicketCounters(node) + + t := &ticket{ + issueTime: now, + topics: topics, + serial: n.lastIssuedTicket, + regTime: make([]mclock.AbsTime, len(topics)), + } + for i, topic := range topics { + var waitPeriod time.Duration + if topic := topictab.topics[topic]; topic != nil { + waitPeriod = topic.wcl.waitPeriod + } else { + waitPeriod = minWaitPeriod + } + + t.regTime[i] = now + mclock.AbsTime(waitPeriod) + } + return t +} + +const gcInterval = time.Minute + +func (t *topicTable) collectGarbage() { + tm := mclock.Now() + if time.Duration(tm-t.lastGarbageCollection) < gcInterval { + return + } + t.lastGarbageCollection = tm + + for node, n := range t.nodes { + for _, e := range n.entries { + if e.expire <= tm { + t.deleteEntry(e) + } + } + + t.checkDeleteNode(node) + } + + for topic, _ := range t.topics { + t.checkDeleteTopic(topic) + } +} + +const ( + minWaitPeriod = time.Minute + regTimeWindow = 10 // seconds + avgnoRegTimeout = time.Minute * 10 + // target average interval between two incoming ad requests + wcTargetRegInterval = time.Minute * 10 / maxEntriesPerTopic + // + wcTimeConst = time.Minute * 10 +) + +// initialization is not required, will set to minWaitPeriod at first registration +type waitControlLoop struct { + lastIncoming mclock.AbsTime + waitPeriod time.Duration +} + +func (w *waitControlLoop) registered(tm mclock.AbsTime) { + w.waitPeriod = w.nextWaitPeriod(tm) + w.lastIncoming = tm +} + +func (w *waitControlLoop) nextWaitPeriod(tm mclock.AbsTime) time.Duration { + period := tm - w.lastIncoming + wp := time.Duration(float64(w.waitPeriod) * math.Exp((float64(wcTargetRegInterval)-float64(period))/float64(wcTimeConst))) + if wp < minWaitPeriod { + wp = minWaitPeriod + } + return wp +} + +func (w *waitControlLoop) hasMinimumWaitPeriod() bool { + return w.nextWaitPeriod(mclock.Now()) == minWaitPeriod +} + +func noRegTimeout() time.Duration { + e := rand.ExpFloat64() + if e > 100 { + e = 100 + } + return time.Duration(float64(avgnoRegTimeout) * e) +} + +type topicRequestQueueItem struct { + topic Topic + priority uint64 + index int +} + +// A topicRequestQueue implements heap.Interface and holds topicRequestQueueItems. +type topicRequestQueue []*topicRequestQueueItem + +func (tq topicRequestQueue) Len() int { return len(tq) } + +func (tq topicRequestQueue) Less(i, j int) bool { + return tq[i].priority < tq[j].priority +} + +func (tq topicRequestQueue) Swap(i, j int) { + tq[i], tq[j] = tq[j], tq[i] + tq[i].index = i + tq[j].index = j +} + +func (tq *topicRequestQueue) Push(x interface{}) { + n := len(*tq) + item := x.(*topicRequestQueueItem) + item.index = n + *tq = append(*tq, item) +} + +func (tq *topicRequestQueue) Pop() interface{} { + old := *tq + n := len(old) + item := old[n-1] + item.index = -1 + *tq = old[0 : n-1] + return item +} + +func (tq *topicRequestQueue) update(item *topicRequestQueueItem, priority uint64) { + item.priority = priority + heap.Fix(tq, item.index) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go new file mode 100644 index 000000000..46d3200bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go @@ -0,0 +1,460 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "crypto/ecdsa" + "errors" + "fmt" + "net" + "time" + + "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" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/rlp" +) + +const Version = 4 + +// Errors +var ( + errPacketTooSmall = errors.New("too small") + errBadHash = errors.New("bad hash") + errExpired = errors.New("expired") + errUnsolicitedReply = errors.New("unsolicited reply") + errUnknownNode = errors.New("unknown node") + errTimeout = errors.New("RPC timeout") + errClockWarp = errors.New("reply deadline too far in the future") + errClosed = errors.New("socket closed") +) + +// Timeouts +const ( + respTimeout = 500 * time.Millisecond + sendTimeout = 500 * time.Millisecond + expiration = 20 * time.Second + + ntpFailureThreshold = 32 // Continuous timeouts after which to check NTP + ntpWarningCooldown = 10 * time.Minute // Minimum amount of time to pass before repeating NTP warning + driftThreshold = 10 * time.Second // Allowed clock drift before warning user +) + +// RPC request structures +type ( + ping struct { + Version uint + From, To rpcEndpoint + Expiration uint64 + + // v5 + Topics []Topic + + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // pong is the reply to ping. + pong struct { + // This field should mirror the UDP envelope address + // of the ping packet, which provides a way to discover the + // the external address (after NAT). + To rpcEndpoint + + ReplyTok []byte // This contains the hash of the ping packet. + Expiration uint64 // Absolute timestamp at which the packet becomes invalid. + + // v5 + TopicHash common.Hash + TicketSerial uint32 + WaitPeriods []uint32 + + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // findnode is a query for nodes close to the given target. + findnode struct { + Target NodeID // doesn't need to be an actual public key + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // findnode is a query for nodes close to the given target. + findnodeHash struct { + Target common.Hash + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // reply to findnode + neighbors struct { + Nodes []rpcNode + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + topicRegister struct { + Topics []Topic + Idx uint + Pong []byte + } + + topicQuery struct { + Topic Topic + Expiration uint64 + } + + // reply to topicQuery + topicNodes struct { + Echo common.Hash + Nodes []rpcNode + } + + rpcNode struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP uint16 // for discovery protocol + TCP uint16 // for RLPx protocol + ID NodeID + } + + rpcEndpoint struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP uint16 // for discovery protocol + TCP uint16 // for RLPx protocol + } +) + +const ( + macSize = 256 / 8 + sigSize = 520 / 8 + headSize = macSize + sigSize // space of packet frame data +) + +// Neighbors replies are sent across multiple packets to +// stay below the 1280 byte limit. We compute the maximum number +// of entries by stuffing a packet until it grows too large. +var maxNeighbors = func() int { + p := neighbors{Expiration: ^uint64(0)} + maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} + for n := 0; ; n++ { + p.Nodes = append(p.Nodes, maxSizeNode) + size, _, err := rlp.EncodeToReader(p) + if err != nil { + // If this ever happens, it will be caught by the unit tests. + panic("cannot encode: " + err.Error()) + } + if headSize+size+1 >= 1280 { + return n + } + } +}() + +var maxTopicNodes = func() int { + p := topicNodes{} + maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} + for n := 0; ; n++ { + p.Nodes = append(p.Nodes, maxSizeNode) + size, _, err := rlp.EncodeToReader(p) + if err != nil { + // If this ever happens, it will be caught by the unit tests. + panic("cannot encode: " + err.Error()) + } + if headSize+size+1 >= 1280 { + return n + } + } +}() + +func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint { + ip := addr.IP.To4() + if ip == nil { + ip = addr.IP.To16() + } + return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} +} + +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 + n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP) + err := n.validateComplete() + return n, err +} + +func nodeToRPC(n *Node) rpcNode { + return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP} +} + +type ingressPacket struct { + remoteID NodeID + remoteAddr *net.UDPAddr + ev nodeEvent + hash []byte + data interface{} // one of the RPC structs + rawData []byte +} + +type conn interface { + ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) + WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) + Close() error + LocalAddr() net.Addr +} + +// udp implements the RPC protocol. +type udp struct { + conn conn + priv *ecdsa.PrivateKey + ourEndpoint rpcEndpoint + nat nat.Interface + net *Network +} + +// 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) { + transport, err := listenUDP(priv, laddr) + if err != nil { + return nil, err + } + net, err := newNetwork(transport, priv.PublicKey, natm, nodeDBPath) + if err != nil { + return nil, err + } + transport.net = net + go transport.readLoop() + return net, nil +} + +func listenUDP(priv *ecdsa.PrivateKey, laddr string) (*udp, error) { + addr, err := net.ResolveUDPAddr("udp", laddr) + if err != nil { + return nil, err + } + conn, err := net.ListenUDP("udp", addr) + if err != nil { + return nil, err + } + return &udp{conn: conn, priv: priv, ourEndpoint: makeEndpoint(addr, uint16(addr.Port))}, nil +} + +func (t *udp) localAddr() *net.UDPAddr { + return t.conn.LocalAddr().(*net.UDPAddr) +} + +func (t *udp) Close() { + t.conn.Close() +} + +func (t *udp) send(remote *Node, ptype nodeEvent, data interface{}) (hash []byte) { + hash, _ = t.sendPacket(remote.ID, remote.addr(), byte(ptype), data) + return hash +} + +func (t *udp) sendPing(remote *Node, toaddr *net.UDPAddr, topics []Topic) (hash []byte) { + hash, _ = t.sendPacket(remote.ID, toaddr, byte(pingPacket), ping{ + Version: Version, + From: t.ourEndpoint, + To: makeEndpoint(toaddr, uint16(toaddr.Port)), // TODO: maybe use known TCP port from DB + Expiration: uint64(time.Now().Add(expiration).Unix()), + Topics: topics, + }) + return hash +} + +func (t *udp) sendFindnode(remote *Node, target NodeID) { + t.sendPacket(remote.ID, remote.addr(), byte(findnodePacket), findnode{ + Target: target, + Expiration: uint64(time.Now().Add(expiration).Unix()), + }) +} + +func (t *udp) sendNeighbours(remote *Node, results []*Node) { + // Send neighbors in chunks with at most maxNeighbors per packet + // to stay below the 1280 byte limit. + p := neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} + for i, result := range results { + p.Nodes = append(p.Nodes, nodeToRPC(result)) + if len(p.Nodes) == maxNeighbors || i == len(results)-1 { + t.sendPacket(remote.ID, remote.addr(), byte(neighborsPacket), p) + p.Nodes = p.Nodes[:0] + } + } +} + +func (t *udp) sendFindnodeHash(remote *Node, target common.Hash) { + t.sendPacket(remote.ID, remote.addr(), byte(findnodeHashPacket), findnodeHash{ + Target: target, + Expiration: uint64(time.Now().Add(expiration).Unix()), + }) +} + +func (t *udp) sendTopicRegister(remote *Node, topics []Topic, idx int, pong []byte) { + t.sendPacket(remote.ID, remote.addr(), byte(topicRegisterPacket), topicRegister{ + Topics: topics, + Idx: uint(idx), + Pong: pong, + }) +} + +func (t *udp) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) { + p := topicNodes{Echo: queryHash} + if len(nodes) == 0 { + t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) + return + } + for i, result := range nodes { + 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) + p.Nodes = p.Nodes[:0] + } + } +} + +func (t *udp) sendPacket(toid NodeID, toaddr *net.UDPAddr, ptype byte, req interface{}) (hash []byte, err error) { + //fmt.Println("sendPacket", nodeEvent(ptype), toaddr.String(), toid.String()) + packet, hash, err := encodePacket(t.priv, ptype, req) + if err != nil { + //fmt.Println(err) + return hash, err + } + glog.V(logger.Detail).Infof(">>> %v to %x@%v\n", nodeEvent(ptype), toid[:8], toaddr) + if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil { + glog.V(logger.Detail).Infoln("UDP send failed:", err) + } + //fmt.Println(err) + return hash, err +} + +// zeroed padding space for encodePacket. +var headSpace = make([]byte, headSize) + +func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) (p, hash []byte, err error) { + b := new(bytes.Buffer) + b.Write(headSpace) + b.WriteByte(ptype) + if err := rlp.Encode(b, req); err != nil { + glog.V(logger.Error).Infoln("error encoding packet:", err) + return nil, nil, err + } + packet := b.Bytes() + sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv) + if err != nil { + glog.V(logger.Error).Infoln("could not sign packet:", err) + return nil, nil, err + } + copy(packet[macSize:], sig) + // add the hash to the front. Note: this doesn't protect the + // packet in any way. + hash = crypto.Keccak256(packet[macSize:]) + copy(packet, hash) + return packet, hash, nil +} + +// readLoop runs in its own goroutine. it injects ingress UDP packets +// into the network loop. +func (t *udp) readLoop() { + defer t.conn.Close() + // Discovery packets are defined to be no larger than 1280 bytes. + // Packets larger than this size will be cut at the end and treated + // as invalid because their hash won't match. + buf := make([]byte, 1280) + for { + nbytes, from, err := t.conn.ReadFromUDP(buf) + if isTemporaryError(err) { + // Ignore temporary read errors. + glog.V(logger.Debug).Infof("Temporary read error: %v", err) + continue + } else if err != nil { + // Shut down the loop for permament errors. + glog.V(logger.Debug).Infof("Read error: %v", err) + return + } + t.handlePacket(from, buf[:nbytes]) + } +} + +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 { + glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err) + //fmt.Println("bad packet", err) + return err + } + t.net.reqReadPacket(pkt) + return nil +} + +func decodePacket(buffer []byte, pkt *ingressPacket) error { + if len(buffer) < headSize+1 { + return errPacketTooSmall + } + buf := make([]byte, len(buffer)) + copy(buf, buffer) + hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] + shouldhash := crypto.Keccak256(buf[macSize:]) + if !bytes.Equal(hash, shouldhash) { + return errBadHash + } + fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig) + if err != nil { + return err + } + pkt.rawData = buf + pkt.hash = hash + pkt.remoteID = fromID + switch pkt.ev = nodeEvent(sigdata[0]); pkt.ev { + case pingPacket: + pkt.data = new(ping) + case pongPacket: + pkt.data = new(pong) + case findnodePacket: + pkt.data = new(findnode) + case neighborsPacket: + pkt.data = new(neighbors) + case findnodeHashPacket: + pkt.data = new(findnodeHash) + case topicRegisterPacket: + pkt.data = new(topicRegister) + case topicQueryPacket: + pkt.data = new(topicQuery) + case topicNodesPacket: + pkt.data = new(topicNodes) + default: + return fmt.Errorf("unknown packet type: %d", sigdata[0]) + } + s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) + err = s.Decode(pkt.data) + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_notwindows.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_notwindows.go new file mode 100644 index 000000000..4da18d0f6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_notwindows.go @@ -0,0 +1,26 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//+build !windows + +package 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 +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_windows.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_windows.go new file mode 100644 index 000000000..1ab9d655e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_windows.go @@ -0,0 +1,40 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//+build windows + +package discv5 + +import ( + "net" + "os" + "syscall" +) + +const _WSAEMSGSIZE = syscall.Errno(10040) + +// reports whether err indicates that a UDP packet didn't +// fit the receive buffer. On Windows, WSARecvFrom returns +// code WSAEMSGSIZE and no data if this happens. +func isPacketTooBig(err error) bool { + if opErr, ok := err.(*net.OpError); ok { + if scErr, ok := opErr.Err.(*os.SyscallError); ok { + return scErr.Err == _WSAEMSGSIZE + } + return opErr.Err == _WSAEMSGSIZE + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go index 505a1fc77..f9ba93613 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go @@ -105,7 +105,7 @@ func Map(m Interface, c chan struct{}, protocol string, extport, intport int, na glog.V(logger.Debug).Infof("deleting port mapping: %s %d -> %d (%s) using %s\n", protocol, extport, intport, name, m) m.DeleteMapping(protocol, extport, intport) }() - if err := m.AddMapping(protocol, intport, extport, name, mapTimeout); err != nil { + if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil { glog.V(logger.Debug).Infof("network port %s:%d could not be mapped: %v\n", protocol, intport, err) } else { glog.V(logger.Info).Infof("mapped network port %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) @@ -118,7 +118,7 @@ func Map(m Interface, c chan struct{}, protocol string, extport, intport int, na } case <-refresh.C: glog.V(logger.Detail).Infof("refresh port mapping %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) - if err := m.AddMapping(protocol, intport, extport, name, mapTimeout); err != nil { + if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil { glog.V(logger.Debug).Infof("network port %s:%d could not be mapped: %v\n", protocol, intport, err) } refresh.Reset(mapUpdateInterval) @@ -197,11 +197,7 @@ type autodisc struct { func startautodisc(what string, doit func() Interface) Interface { // TODO: monitor network configuration and rerun doit when it changes. - ad := &autodisc{what: what, doit: doit} - // Start the auto discovery as early as possible so it is already - // in progress when the rest of the stack calls the methods. - go ad.wait() - return ad + return &autodisc{what: what, doit: doit} } func (n *autodisc) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error { diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/server.go b/vendor/github.com/ethereum/go-ethereum/p2p/server.go index 8e3cd93f9..7381127dc 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/server.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/server.go @@ -28,6 +28,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/discv5" "github.com/ethereum/go-ethereum/p2p/nat" ) @@ -72,14 +73,26 @@ type Config struct { // or not. Disabling is usually useful for protocol debugging (manual topology). Discovery bool + // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery + // protocol should be started or not. + DiscoveryV5 bool + + // Listener address for the V5 discovery protocol UDP traffic. + DiscoveryV5Addr string + // Name sets the node name of this server. // Use common.MakeName to create a name that follows existing conventions. Name string - // Bootstrap nodes are used to establish connectivity + // BootstrapNodes are used to establish connectivity // with the rest of the network. BootstrapNodes []*discover.Node + // BootstrapNodesV5 are used to establish connectivity + // with the rest of the network using the V5 discovery + // protocol. + BootstrapNodesV5 []*discv5.Node + // Static nodes are used as pre-configured connections which are always // maintained and re-connected on disconnects. StaticNodes []*discover.Node @@ -135,6 +148,7 @@ type Server struct { listener net.Listener ourHandshake *protoHandshake lastLookup time.Time + DiscV5 *discv5.Network // These are for Peers, PeerCount (and nothing else). peerOp chan peerOpFunc @@ -352,6 +366,17 @@ func (srv *Server) Start() (err error) { srv.ntab = ntab } + if srv.DiscoveryV5 { + ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "") //srv.NodeDatabase) + if err != nil { + return err + } + if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil { + return err + } + srv.DiscV5 = ntab + } + dynPeers := (srv.MaxPeers + 1) / 2 if !srv.Discovery { dynPeers = 0 @@ -527,6 +552,9 @@ running: if srv.ntab != nil { srv.ntab.Close() } + if srv.DiscV5 != nil { + srv.DiscV5.Close() + } // Disconnect all peers. for _, p := range peers { p.Disconnect(DiscQuitting) @@ -728,7 +756,7 @@ type NodeInfo struct { Protocols map[string]interface{} `json:"protocols"` } -// Info gathers and returns a collection of metadata known about the host. +// NodeInfo gathers and returns a collection of metadata known about the host. func (srv *Server) NodeInfo() *NodeInfo { node := srv.Self() diff --git a/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go new file mode 100644 index 000000000..830b309d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go @@ -0,0 +1,52 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import ( + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" +) + +// MainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on +// the main Ethereum network. +var MainnetBootnodes = []*discover.Node{ + // ETH/DEV Go Bootnodes + discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE + discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR + discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG + + // ETH/DEV Cpp Bootnodes + discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"), +} + +// TestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Morden test network. +var TestnetBootnodes = []*discover.Node{ + // ETH/DEV Go Bootnodes + discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"), + discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"), + + // ETH/DEV Cpp Bootnodes +} + +// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the +// experimental RLPx v5 topic-discovery network. +var DiscoveryV5Bootnodes = []*discv5.Node{ + discv5.MustParseNode("enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305"), + discv5.MustParseNode("enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308"), + discv5.MustParseNode("enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309"), +} diff --git a/vendor/github.com/ethereum/go-ethereum/params/config.go b/vendor/github.com/ethereum/go-ethereum/params/config.go new file mode 100644 index 000000000..8c285781e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/config.go @@ -0,0 +1,150 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// MainnetChainConfig is the chain parameters to run a node on the main network. +var MainnetChainConfig = &ChainConfig{ + ChainId: MainNetChainID, + HomesteadBlock: MainNetHomesteadBlock, + DAOForkBlock: MainNetDAOForkBlock, + DAOForkSupport: true, + EIP150Block: MainNetHomesteadGasRepriceBlock, + EIP150Hash: MainNetHomesteadGasRepriceHash, + EIP155Block: MainNetSpuriousDragon, + EIP158Block: MainNetSpuriousDragon, +} + +// TestnetChainConfig is the chain parameters to run a node on the test network. +var TestnetChainConfig = &ChainConfig{ + ChainId: big.NewInt(3), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(10), +} + +// ChainConfig is the core config which determines the blockchain settings. +// +// ChainConfig is stored in the database on a per block basis. This means +// that any network, identified by its genesis block, can have its own +// set of configuration options. +type ChainConfig struct { + ChainId *big.Int `json:"chainId"` // Chain id identifies the current chain and is used for replay protection + + HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead) + DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) + DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork + + // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) + EIP150Block *big.Int `json:"eip150Block"` // EIP150 HF block (nil = no fork) + EIP150Hash common.Hash `json:"eip150Hash"` // EIP150 HF hash (fast sync aid) + + EIP155Block *big.Int `json:"eip155Block"` // EIP155 HF block + EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block +} + +// String implements the Stringer interface. +func (c *ChainConfig) String() string { + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}", + c.ChainId, + c.HomesteadBlock, + c.DAOForkBlock, + c.DAOForkSupport, + c.EIP150Block, + c.EIP155Block, + c.EIP158Block, + ) +} + +var ( + TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)} + TestRules = TestChainConfig.Rules(new(big.Int)) +) + +// IsHomestead returns whether num is either equal to the homestead block or greater. +func (c *ChainConfig) IsHomestead(num *big.Int) bool { + if c.HomesteadBlock == nil || num == nil { + return false + } + return num.Cmp(c.HomesteadBlock) >= 0 +} + +// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). +// +// The returned GasTable's fields shouldn't, under any circumstances, be changed. +func (c *ChainConfig) GasTable(num *big.Int) GasTable { + if num == nil { + return GasTableHomestead + } + + switch { + case c.EIP158Block != nil && num.Cmp(c.EIP158Block) >= 0: + return GasTableEIP158 + case c.EIP150Block != nil && num.Cmp(c.EIP150Block) >= 0: + return GasTableHomesteadGasRepriceFork + default: + return GasTableHomestead + } +} + +func (c *ChainConfig) IsEIP150(num *big.Int) bool { + if c.EIP150Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP150Block) >= 0 + +} + +func (c *ChainConfig) IsEIP155(num *big.Int) bool { + if c.EIP155Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP155Block) >= 0 + +} + +func (c *ChainConfig) IsEIP158(num *big.Int) bool { + if c.EIP158Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP158Block) >= 0 + +} + +// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions +// that do not have or require information about the block. +// +// Rules is a one time interface meaning that it shouldn't be used in between transition +// phases. +type Rules struct { + ChainId *big.Int + IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool +} + +func (c *ChainConfig) Rules(num *big.Int) Rules { + return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num)} +} diff --git a/vendor/github.com/ethereum/go-ethereum/params/gas_table.go b/vendor/github.com/ethereum/go-ethereum/params/gas_table.go new file mode 100644 index 000000000..093dacc8b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/gas_table.go @@ -0,0 +1,83 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import "math/big" + +type GasTable struct { + ExtcodeSize *big.Int + ExtcodeCopy *big.Int + Balance *big.Int + SLoad *big.Int + Calls *big.Int + Suicide *big.Int + + ExpByte *big.Int + + // CreateBySuicide occurs when the + // refunded account is one that does + // not exist. This logic is similar + // to call. May be left nil. Nil means + // not charged. + CreateBySuicide *big.Int +} + +var ( + // GasTableHomestead contain the gas prices for + // the homestead phase. + GasTableHomestead = GasTable{ + ExtcodeSize: big.NewInt(20), + ExtcodeCopy: big.NewInt(20), + Balance: big.NewInt(20), + SLoad: big.NewInt(50), + Calls: big.NewInt(40), + Suicide: big.NewInt(0), + ExpByte: big.NewInt(10), + + // explicitly set to nil to indicate + // this rule does not apply to homestead. + CreateBySuicide: nil, + } + + // GasTableHomestead contain the gas re-prices for + // the homestead phase. + // + // TODO rename to GasTableEIP150 + GasTableHomesteadGasRepriceFork = GasTable{ + ExtcodeSize: big.NewInt(700), + ExtcodeCopy: big.NewInt(700), + Balance: big.NewInt(400), + SLoad: big.NewInt(200), + Calls: big.NewInt(700), + Suicide: big.NewInt(5000), + ExpByte: big.NewInt(10), + + CreateBySuicide: big.NewInt(25000), + } + + GasTableEIP158 = GasTable{ + ExtcodeSize: big.NewInt(700), + ExtcodeCopy: big.NewInt(700), + Balance: big.NewInt(400), + SLoad: big.NewInt(200), + Calls: big.NewInt(700), + Suicide: big.NewInt(5000), + ExpByte: big.NewInt(50), + + CreateBySuicide: big.NewInt(25000), + } +) diff --git a/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go index 2dfc251b6..e98925c2b 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go +++ b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go @@ -14,9 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// DO NOT EDIT!!! -// AUTOGENERATED FROM generators/defaults.go - package params import "math/big" @@ -71,4 +68,6 @@ var ( SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation. MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + + MaxCodeSize = 24576 ) diff --git a/vendor/github.com/ethereum/go-ethereum/params/util.go b/vendor/github.com/ethereum/go-ethereum/params/util.go index 9d1fa54e6..546ebb35c 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/util.go +++ b/vendor/github.com/ethereum/go-ethereum/params/util.go @@ -16,9 +16,28 @@ package params -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) var ( - TestNetHomesteadBlock = big.NewInt(494000) // Testnet homestead block + TestNetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet genesis hash to enforce below configs on + MainNetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") // Mainnet genesis hash to enforce below configs on + + TestNetHomesteadBlock = big.NewInt(0) // Testnet homestead block MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block + + TestNetHomesteadGasRepriceBlock = big.NewInt(0) // Testnet gas reprice block + MainNetHomesteadGasRepriceBlock = big.NewInt(2463000) // Mainnet gas reprice block + + TestNetHomesteadGasRepriceHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet gas reprice block hash (used by fast sync) + MainNetHomesteadGasRepriceHash = common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0") // Mainnet gas reprice block hash (used by fast sync) + + TestNetSpuriousDragon = big.NewInt(10) + MainNetSpuriousDragon = big.NewInt(2675000) + + TestNetChainID = big.NewInt(3) // Test net default chain ID + MainNetChainID = big.NewInt(1) // main net default chain ID ) diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/http.go b/vendor/github.com/ethereum/go-ethereum/rpc/http.go index afcdd4bd6..7d4fe5d47 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/http.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/http.go @@ -170,6 +170,7 @@ func newCorsHandler(srv *Server, corsString string) http.Handler { c := cors.New(cors.Options{ AllowedOrigins: allowedOrigins, AllowedMethods: []string{"POST", "GET"}, + MaxAge: 600, }) return c.Handler(srv) } diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/server.go b/vendor/github.com/ethereum/go-ethereum/rpc/server.go index f7dddded7..996c63700 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/server.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/server.go @@ -337,13 +337,7 @@ func (s *Server) exec(ctx context.Context, codec ServerCodec, req *serverRequest if req.err != nil { response = codec.CreateErrorResponse(&req.id, req.err) } else { -/*fmt.Println() -fmt.Println("SREQ") -fmt.Println(*req)*/ response, callback = s.handle(ctx, codec, req) -/*fmt.Println("RESP") -fmt.Println(response) -fmt.Println()*/ } if err := codec.Write(response); err != nil { @@ -367,15 +361,9 @@ func (s *Server) execBatch(ctx context.Context, codec ServerCodec, requests []*s responses[i] = codec.CreateErrorResponse(&req.id, req.err) } else { var callback func() -/*fmt.Println() -fmt.Println("SREQ batch") -fmt.Println(*req)*/ if responses[i], callback = s.handle(ctx, codec, req); callback != nil { callbacks = append(callbacks, callback) } -/*fmt.Println("RESP") -fmt.Println(responses[i]) -fmt.Println()*/ } } @@ -403,10 +391,6 @@ func (s *Server) readRequest(codec ServerCodec) ([]*serverRequest, bool, Error) // verify requests for i, r := range reqs { -/*fmt.Println() -fmt.Println(time.Now()) -fmt.Println("REQ") -fmt.Println(r)*/ var ok bool var svc *service diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/types.go b/vendor/github.com/ethereum/go-ethereum/rpc/types.go index e313bcda0..ebe388373 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/types.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/types.go @@ -17,6 +17,8 @@ package rpc import ( + "bytes" + "encoding/hex" "fmt" "math" "math/big" @@ -273,30 +275,30 @@ func (bn BlockNumber) Int64() int64 { return (int64)(bn) } -type ClientRestartWrapper struct { - client *Client - newClientFn func() *Client - mu sync.RWMutex +// HexBytes JSON-encodes as hex with 0x prefix. +type HexBytes []byte + +func (b HexBytes) 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 } -func NewClientRestartWrapper(newClientFn func() *Client) *ClientRestartWrapper { - return &ClientRestartWrapper{ - client: newClientFn(), - newClientFn: newClientFn, +func (b *HexBytes) UnmarshalJSON(input []byte) error { + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] } -} - -func (rw *ClientRestartWrapper) Client() *Client { - rw.mu.RLock() - defer rw.mu.RUnlock() - - return rw.client -} - -func (rw *ClientRestartWrapper) Restart() { - rw.mu.Lock() - defer rw.mu.Unlock() - - rw.client.Close() - rw.client = rw.newClientFn() + if !bytes.HasPrefix(input, []byte("0x")) { + return fmt.Errorf("missing 0x prefix for hex byte array") + } + input = input[2:] + if len(input) == 0 { + *b = nil + return nil + } + *b = make([]byte, len(input)/2) + _, err := hex.Decode(*b, input) + return err } diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/utils.go b/vendor/github.com/ethereum/go-ethereum/rpc/utils.go index b590ba62f..c249e9b4a 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/utils.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/utils.go @@ -24,6 +24,7 @@ import ( "math/big" "math/rand" "reflect" + "strings" "sync" "time" "unicode" @@ -250,5 +251,13 @@ func NewID() ID { val >>= 8 } } - return ID("0x" + hex.EncodeToString(id)) + + rpcId := hex.EncodeToString(id) + // rpc ID's are RPC quantities, no leading zero's and 0 is 0x0 + rpcId = strings.TrimLeft(rpcId, "0") + if rpcId == "" { + rpcId = "0" + } + + return ID("0x" + rpcId) } diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go new file mode 100644 index 000000000..673cff350 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go @@ -0,0 +1,191 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "fmt" + "io" + "regexp" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +var ( + hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") + slashes = regexp.MustCompile("/+") + domainAndVersion = regexp.MustCompile("[@:;,]+") +) + +type Resolver interface { + Resolve(string) (common.Hash, error) +} + +/* +Api implements webserver/file system related content storage and retrieval +on top of the dpa +it is the public interface of the dpa which is included in the ethereum stack +*/ +type Api struct { + dpa *storage.DPA + dns Resolver +} + +//the api constructor initialises +func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) { + self = &Api{ + dpa: dpa, + dns: dns, + } + return +} + +// DPA reader API +func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader { + return self.dpa.Retrieve(key) +} + +func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) { + return self.dpa.Store(data, size, wg, nil) +} + +type ErrResolve error + +// DNS Resolver +func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error) { + if hashMatcher.MatchString(hostPort) || self.dns == nil { + glog.V(logger.Detail).Infof("host is a contentHash: '%v'", hostPort) + return storage.Key(common.Hex2Bytes(hostPort)), nil + } + if !nameresolver { + return nil, fmt.Errorf("'%s' is not a content hash value.", hostPort) + } + contentHash, err := self.dns.Resolve(hostPort) + if err != nil { + err = ErrResolve(err) + glog.V(logger.Warn).Infof("DNS error : %v", err) + } + glog.V(logger.Detail).Infof("host lookup: %v -> %v", err) + return contentHash[:], err +} + +func parse(uri string) (hostPort, path string) { + parts := slashes.Split(uri, 3) + var i int + if len(parts) == 0 { + return + } + // beginning with slash is now optional + for len(parts[i]) == 0 { + i++ + } + hostPort = parts[i] + for i < len(parts)-1 { + i++ + if len(path) > 0 { + path = path + "/" + parts[i] + } else { + path = parts[i] + } + } + glog.V(logger.Debug).Infof("host: '%s', path '%s' requested.", hostPort, path) + return +} + +func (self *Api) parseAndResolve(uri string, nameresolver bool) (key storage.Key, hostPort, path string, err error) { + hostPort, path = parse(uri) + //resolving host and port + contentHash, err := self.Resolve(hostPort, nameresolver) + glog.V(logger.Debug).Infof("Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path) + return contentHash[:], hostPort, path, err +} + +// Put provides singleton manifest creation on top of dpa store +func (self *Api) Put(content, contentType string) (string, error) { + r := strings.NewReader(content) + wg := &sync.WaitGroup{} + key, err := self.dpa.Store(r, int64(len(content)), wg, nil) + if err != nil { + return "", err + } + manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) + r = strings.NewReader(manifest) + key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil) + if err != nil { + return "", err + } + wg.Wait() + return key.String(), nil +} + +// Get uses iterative manifest retrieval and prefix matching +// to resolve path to content using dpa retrieve +// it returns a section reader, mimeType, status and an error +func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionReader, mimeType string, status int, err error) { + + key, _, path, err := self.parseAndResolve(uri, nameresolver) + quitC := make(chan bool) + trie, err := loadManifest(self.dpa, key, quitC) + if err != nil { + glog.V(logger.Warn).Infof("loadManifestTrie error: %v", err) + return + } + + glog.V(logger.Detail).Infof("getEntry(%s)", path) + entry, _ := trie.getEntry(path) + if entry != nil { + key = common.Hex2Bytes(entry.Hash) + status = entry.Status + mimeType = entry.ContentType + glog.V(logger.Detail).Infof("content lookup key: '%v' (%v)", key, mimeType) + reader = self.dpa.Retrieve(key) + } else { + err = fmt.Errorf("manifest entry for '%s' not found", path) + glog.V(logger.Warn).Infof("%v", err) + } + return +} + +func (self *Api) Modify(uri, contentHash, contentType string, nameresolver bool) (newRootHash string, err error) { + root, _, path, err := self.parseAndResolve(uri, nameresolver) + quitC := make(chan bool) + trie, err := loadManifest(self.dpa, root, quitC) + if err != nil { + return + } + + if contentHash != "" { + entry := &manifestTrieEntry{ + Path: path, + Hash: contentHash, + ContentType: contentType, + } + trie.addEntry(entry, quitC) + } else { + trie.deleteEntry(path, quitC) + } + + err = trie.recalcAndStore() + if err != nil { + return + } + return trie.hash.String(), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go new file mode 100644 index 000000000..c04a015ef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go @@ -0,0 +1,131 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "crypto/ecdsa" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + port = "8500" +) + +// by default ens root is north internal +var ( + toyNetEnsRoot = common.HexToAddress("0xd344889e0be3e9ef6c26b0f60ef66a32e83c1b69") +) + +// separate bzz directories +// allow several bzz nodes running in parallel +type Config struct { + // serialised/persisted fields + *storage.StoreParams + *storage.ChunkerParams + *network.HiveParams + Swap *swap.SwapParams + *network.SyncParams + Path string + Port string + PublicKey string + BzzKey string + EnsRoot common.Address +} + +// config is agnostic to where private key is coming from +// so managing accounts is outside swarm and left to wrappers +func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (self *Config, err error) { + address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address + dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes())) + err = os.MkdirAll(dirpath, os.ModePerm) + if err != nil { + return + } + confpath := filepath.Join(dirpath, "config.json") + var data []byte + pubkey := crypto.FromECDSAPub(&prvKey.PublicKey) + pubkeyhex := common.ToHex(pubkey) + keyhex := crypto.Sha3Hash(pubkey).Hex() + + self = &Config{ + SyncParams: network.NewSyncParams(dirpath), + HiveParams: network.NewHiveParams(dirpath), + ChunkerParams: storage.NewChunkerParams(), + StoreParams: storage.NewStoreParams(dirpath), + Port: port, + Path: dirpath, + Swap: swap.DefaultSwapParams(contract, prvKey), + PublicKey: pubkeyhex, + BzzKey: keyhex, + EnsRoot: toyNetEnsRoot, + } + data, err = ioutil.ReadFile(confpath) + if err != nil { + if !os.IsNotExist(err) { + return + } + // file does not exist + // write out config file + err = self.Save() + if err != nil { + err = fmt.Errorf("error writing config: %v", err) + } + return + } + // file exists, deserialise + err = json.Unmarshal(data, self) + if err != nil { + return nil, fmt.Errorf("unable to parse config: %v", err) + } + // check public key + if pubkeyhex != self.PublicKey { + return nil, fmt.Errorf("public key does not match the one in the config file %v != %v", pubkeyhex, self.PublicKey) + } + if keyhex != self.BzzKey { + return nil, fmt.Errorf("bzz key does not match the one in the config file %v != %v", keyhex, self.BzzKey) + } + self.Swap.SetKey(prvKey) + + if (self.EnsRoot == common.Address{}) { + self.EnsRoot = toyNetEnsRoot + } + + return +} + +func (self *Config) Save() error { + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + err = os.MkdirAll(self.Path, os.ModePerm) + if err != nil { + return err + } + confpath := filepath.Join(self.Path, "config.json") + return ioutil.WriteFile(confpath, data, os.ModePerm) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go new file mode 100644 index 000000000..428f3e3ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go @@ -0,0 +1,283 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "bufio" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const maxParallelFiles = 5 + +type FileSystem struct { + api *Api +} + +func NewFileSystem(api *Api) *FileSystem { + return &FileSystem{api} +} + +// Upload replicates a local directory as a manifest file and uploads it +// using dpa store +// TODO: localpath should point to a manifest +func (self *FileSystem) Upload(lpath, index string) (string, error) { + var list []*manifestTrieEntry + localpath, err := filepath.Abs(filepath.Clean(lpath)) + if err != nil { + return "", err + } + + f, err := os.Open(localpath) + if err != nil { + return "", err + } + stat, err := f.Stat() + if err != nil { + return "", err + } + + var start int + if stat.IsDir() { + start = len(localpath) + glog.V(logger.Debug).Infof("uploading '%s'", localpath) + err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { + if (err == nil) && !info.IsDir() { + //fmt.Printf("lp %s path %s\n", localpath, path) + if len(path) <= start { + return fmt.Errorf("Path is too short") + } + if path[:start] != localpath { + return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) + } + entry := &manifestTrieEntry{ + Path: filepath.ToSlash(path), + } + list = append(list, entry) + } + return err + }) + if err != nil { + return "", err + } + } else { + dir := filepath.Dir(localpath) + start = len(dir) + if len(localpath) <= start { + return "", fmt.Errorf("Path is too short") + } + if localpath[:start] != dir { + return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir) + } + entry := &manifestTrieEntry{ + Path: filepath.ToSlash(localpath), + } + list = append(list, entry) + } + + cnt := len(list) + errors := make([]error, cnt) + done := make(chan bool, maxParallelFiles) + dcnt := 0 + awg := &sync.WaitGroup{} + + for i, entry := range list { + if i >= dcnt+maxParallelFiles { + <-done + dcnt++ + } + awg.Add(1) + go func(i int, entry *manifestTrieEntry, done chan bool) { + f, err := os.Open(entry.Path) + if err == nil { + stat, _ := f.Stat() + var hash storage.Key + wg := &sync.WaitGroup{} + hash, err = self.api.dpa.Store(f, stat.Size(), wg, nil) + if hash != nil { + list[i].Hash = hash.String() + } + wg.Wait() + awg.Done() + if err == nil { + first512 := make([]byte, 512) + fread, _ := f.ReadAt(first512, 0) + if fread > 0 { + mimeType := http.DetectContentType(first512[:fread]) + if filepath.Ext(entry.Path) == ".css" { + mimeType = "text/css" + } + list[i].ContentType = mimeType + } + } + f.Close() + } + errors[i] = err + done <- true + }(i, entry, done) + } + for dcnt < cnt { + <-done + dcnt++ + } + + trie := &manifestTrie{ + dpa: self.api.dpa, + } + quitC := make(chan bool) + for i, entry := range list { + if errors[i] != nil { + return "", errors[i] + } + entry.Path = RegularSlashes(entry.Path[start:]) + if entry.Path == index { + ientry := &manifestTrieEntry{ + Path: "", + Hash: entry.Hash, + ContentType: entry.ContentType, + } + trie.addEntry(ientry, quitC) + } + trie.addEntry(entry, quitC) + } + + err2 := trie.recalcAndStore() + var hs string + if err2 == nil { + hs = trie.hash.String() + } + awg.Wait() + return hs, err2 +} + +// Download replicates the manifest path structure on the local filesystem +// under localpath +func (self *FileSystem) Download(bzzpath, localpath string) error { + lpath, err := filepath.Abs(filepath.Clean(localpath)) + if err != nil { + return err + } + err = os.MkdirAll(lpath, os.ModePerm) + if err != nil { + return err + } + + //resolving host and port + key, _, path, err := self.api.parseAndResolve(bzzpath, true) + if err != nil { + return err + } + + if len(path) > 0 { + path += "/" + } + + quitC := make(chan bool) + trie, err := loadManifest(self.api.dpa, key, quitC) + if err != nil { + glog.V(logger.Warn).Infof("fs.Download: loadManifestTrie error: %v", err) + return err + } + + type downloadListEntry struct { + key storage.Key + path string + } + + var list []*downloadListEntry + var mde error + + prevPath := lpath + err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) { + glog.V(logger.Detail).Infof("fs.Download: %#v", entry) + + key = common.Hex2Bytes(entry.Hash) + path := lpath + "/" + suffix + dir := filepath.Dir(path) + if dir != prevPath { + mde = os.MkdirAll(dir, os.ModePerm) + prevPath = dir + } + if (mde == nil) && (path != dir+"/") { + list = append(list, &downloadListEntry{key: key, path: path}) + } + }) + if err != nil { + return err + } + + wg := sync.WaitGroup{} + errC := make(chan error) + done := make(chan bool, maxParallelFiles) + for i, entry := range list { + select { + case done <- true: + wg.Add(1) + case <-quitC: + return fmt.Errorf("aborted") + } + go func(i int, entry *downloadListEntry) { + defer wg.Done() + f, err := os.Create(entry.path) // TODO: path separators + if err == nil { + + reader := self.api.dpa.Retrieve(entry.key) + writer := bufio.NewWriter(f) + size, err := reader.Size(quitC) + if err == nil { + _, err = io.CopyN(writer, reader, size) // TODO: handle errors + err2 := writer.Flush() + if err == nil { + err = err2 + } + err2 = f.Close() + if err == nil { + err = err2 + } + } + } + if err != nil { + select { + case errC <- err: + case <-quitC: + } + return + } + <-done + }(i, entry) + } + go func() { + wg.Wait() + close(errC) + }() + select { + case err = <-errC: + return err + case <-quitC: + return fmt.Errorf("aborted") + } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go new file mode 100644 index 000000000..a3a644b73 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go @@ -0,0 +1,69 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package http + +import ( + "fmt" + "net/http" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +http roundtripper to register for bzz url scheme +see https://github.com/ethereum/go-ethereum/issues/2040 +Usage: + +import ( + "github.com/ethereum/go-ethereum/common/httpclient" + "github.com/ethereum/go-ethereum/swarm/api/http" +) +client := httpclient.New() +// for (private) swarm proxy running locally +client.RegisterScheme("bzz", &http.RoundTripper{Port: port}) +client.RegisterScheme("bzzi", &http.RoundTripper{Port: port}) +client.RegisterScheme("bzzr", &http.RoundTripper{Port: port}) + +The port you give the Roundtripper is the port the swarm proxy is listening on. +If Host is left empty, localhost is assumed. + +Using a public gateway, the above few lines gives you the leanest +bzz-scheme aware read-only http client. You really only ever need this +if you need go-native swarm access to bzz addresses, e.g., +github.com/ethereum/go-ethereum/common/natspec + +*/ + +type RoundTripper struct { + Host string + Port string +} + +func (self *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) { + host := self.Host + if len(host) == 0 { + host = "localhost" + } + url := fmt.Sprintf("http://%s:%s/%s:/%s/%s", host, self.Port, req.Proto, req.URL.Host, req.URL.Path) + glog.V(logger.Info).Infof("roundtripper: proxying request '%s' to '%s'", req.RequestURI, url) + reqProxy, err := http.NewRequest(req.Method, url, req.Body) + if err != nil { + return nil, err + } + return http.DefaultClient.Do(reqProxy) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go new file mode 100644 index 000000000..9be60ef94 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go @@ -0,0 +1,290 @@ +// 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 . + +/* +A simple http server interface to Swarm +*/ +package http + +import ( + "bytes" + "io" + "net/http" + "regexp" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/api" +) + +const ( + rawType = "application/octet-stream" +) + +var ( + // accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw) + bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+") + trailingSlashes = regexp.MustCompile("/+$") + rootDocumentUri = regexp.MustCompile("^/+bzz[i]?:/+[^/]+$") + // forever = func() time.Time { return time.Unix(0, 0) } + forever = time.Now +) + +type sequentialReader struct { + reader io.Reader + pos int64 + ahead map[int64](chan bool) + lock sync.Mutex +} + +// browser API for registering bzz url scheme handlers: +// https://developer.mozilla.org/en/docs/Web-based_protocol_handlers +// electron (chromium) api for registering bzz url scheme handlers: +// https://github.com/atom/electron/blob/master/docs/api/protocol.md + +// starts up http server +func StartHttpServer(api *api.Api, port string) { + serveMux := http.NewServeMux() + serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + handler(w, r, api) + }) + go http.ListenAndServe(":"+port, serveMux) + glog.V(logger.Info).Infof("Swarm HTTP proxy started on localhost:%s", port) +} + +func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { + requestURL := r.URL + // This is wrong + // if requestURL.Host == "" { + // var err error + // requestURL, err = url.Parse(r.Referer() + requestURL.String()) + // if err != nil { + // http.Error(w, err.Error(), http.StatusBadRequest) + // return + // } + // } + glog.V(logger.Debug).Infof("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept")) + uri := requestURL.Path + var raw, nameresolver bool + var proto string + + // HTTP-based URL protocol handler + glog.V(logger.Debug).Infof("BZZ request URI: '%s'", uri) + + path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string { + proto = p + return "" + }) + + // protocol identification (ugly) + if proto == "" { + if glog.V(logger.Error) { + glog.Errorf( + "[BZZ] Swarm: Protocol error in request `%s`.", + uri, + ) + http.Error(w, "BZZ protocol error", http.StatusBadRequest) + return + } + } + if len(proto) > 4 { + raw = proto[1:5] == "bzzr" + nameresolver = proto[1:5] != "bzzi" + } + + glog.V(logger.Debug).Infof( + "[BZZ] Swarm: %s request over protocol %s '%s' received.", + r.Method, proto, path, + ) + + switch { + case r.Method == "POST" || r.Method == "PUT": + if r.Header.Get("content-length") == "" { + http.Error(w, "Missing Content-Length header in request.", http.StatusBadRequest) + return + } + key, err := a.Store(io.LimitReader(r.Body, r.ContentLength), r.ContentLength, nil) + if err == nil { + glog.V(logger.Debug).Infof("Content for %v stored", key.Log()) + } else { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if r.Method == "POST" { + if raw { + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(common.Bytes2Hex(key)))) + } else { + http.Error(w, "No POST to "+uri+" allowed.", http.StatusBadRequest) + return + } + } else { + // PUT + if raw { + http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest) + return + } else { + path = api.RegularSlashes(path) + mime := r.Header.Get("Content-Type") + // TODO proper root hash separation + glog.V(logger.Debug).Infof("Modify '%s' to store %v as '%s'.", path, key.Log(), mime) + newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver) + if err == nil { + glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey) + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) + } else { + http.Error(w, "PUT to "+path+"failed.", http.StatusBadRequest) + return + } + } + } + case r.Method == "DELETE": + if raw { + http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest) + return + } else { + path = api.RegularSlashes(path) + glog.V(logger.Debug).Infof("Delete '%s'.", path) + newKey, err := a.Modify(path, "", "", nameresolver) + if err == nil { + glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey) + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) + } else { + http.Error(w, "DELETE to "+path+"failed.", http.StatusBadRequest) + return + } + } + case r.Method == "GET" || r.Method == "HEAD": + path = trailingSlashes.ReplaceAllString(path, "") + if raw { + // resolving host + key, err := a.Resolve(path, nameresolver) + if err != nil { + glog.V(logger.Error).Infof("%v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // retrieving content + reader := a.Retrieve(key) + quitC := make(chan bool) + size, err := reader.Size(quitC) + glog.V(logger.Debug).Infof("Reading %d bytes.", size) + + // setting mime type + qv := requestURL.Query() + mimeType := qv.Get("content_type") + if mimeType == "" { + mimeType = rawType + } + + w.Header().Set("Content-Type", mimeType) + http.ServeContent(w, r, uri, forever(), reader) + glog.V(logger.Debug).Infof("Serve raw content '%s' (%d bytes) as '%s'", uri, size, mimeType) + + // retrieve path via manifest + } else { + glog.V(logger.Debug).Infof("Structured GET request '%s' received.", uri) + // add trailing slash, if missing + if rootDocumentUri.MatchString(uri) { + http.Redirect(w, r, path+"/", http.StatusFound) + return + } + reader, mimeType, status, err := a.Get(path, nameresolver) + if err != nil { + if _, ok := err.(api.ErrResolve); ok { + glog.V(logger.Debug).Infof("%v", err) + status = http.StatusBadRequest + } else { + glog.V(logger.Debug).Infof("error retrieving '%s': %v", uri, err) + status = http.StatusNotFound + } + http.Error(w, err.Error(), status) + return + } + // set mime type and status headers + w.Header().Set("Content-Type", mimeType) + if status > 0 { + w.WriteHeader(status) + } else { + status = 200 + } + quitC := make(chan bool) + size, err := reader.Size(quitC) + glog.V(logger.Debug).Infof("Served '%s' (%d bytes) as '%s' (status code: %v)", uri, size, mimeType, status) + + http.ServeContent(w, r, path, forever(), reader) + + } + default: + http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed) + } +} + +func (self *sequentialReader) ReadAt(target []byte, off int64) (n int, err error) { + self.lock.Lock() + // assert self.pos <= off + if self.pos > off { + glog.V(logger.Error).Infof("non-sequential read attempted from sequentialReader; %d > %d", + self.pos, off) + panic("Non-sequential read attempt") + } + if self.pos != off { + glog.V(logger.Debug).Infof("deferred read in POST at position %d, offset %d.", + self.pos, off) + wait := make(chan bool) + self.ahead[off] = wait + self.lock.Unlock() + if <-wait { + // failed read behind + n = 0 + err = io.ErrUnexpectedEOF + return + } + self.lock.Lock() + } + localPos := 0 + for localPos < len(target) { + n, err = self.reader.Read(target[localPos:]) + localPos += n + glog.V(logger.Debug).Infof("Read %d bytes into buffer size %d from POST, error %v.", + n, len(target), err) + if err != nil { + glog.V(logger.Debug).Infof("POST stream's reading terminated with %v.", err) + for i := range self.ahead { + self.ahead[i] <- true + delete(self.ahead, i) + } + self.lock.Unlock() + return localPos, err + } + self.pos += int64(n) + } + wait := self.ahead[self.pos] + if wait != nil { + glog.V(logger.Debug).Infof("deferred read in POST at position %d triggered.", + self.pos) + delete(self.ahead, self.pos) + close(wait) + } + self.lock.Unlock() + return localPos, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go new file mode 100644 index 000000000..a289c01f9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go @@ -0,0 +1,336 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "bytes" + "encoding/json" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + manifestType = "application/bzz-manifest+json" +) + +type manifestTrie struct { + dpa *storage.DPA + entries [257]*manifestTrieEntry // indexed by first character of path, entries[256] is the empty path entry + hash storage.Key // if hash != nil, it is stored +} + +type manifestJSON struct { + Entries []*manifestTrieEntry `json:"entries"` +} + +type manifestTrieEntry struct { + Path string `json:"path"` + Hash string `json:"hash"` // for manifest content type, empty until subtrie is evaluated + ContentType string `json:"contentType"` + Status int `json:"status"` + subtrie *manifestTrie +} + +func loadManifest(dpa *storage.DPA, hash storage.Key, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand + + glog.V(logger.Detail).Infof("manifest lookup key: '%v'.", hash.Log()) + // retrieve manifest via DPA + manifestReader := dpa.Retrieve(hash) + return readManifest(manifestReader, hash, dpa, quitC) +} + +func readManifest(manifestReader storage.LazySectionReader, hash storage.Key, dpa *storage.DPA, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand + + // TODO check size for oversized manifests + size, err := manifestReader.Size(quitC) + manifestData := make([]byte, size) + read, err := manifestReader.Read(manifestData) + if int64(read) < size { + glog.V(logger.Detail).Infof("Manifest %v not found.", hash.Log()) + if err == nil { + err = fmt.Errorf("Manifest retrieval cut short: read %v, expect %v", read, size) + } + return + } + + glog.V(logger.Detail).Infof("Manifest %v retrieved", hash.Log()) + man := manifestJSON{} + err = json.Unmarshal(manifestData, &man) + if err != nil { + err = fmt.Errorf("Manifest %v is malformed: %v", hash.Log(), err) + glog.V(logger.Detail).Infof("%v", err) + return + } + + glog.V(logger.Detail).Infof("Manifest %v has %d entries.", hash.Log(), len(man.Entries)) + + trie = &manifestTrie{ + dpa: dpa, + } + for _, entry := range man.Entries { + trie.addEntry(entry, quitC) + } + return +} + +func (self *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) { + self.hash = nil // trie modified, hash needs to be re-calculated on demand + + if len(entry.Path) == 0 { + self.entries[256] = entry + return + } + + b := byte(entry.Path[0]) + if (self.entries[b] == nil) || (self.entries[b].Path == entry.Path) { + self.entries[b] = entry + return + } + + oldentry := self.entries[b] + cpl := 0 + for (len(entry.Path) > cpl) && (len(oldentry.Path) > cpl) && (entry.Path[cpl] == oldentry.Path[cpl]) { + cpl++ + } + + if (oldentry.ContentType == manifestType) && (cpl == len(oldentry.Path)) { + if self.loadSubTrie(oldentry, quitC) != nil { + return + } + entry.Path = entry.Path[cpl:] + oldentry.subtrie.addEntry(entry, quitC) + oldentry.Hash = "" + return + } + + commonPrefix := entry.Path[:cpl] + + subtrie := &manifestTrie{ + dpa: self.dpa, + } + entry.Path = entry.Path[cpl:] + oldentry.Path = oldentry.Path[cpl:] + subtrie.addEntry(entry, quitC) + subtrie.addEntry(oldentry, quitC) + + self.entries[b] = &manifestTrieEntry{ + Path: commonPrefix, + Hash: "", + ContentType: manifestType, + subtrie: subtrie, + } +} + +func (self *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) { + for _, e := range self.entries { + if e != nil { + cnt++ + entry = e + } + } + return +} + +func (self *manifestTrie) deleteEntry(path string, quitC chan bool) { + self.hash = nil // trie modified, hash needs to be re-calculated on demand + + if len(path) == 0 { + self.entries[256] = nil + return + } + + b := byte(path[0]) + entry := self.entries[b] + if entry == nil { + return + } + if entry.Path == path { + self.entries[b] = nil + return + } + + epl := len(entry.Path) + if (entry.ContentType == manifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { + if self.loadSubTrie(entry, quitC) != nil { + return + } + entry.subtrie.deleteEntry(path[epl:], quitC) + entry.Hash = "" + // remove subtree if it has less than 2 elements + cnt, lastentry := entry.subtrie.getCountLast() + if cnt < 2 { + if lastentry != nil { + lastentry.Path = entry.Path + lastentry.Path + } + self.entries[b] = lastentry + } + } +} + +func (self *manifestTrie) recalcAndStore() error { + if self.hash != nil { + return nil + } + + var buffer bytes.Buffer + buffer.WriteString(`{"entries":[`) + + list := &manifestJSON{} + for _, entry := range self.entries { + if entry != nil { + if entry.Hash == "" { // TODO: paralellize + err := entry.subtrie.recalcAndStore() + if err != nil { + return err + } + entry.Hash = entry.subtrie.hash.String() + } + list.Entries = append(list.Entries, entry) + } + } + + manifest, err := json.Marshal(list) + if err != nil { + return err + } + + sr := bytes.NewReader(manifest) + wg := &sync.WaitGroup{} + key, err2 := self.dpa.Store(sr, int64(len(manifest)), wg, nil) + wg.Wait() + self.hash = key + return err2 +} + +func (self *manifestTrie) loadSubTrie(entry *manifestTrieEntry, quitC chan bool) (err error) { + if entry.subtrie == nil { + hash := common.Hex2Bytes(entry.Hash) + entry.subtrie, err = loadManifest(self.dpa, hash, quitC) + entry.Hash = "" // might not match, should be recalculated + } + return +} + +func (self *manifestTrie) listWithPrefixInt(prefix, rp string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) error { + plen := len(prefix) + var start, stop int + if plen == 0 { + start = 0 + stop = 256 + } else { + start = int(prefix[0]) + stop = start + } + + for i := start; i <= stop; i++ { + select { + case <-quitC: + return fmt.Errorf("aborted") + default: + } + entry := self.entries[i] + if entry != nil { + epl := len(entry.Path) + if entry.ContentType == manifestType { + l := plen + if epl < l { + l = epl + } + if prefix[:l] == entry.Path[:l] { + err := self.loadSubTrie(entry, quitC) + if err != nil { + return err + } + err = entry.subtrie.listWithPrefixInt(prefix[l:], rp+entry.Path[l:], quitC, cb) + if err != nil { + return err + } + } + } else { + if (epl >= plen) && (prefix == entry.Path[:plen]) { + cb(entry, rp+entry.Path[plen:]) + } + } + } + } + return nil +} + +func (self *manifestTrie) listWithPrefix(prefix string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) (err error) { + return self.listWithPrefixInt(prefix, "", quitC, cb) +} + +func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *manifestTrieEntry, pos int) { + + glog.V(logger.Detail).Infof("findPrefixOf(%s)", path) + + if len(path) == 0 { + return self.entries[256], 0 + } + + b := byte(path[0]) + entry = self.entries[b] + if entry == nil { + return self.entries[256], 0 + } + epl := len(entry.Path) + glog.V(logger.Detail).Infof("path = %v entry.Path = %v epl = %v", path, entry.Path, epl) + if (len(path) >= epl) && (path[:epl] == entry.Path) { + glog.V(logger.Detail).Infof("entry.ContentType = %v", entry.ContentType) + if entry.ContentType == manifestType { + if self.loadSubTrie(entry, quitC) != nil { + return nil, 0 + } + entry, pos = entry.subtrie.findPrefixOf(path[epl:], quitC) + if entry != nil { + pos += epl + } + } else { + pos = epl + } + } else { + entry = nil + } + return +} + +// file system manifest always contains regularized paths +// no leading or trailing slashes, only single slashes inside +func RegularSlashes(path string) (res string) { + for i := 0; i < len(path); i++ { + if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) { + res = res + path[i:i+1] + } + } + if (len(res) > 0) && (res[len(res)-1] == '/') { + res = res[:len(res)-1] + } + return +} + +func (self *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) { + path := RegularSlashes(spath) + var pos int + quitC := make(chan bool) + entry, pos = self.findPrefixOf(path, quitC) + return entry, path[:pos] +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go new file mode 100644 index 000000000..31b484675 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go @@ -0,0 +1,70 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +type Response struct { + MimeType string + Status int + Size int64 + // Content []byte + Content string +} + +// implements a service +type Storage struct { + api *Api +} + +func NewStorage(api *Api) *Storage { + return &Storage{api} +} + +// Put uploads the content to the swarm with a simple manifest speficying +// its content type +func (self *Storage) Put(content, contentType string) (string, error) { + return self.api.Put(content, contentType) +} + +// Get retrieves the content from bzzpath and reads the response in full +// It returns the Response object, which serialises containing the +// response body as the value of the Content field +// NOTE: if error is non-nil, sResponse may still have partial content +// the actual size of which is given in len(resp.Content), while the expected +// size is resp.Size +func (self *Storage) Get(bzzpath string) (*Response, error) { + reader, mimeType, status, err := self.api.Get(bzzpath, true) + if err != nil { + return nil, err + } + quitC := make(chan bool) + expsize, err := reader.Size(quitC) + if err != nil { + return nil, err + } + body := make([]byte, expsize) + size, err := reader.Read(body) + if int64(size) == expsize { + err = nil + } + return &Response{mimeType, status, expsize, string(body[:size])}, err +} + +// Modify(rootHash, path, contentHash, contentType) takes th e manifest trie rooted in rootHash, +// and merge on to it. creating an entry w conentType (mime) +func (self *Storage) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) { + return self.api.Modify(rootHash+"/"+path, contentHash, contentType, true) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go new file mode 100644 index 000000000..6631196c1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go @@ -0,0 +1,46 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "github.com/ethereum/go-ethereum/swarm/network" +) + +type Control struct { + api *Api + hive *network.Hive +} + +func NewControl(api *Api, hive *network.Hive) *Control { + return &Control{api, hive} +} + +func (self *Control) BlockNetworkRead(on bool) { + self.hive.BlockNetworkRead(on) +} + +func (self *Control) SyncEnabled(on bool) { + self.hive.SyncEnabled(on) +} + +func (self *Control) SwapEnabled(on bool) { + self.hive.SwapEnabled(on) +} + +func (self *Control) Hive() string { + return self.hive.String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go new file mode 100644 index 000000000..79987cc6b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go @@ -0,0 +1,211 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "bytes" + "encoding/binary" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// Handler for storage/retrieval related protocol requests +// implements the StorageHandler interface used by the bzz protocol +type Depo struct { + hashfunc storage.Hasher + localStore storage.ChunkStore + netStore storage.ChunkStore +} + +func NewDepo(hash storage.Hasher, localStore, remoteStore storage.ChunkStore) *Depo { + return &Depo{ + hashfunc: hash, + localStore: localStore, + netStore: remoteStore, // entrypoint internal + } +} + +// Handles UnsyncedKeysMsg after msg decoding - unsynced hashes upto sync state +// * the remote sync state is just stored and handled in protocol +// * filters through the new syncRequests and send the ones missing +// * back immediately as a deliveryRequest message +// * empty message just pings back for more (is this needed?) +// * strict signed sync states may be needed. +func (self *Depo) HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error { + unsynced := req.Unsynced + var missing []*syncRequest + var chunk *storage.Chunk + var err error + for _, req := range unsynced { + // skip keys that are found, + chunk, err = self.localStore.Get(storage.Key(req.Key[:])) + if err != nil || chunk.SData == nil { + missing = append(missing, req) + } + } + glog.V(logger.Debug).Infof("Depo.HandleUnsyncedKeysMsg: received %v unsynced keys: %v missing. new state: %v", len(unsynced), len(missing), req.State) + glog.V(logger.Detail).Infof("Depo.HandleUnsyncedKeysMsg: received %v", unsynced) + // send delivery request with missing keys + err = p.deliveryRequest(missing) + if err != nil { + return err + } + // set peers state to persist + p.syncState = req.State + return nil +} + +// Handles deliveryRequestMsg +// * serves actual chunks asked by the remote peer +// by pushing to the delivery queue (sync db) of the correct priority +// (remote peer is free to reprioritize) +// * the message implies remote peer wants more, so trigger for +// * new outgoing unsynced keys message is fired +func (self *Depo) HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer) error { + deliver := req.Deliver + // queue the actual delivery of a chunk () + glog.V(logger.Detail).Infof("Depo.HandleDeliveryRequestMsg: received %v delivery requests: %v", len(deliver), deliver) + for _, sreq := range deliver { + // TODO: look up in cache here or in deliveries + // priorities are taken from the message so the remote party can + // reprioritise to at their leisure + // r = self.pullCached(sreq.Key) // pulls and deletes from cache + Push(p, sreq.Key, sreq.Priority) + } + + // sends it out as unsyncedKeysMsg + p.syncer.sendUnsyncedKeys() + return nil +} + +// the entrypoint for store requests coming from the bzz wire protocol +// if key found locally, return. otherwise +// remote is untrusted, so hash is verified and chunk passed on to NetStore +func (self *Depo) HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) { + req.from = p + chunk, err := self.localStore.Get(req.Key) + switch { + case err != nil: + glog.V(logger.Detail).Infof("Depo.handleStoreRequest: %v not found locally. create new chunk/request", req.Key) + // not found in memory cache, ie., a genuine store request + // create chunk + chunk = storage.NewChunk(req.Key, nil) + + case chunk.SData == nil: + // found chunk in memory store, needs the data, validate now + hasher := self.hashfunc() + hasher.Write(req.SData) + if !bytes.Equal(hasher.Sum(nil), req.Key) { + // data does not validate, ignore + // TODO: peer should be penalised/dropped? + glog.V(logger.Warn).Infof("Depo.HandleStoreRequest: chunk invalid. store request ignored: %v", req) + return + } + glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v. request entry found", req) + + default: + // data is found, store request ignored + // this should update access count? + glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v found locally. ignore.", req) + return + } + + // update chunk with size and data + chunk.SData = req.SData // protocol validates that SData is minimum 9 bytes long (int64 size + at least one byte of data) + chunk.Size = int64(binary.LittleEndian.Uint64(req.SData[0:8])) + glog.V(logger.Detail).Infof("delivery of %p from %v", chunk, p) + chunk.Source = p + self.netStore.Put(chunk) +} + +// entrypoint for retrieve requests coming from the bzz wire protocol +// checks swap balance - return if peer has no credit +func (self *Depo) HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer) { + req.from = p + // swap - record credit for 1 request + // note that only charge actual reqsearches + var err error + if p.swap != nil { + err = p.swap.Add(1) + } + if err != nil { + glog.V(logger.Warn).Infof("Depo.HandleRetrieveRequest: %v - cannot process request: %v", req.Key.Log(), err) + return + } + + // call storage.NetStore#Get which + // blocks until local retrieval finished + // launches cloud retrieval + chunk, _ := self.netStore.Get(req.Key) + req = self.strategyUpdateRequest(chunk.Req, req) + // check if we can immediately deliver + if chunk.SData != nil { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content found, delivering...", req.Key.Log()) + + if req.MaxSize == 0 || int64(req.MaxSize) >= chunk.Size { + sreq := &storeRequestMsgData{ + Id: req.Id, + Key: chunk.Key, + SData: chunk.SData, + requestTimeout: req.timeout, // + } + p.syncer.addRequest(sreq, DeliverReq) + } else { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content found, not wanted", req.Key.Log()) + } + } else { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content not found locally. asked swarm for help. will get back", req.Key.Log()) + } +} + +// add peer request the chunk and decides the timeout for the response if still searching +func (self *Depo) strategyUpdateRequest(rs *storage.RequestStatus, origReq *retrieveRequestMsgData) (req *retrieveRequestMsgData) { + glog.V(logger.Detail).Infof("Depo.strategyUpdateRequest: key %v", origReq.Key.Log()) + // we do not create an alternative one + req = origReq + if rs != nil { + self.addRequester(rs, req) + req.setTimeout(self.searchTimeout(rs, req)) + } + return +} + +// decides the timeout promise sent with the immediate peers response to a retrieve request +// if timeout is explicitly set and expired +func (self *Depo) searchTimeout(rs *storage.RequestStatus, req *retrieveRequestMsgData) (timeout *time.Time) { + reqt := req.getTimeout() + t := time.Now().Add(searchTimeout) + if reqt != nil && reqt.Before(t) { + return reqt + } else { + return &t + } +} + +/* +adds a new peer to an existing open request +only add if less than requesterCount peers forwarded the same request id so far +note this is done irrespective of status (searching or found) +*/ +func (self *Depo) addRequester(rs *storage.RequestStatus, req *retrieveRequestMsgData) { + glog.V(logger.Detail).Infof("Depo.addRequester: key %v - add peer to req.Id %v", req.Key.Log(), req.from, req.Id) + list := rs.Requesters[req.Id] + rs.Requesters[req.Id] = append(list, req) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go new file mode 100644 index 000000000..fef79c70b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go @@ -0,0 +1,150 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const requesterCount = 3 + +/* +forwarder implements the CloudStore interface (use by storage.NetStore) +and serves as the cloud store backend orchestrating storage/retrieval/delivery +via the native bzz protocol +which uses an MSB logarithmic distance-based semi-permanent Kademlia table for +* recursive forwarding style routing for retrieval +* smart syncronisation +*/ + +type forwarder struct { + hive *Hive +} + +func NewForwarder(hive *Hive) *forwarder { + return &forwarder{hive: hive} +} + +// generate a unique id uint64 +func generateId() uint64 { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + return uint64(r.Int63()) +} + +var searchTimeout = 3 * time.Second + +// forwarding logic +// logic propagating retrieve requests to peers given by the kademlia hive +func (self *forwarder) Retrieve(chunk *storage.Chunk) { + peers := self.hive.getPeers(chunk.Key, 0) + glog.V(logger.Detail).Infof("forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers)) +OUT: + for _, p := range peers { + glog.V(logger.Detail).Infof("forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p) + for _, recipients := range chunk.Req.Requesters { + for _, recipient := range recipients { + req := recipient.(*retrieveRequestMsgData) + if req.from.Addr() == p.Addr() { + continue OUT + } + } + } + req := &retrieveRequestMsgData{ + Key: chunk.Key, + Id: generateId(), + } + var err error + if p.swap != nil { + err = p.swap.Add(-1) + } + if err == nil { + p.retrieve(req) + break OUT + } + glog.V(logger.Warn).Infof("forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err) + } +} + +// requests to specific peers given by the kademlia hive +// except for peers that the store request came from (if any) +// delivery queueing taken care of by syncer +func (self *forwarder) Store(chunk *storage.Chunk) { + var n int + msg := &storeRequestMsgData{ + Key: chunk.Key, + SData: chunk.SData, + } + var source *peer + if chunk.Source != nil { + source = chunk.Source.(*peer) + } + for _, p := range self.hive.getPeers(chunk.Key, 0) { + glog.V(logger.Detail).Infof("forwarder.Store: %v %v", p, chunk) + + if p.syncer != nil && (source == nil || p.Addr() != source.Addr()) { + n++ + Deliver(p, msg, PropagateReq) + } + } + glog.V(logger.Detail).Infof("forwarder.Store: sent to %v peers (chunk = %v)", n, chunk) +} + +// once a chunk is found deliver it to its requesters unless timed out +func (self *forwarder) Deliver(chunk *storage.Chunk) { + // iterate over request entries + for id, requesters := range chunk.Req.Requesters { + counter := requesterCount + msg := &storeRequestMsgData{ + Key: chunk.Key, + SData: chunk.SData, + } + var n int + var req *retrieveRequestMsgData + // iterate over requesters with the same id + for id, r := range requesters { + req = r.(*retrieveRequestMsgData) + if req.timeout == nil || req.timeout.After(time.Now()) { + glog.V(logger.Detail).Infof("forwarder.Deliver: %v -> %v", req.Id, req.from) + msg.Id = uint64(id) + Deliver(req.from, msg, DeliverReq) + n++ + counter-- + if counter <= 0 { + break + } + } + } + glog.V(logger.Detail).Infof("forwarder.Deliver: submit chunk %v (request id %v) for delivery to %v peers", chunk.Key.Log(), id, n) + } +} + +// initiate delivery of a chunk to a particular peer via syncer#addRequest +// depending on syncer mode and priority settings and sync request type +// this either goes via confirmation roundtrip or queued or pushed directly +func Deliver(p *peer, req interface{}, ty int) { + p.syncer.addRequest(req, ty) +} + +// push chunk over to peer +func Push(p *peer, key storage.Key, priority uint) { + p.syncer.doDelivery(key, priority, p.syncer.quit) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go new file mode 100644 index 000000000..f5ebdd008 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go @@ -0,0 +1,383 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "fmt" + "math/rand" + "path/filepath" + "time" + + "github.com/ethereum/go-ethereum/common" + "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/swarm/network/kademlia" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// Hive is the logistic manager of the swarm +// it uses a generic kademlia nodetable to find best peer list +// for any target +// this is used by the netstore to search for content in the swarm +// the bzz protocol peersMsgData exchange is relayed to Kademlia +// for db storage and filtering +// connections and disconnections are reported and relayed +// to keep the nodetable uptodate + +type Hive struct { + listenAddr func() string + callInterval uint64 + id discover.NodeID + addr kademlia.Address + kad *kademlia.Kademlia + path string + quit chan bool + toggle chan bool + more chan bool + + // for testing only + swapEnabled bool + syncEnabled bool + blockRead bool + blockWrite bool +} + +const ( + callInterval = 3000000000 + // bucketSize = 3 + // maxProx = 8 + // proxBinSize = 4 +) + +type HiveParams struct { + CallInterval uint64 + KadDbPath string + *kademlia.KadParams +} + +func NewHiveParams(path string) *HiveParams { + kad := kademlia.NewKadParams() + // kad.BucketSize = bucketSize + // kad.MaxProx = maxProx + // kad.ProxBinSize = proxBinSize + + return &HiveParams{ + CallInterval: callInterval, + KadDbPath: filepath.Join(path, "bzz-peers.json"), + KadParams: kad, + } +} + +func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive { + kad := kademlia.New(kademlia.Address(addr), params.KadParams) + return &Hive{ + callInterval: params.CallInterval, + kad: kad, + addr: kad.Addr(), + path: params.KadDbPath, + swapEnabled: swapEnabled, + syncEnabled: syncEnabled, + } +} + +func (self *Hive) SyncEnabled(on bool) { + self.syncEnabled = on +} + +func (self *Hive) SwapEnabled(on bool) { + self.swapEnabled = on +} + +func (self *Hive) BlockNetworkRead(on bool) { + self.blockRead = on +} + +func (self *Hive) BlockNetworkWrite(on bool) { + self.blockWrite = on +} + +// public accessor to the hive base address +func (self *Hive) Addr() kademlia.Address { + return self.addr +} + +// Start receives network info only at startup +// listedAddr is a function to retrieve listening address to advertise to peers +// connectPeer is a function to connect to a peer based on its NodeID or enode URL +// there are called on the p2p.Server which runs on the node +func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPeer func(string) error) (err error) { + self.toggle = make(chan bool) + self.more = make(chan bool) + self.quit = make(chan bool) + self.id = id + self.listenAddr = listenAddr + err = self.kad.Load(self.path, nil) + if err != nil { + glog.V(logger.Warn).Infof("Warning: error reading kaddb '%s' (skipping): %v", self.path, err) + err = nil + } + // this loop is doing bootstrapping and maintains a healthy table + go self.keepAlive() + go func() { + // whenever toggled ask kademlia about most preferred peer + for alive := range self.more { + if !alive { + // receiving false closes the loop while allowing parallel routines + // to attempt to write to more (remove Peer when shutting down) + return + } + node, need, proxLimit := self.kad.Suggest() + + if node != nil && len(node.Url) > 0 { + glog.V(logger.Detail).Infof("call known bee %v", node.Url) + // enode or any lower level connection address is unnecessary in future + // discovery table is used to look it up. + connectPeer(node.Url) + } + if need { + // a random peer is taken from the table + peers := self.kad.FindClosest(kademlia.RandomAddressAt(self.addr, rand.Intn(self.kad.MaxProx)), 1) + if len(peers) > 0 { + // a random address at prox bin 0 is sent for lookup + randAddr := kademlia.RandomAddressAt(self.addr, proxLimit) + req := &retrieveRequestMsgData{ + Key: storage.Key(randAddr[:]), + } + glog.V(logger.Detail).Infof("call any bee near %v (PO%03d) - messenger bee: %v", randAddr, proxLimit, peers[0]) + peers[0].(*peer).retrieve(req) + } else { + glog.V(logger.Warn).Infof("no peer") + } + glog.V(logger.Detail).Infof("buzz kept alive") + } else { + glog.V(logger.Info).Infof("no need for more bees") + } + select { + case self.toggle <- need: + case <-self.quit: + return + } + glog.V(logger.Debug).Infof("queen's address: %v, population: %d (%d)", self.addr, self.kad.Count(), self.kad.DBCount()) + } + }() + return +} + +// keepAlive is a forever loop +// in its awake state it periodically triggers connection attempts +// by writing to self.more until Kademlia Table is saturated +// wake state is toggled by writing to self.toggle +// it restarts if the table becomes non-full again due to disconnections +func (self *Hive) keepAlive() { + alarm := time.NewTicker(time.Duration(self.callInterval)).C + for { + select { + case <-alarm: + if self.kad.DBCount() > 0 { + select { + case self.more <- true: + glog.V(logger.Debug).Infof("buzz wakeup") + default: + } + } + case need := <-self.toggle: + if alarm == nil && need { + alarm = time.NewTicker(time.Duration(self.callInterval)).C + } + if alarm != nil && !need { + alarm = nil + + } + case <-self.quit: + return + } + } +} + +func (self *Hive) Stop() error { + // closing toggle channel quits the updateloop + close(self.quit) + return self.kad.Save(self.path, saveSync) +} + +// called at the end of a successful protocol handshake +func (self *Hive) addPeer(p *peer) error { + defer func() { + select { + case self.more <- true: + default: + } + }() + glog.V(logger.Detail).Infof("hi new bee %v", p) + err := self.kad.On(p, loadSync) + if err != nil { + return err + } + // self lookup (can be encoded as nil/zero key since peers addr known) + no id () + // the most common way of saying hi in bzz is initiation of gossip + // let me know about anyone new from my hood , here is the storageradius + // to send the 6 byte self lookup + // we do not record as request or forward it, just reply with peers + p.retrieve(&retrieveRequestMsgData{}) + glog.V(logger.Detail).Infof("'whatsup wheresdaparty' sent to %v", p) + + return nil +} + +// called after peer disconnected +func (self *Hive) removePeer(p *peer) { + glog.V(logger.Debug).Infof("bee %v removed", p) + self.kad.Off(p, saveSync) + select { + case self.more <- true: + default: + } + if self.kad.Count() == 0 { + glog.V(logger.Debug).Infof("empty, all bees gone") + } +} + +// Retrieve a list of live peers that are closer to target than us +func (self *Hive) getPeers(target storage.Key, max int) (peers []*peer) { + var addr kademlia.Address + copy(addr[:], target[:]) + for _, node := range self.kad.FindClosest(addr, max) { + peers = append(peers, node.(*peer)) + } + return +} + +// disconnects all the peers +func (self *Hive) DropAll() { + glog.V(logger.Info).Infof("dropping all bees") + for _, node := range self.kad.FindClosest(kademlia.Address{}, 0) { + node.Drop() + } +} + +// contructor for kademlia.NodeRecord based on peer address alone +// TODO: should go away and only addr passed to kademlia +func newNodeRecord(addr *peerAddr) *kademlia.NodeRecord { + now := time.Now() + return &kademlia.NodeRecord{ + Addr: addr.Addr, + Url: addr.String(), + Seen: now, + After: now, + } +} + +// called by the protocol when receiving peerset (for target address) +// peersMsgData is converted to a slice of NodeRecords for Kademlia +// this is to store all thats needed +func (self *Hive) HandlePeersMsg(req *peersMsgData, from *peer) { + var nrs []*kademlia.NodeRecord + for _, p := range req.Peers { + nrs = append(nrs, newNodeRecord(p)) + } + self.kad.Add(nrs) +} + +// peer wraps the protocol instance to represent a connected peer +// it implements kademlia.Node interface +type peer struct { + *bzz // protocol instance running on peer connection +} + +// protocol instance implements kademlia.Node interface (embedded peer) +func (self *peer) Addr() kademlia.Address { + return self.remoteAddr.Addr +} + +func (self *peer) Url() string { + return self.remoteAddr.String() +} + +// TODO take into account traffic +func (self *peer) LastActive() time.Time { + return self.lastActive +} + +// reads the serialised form of sync state persisted as the 'Meta' attribute +// and sets the decoded syncState on the online node +func loadSync(record *kademlia.NodeRecord, node kademlia.Node) error { + p, ok := node.(*peer) + if !ok { + return fmt.Errorf("invalid type") + } + if record.Meta == nil { + glog.V(logger.Debug).Infof("no sync state for node record %v setting default", record) + p.syncState = &syncState{DbSyncState: &storage.DbSyncState{}} + return nil + } + state, err := decodeSync(record.Meta) + if err != nil { + return fmt.Errorf("error decoding kddb record meta info into a sync state: %v", err) + } + glog.V(logger.Detail).Infof("sync state for node record %v read from Meta: %s", record, string(*(record.Meta))) + p.syncState = state + return err +} + +// callback when saving a sync state +func saveSync(record *kademlia.NodeRecord, node kademlia.Node) { + if p, ok := node.(*peer); ok { + meta, err := encodeSync(p.syncState) + if err != nil { + glog.V(logger.Warn).Infof("error saving sync state for %v: %v", node, err) + return + } + glog.V(logger.Detail).Infof("saved sync state for %v: %s", node, string(*meta)) + record.Meta = meta + } +} + +// the immediate response to a retrieve request, +// sends relevant peer data given by the kademlia hive to the requester +// TODO: remember peers sent for duration of the session, only new peers sent +func (self *Hive) peers(req *retrieveRequestMsgData) { + if req != nil && req.MaxPeers >= 0 { + var addrs []*peerAddr + if req.timeout == nil || time.Now().Before(*(req.timeout)) { + key := req.Key + // self lookup from remote peer + if storage.IsZeroKey(key) { + addr := req.from.Addr() + key = storage.Key(addr[:]) + req.Key = nil + } + // get peer addresses from hive + for _, peer := range self.getPeers(key, int(req.MaxPeers)) { + addrs = append(addrs, peer.remoteAddr) + } + glog.V(logger.Debug).Infof("Hive sending %d peer addresses to %v. req.Id: %v, req.Key: %v", len(addrs), req.from, req.Id, req.Key.Log()) + + peersData := &peersMsgData{ + Peers: addrs, + Key: req.Key, + Id: req.Id, + } + peersData.setTimeout(req.timeout) + req.from.peers(peersData) + } + } +} + +func (self *Hive) String() string { + return self.kad.String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go new file mode 100644 index 000000000..16c5ce532 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go @@ -0,0 +1,173 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "fmt" + "math/rand" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +type Address common.Hash + +func (a Address) String() string { + return fmt.Sprintf("%x", a[:]) +} + +func (a *Address) MarshalJSON() (out []byte, err error) { + return []byte(`"` + a.String() + `"`), nil +} + +func (a *Address) UnmarshalJSON(value []byte) error { + *a = Address(common.HexToHash(string(value[1 : len(value)-1]))) + return nil +} + +// the string form of the binary representation of an address (only first 8 bits) +func (a Address) Bin() string { + var bs []string + for _, b := range a[:] { + bs = append(bs, fmt.Sprintf("%08b", b)) + } + return strings.Join(bs, "") +} + +/* +Proximity(x, y) returns the proximity order of the MSB distance between x and y + +The distance metric MSB(x, y) of two equal length byte sequences x an y is the +value of the binary integer cast of the x^y, ie., x and y bitwise xor-ed. +the binary cast is big endian: most significant bit first (=MSB). + +Proximity(x, y) is a discrete logarithmic scaling of the MSB distance. +It is defined as the reverse rank of the integer part of the base 2 +logarithm of the distance. +It is calculated by counting the number of common leading zeros in the (MSB) +binary representation of the x^y. + +(0 farthest, 255 closest, 256 self) +*/ +func proximity(one, other Address) (ret int) { + for i := 0; i < len(one); i++ { + oxo := one[i] ^ other[i] + for j := 0; j < 8; j++ { + if (uint8(oxo)>>uint8(7-j))&0x01 != 0 { + return i*8 + j + } + } + } + return len(one) * 8 +} + +// Address.ProxCmp compares the distances a->target and b->target. +// Returns -1 if a is closer to target, 1 if b is closer to target +// and 0 if they are equal. +func (target Address) ProxCmp(a, b Address) int { + for i := range target { + da := a[i] ^ target[i] + db := b[i] ^ target[i] + if da > db { + return 1 + } else if da < db { + return -1 + } + } + return 0 +} + +// randomAddressAt(address, prox) generates a random address +// at proximity order prox relative to address +// if prox is negative a random address is generated +func RandomAddressAt(self Address, prox int) (addr Address) { + addr = self + var pos int + if prox >= 0 { + pos = prox / 8 + trans := prox % 8 + transbytea := byte(0) + for j := 0; j <= trans; j++ { + transbytea |= 1 << uint8(7-j) + } + flipbyte := byte(1 << uint8(7-trans)) + transbyteb := transbytea ^ byte(255) + randbyte := byte(rand.Intn(255)) + addr[pos] = ((addr[pos] & transbytea) ^ flipbyte) | randbyte&transbyteb + } + for i := pos + 1; i < len(addr); i++ { + addr[i] = byte(rand.Intn(255)) + } + + return +} + +// KeyRange(a0, a1, proxLimit) returns the address inclusive address +// range that contain addresses closer to one than other +func KeyRange(one, other Address, proxLimit int) (start, stop Address) { + prox := proximity(one, other) + if prox >= proxLimit { + prox = proxLimit + } + start = CommonBitsAddrByte(one, other, byte(0x00), prox) + stop = CommonBitsAddrByte(one, other, byte(0xff), prox) + return +} + +func CommonBitsAddrF(self, other Address, f func() byte, p int) (addr Address) { + prox := proximity(self, other) + var pos int + if p <= prox { + prox = p + } + pos = prox / 8 + addr = self + trans := byte(prox % 8) + var transbytea byte + if p > prox { + transbytea = byte(0x7f) + } else { + transbytea = byte(0xff) + } + transbytea >>= trans + transbyteb := transbytea ^ byte(0xff) + addrpos := addr[pos] + addrpos &= transbyteb + if p > prox { + addrpos ^= byte(0x80 >> trans) + } + addrpos |= transbytea & f() + addr[pos] = addrpos + for i := pos + 1; i < len(addr); i++ { + addr[i] = f() + } + + return +} + +func CommonBitsAddr(self, other Address, prox int) (addr Address) { + return CommonBitsAddrF(self, other, func() byte { return byte(rand.Intn(255)) }, prox) +} + +func CommonBitsAddrByte(self, other Address, b byte, prox int) (addr Address) { + return CommonBitsAddrF(self, other, func() byte { return b }, prox) +} + +// randomAddressAt() generates a random address +func RandomAddress() Address { + return RandomAddressAt(Address{}, -1) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go new file mode 100644 index 000000000..53a7db451 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go @@ -0,0 +1,351 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +type NodeData interface { + json.Marshaler + json.Unmarshaler +} + +// allow inactive peers under +type NodeRecord struct { + Addr Address // address of node + Url string // Url, used to connect to node + After time.Time // next call after time + Seen time.Time // last connected at time + Meta *json.RawMessage // arbitrary metadata saved for a peer + + node Node +} + +func (self *NodeRecord) setSeen() { + t := time.Now() + self.Seen = t + self.After = t +} + +func (self *NodeRecord) String() string { + return fmt.Sprintf("<%v>", self.Addr) +} + +// persisted node record database () +type KadDb struct { + Address Address + Nodes [][]*NodeRecord + index map[Address]*NodeRecord + cursors []int + lock sync.RWMutex + purgeInterval time.Duration + initialRetryInterval time.Duration + connRetryExp int +} + +func newKadDb(addr Address, params *KadParams) *KadDb { + return &KadDb{ + Address: addr, + Nodes: make([][]*NodeRecord, params.MaxProx+1), // overwritten by load + cursors: make([]int, params.MaxProx+1), + index: make(map[Address]*NodeRecord), + purgeInterval: params.PurgeInterval, + initialRetryInterval: params.InitialRetryInterval, + connRetryExp: params.ConnRetryExp, + } +} + +func (self *KadDb) findOrCreate(index int, a Address, url string) *NodeRecord { + defer self.lock.Unlock() + self.lock.Lock() + + record, found := self.index[a] + if !found { + record = &NodeRecord{ + Addr: a, + Url: url, + } + glog.V(logger.Info).Infof("add new record %v to kaddb", record) + // insert in kaddb + self.index[a] = record + self.Nodes[index] = append(self.Nodes[index], record) + } else { + glog.V(logger.Info).Infof("found record %v in kaddb", record) + } + // update last seen time + record.setSeen() + // update with url in case IP/port changes + record.Url = url + return record +} + +// add adds node records to kaddb (persisted node record db) +func (self *KadDb) add(nrs []*NodeRecord, proximityBin func(Address) int) { + defer self.lock.Unlock() + self.lock.Lock() + var n int + var nodes []*NodeRecord + for _, node := range nrs { + _, found := self.index[node.Addr] + if !found && node.Addr != self.Address { + node.setSeen() + self.index[node.Addr] = node + index := proximityBin(node.Addr) + dbcursor := self.cursors[index] + nodes = self.Nodes[index] + // this is inefficient for allocation, need to just append then shift + newnodes := make([]*NodeRecord, len(nodes)+1) + copy(newnodes[:], nodes[:dbcursor]) + newnodes[dbcursor] = node + copy(newnodes[dbcursor+1:], nodes[dbcursor:]) + glog.V(logger.Detail).Infof("new nodes: %v (keys: %v)\nnodes: %v", newnodes, nodes) + self.Nodes[index] = newnodes + n++ + } + } + if n > 0 { + glog.V(logger.Debug).Infof("%d/%d node records (new/known)", n, len(nrs)) + } +} + +/* +next return one node record with the highest priority for desired +connection. +This is used to pick candidates for live nodes that are most wanted for +a higly connected low centrality network structure for Swarm which best suits +for a Kademlia-style routing. + +* Starting as naive node with empty db, this implements Kademlia bootstrapping +* As a mature node, it fills short lines. All on demand. + +The candidate is chosen using the following strategy: +We check for missing online nodes in the buckets for 1 upto Max BucketSize rounds. +On each round we proceed from the low to high proximity order buckets. +If the number of active nodes (=connected peers) is < rounds, then start looking +for a known candidate. To determine if there is a candidate to recommend the +kaddb node record database row corresponding to the bucket is checked. + +If the row cursor is on position i, the ith element in the row is chosen. +If the record is scheduled not to be retried before NOW, the next element is taken. +If the record is scheduled to be retried, it is set as checked, scheduled for +checking and is returned. The time of the next check is in X (duration) such that +X = ConnRetryExp * delta where delta is the time past since the last check and +ConnRetryExp is constant obsoletion factor. (Note that when node records are added +from peer messages, they are marked as checked and placed at the cursor, ie. +given priority over older entries). Entries which were checked more than +purgeInterval ago are deleted from the kaddb row. If no candidate is found after +a full round of checking the next bucket up is considered. If no candidate is +found when we reach the maximum-proximity bucket, the next round starts. + +node record a is more favoured to b a > b iff a is a passive node (record of +offline past peer) +|proxBin(a)| < |proxBin(b)| +|| (proxBin(a) < proxBin(b) && |proxBin(a)| == |proxBin(b)|) +|| (proxBin(a) == proxBin(b) && lastChecked(a) < lastChecked(b)) + + +The second argument returned names the first missing slot found +*/ +func (self *KadDb) findBest(maxBinSize int, binSize func(int) int) (node *NodeRecord, need bool, proxLimit int) { + // return nil, proxLimit indicates that all buckets are filled + defer self.lock.Unlock() + self.lock.Lock() + + var interval time.Duration + var found bool + var purge []bool + var delta time.Duration + var cursor int + var count int + var after time.Time + + // iterate over columns maximum bucketsize times + for rounds := 1; rounds <= maxBinSize; rounds++ { + ROUND: + // iterate over rows from PO 0 upto MaxProx + for po, dbrow := range self.Nodes { + // if row has rounds connected peers, then take the next + if binSize(po) >= rounds { + continue ROUND + } + if !need { + // set proxlimit to the PO where the first missing slot is found + proxLimit = po + need = true + } + purge = make([]bool, len(dbrow)) + + // there is a missing slot - finding a node to connect to + // select a node record from the relavant kaddb row (of identical prox order) + ROW: + for cursor = self.cursors[po]; !found && count < len(dbrow); cursor = (cursor + 1) % len(dbrow) { + count++ + node = dbrow[cursor] + + // skip already connected nodes + if node.node != nil { + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d/%d) already connected", node.Addr, po, cursor, len(dbrow)) + continue ROW + } + + // if node is scheduled to connect + if time.Time(node.After).After(time.Now()) { + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) skipped. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) + continue ROW + } + + delta = time.Since(time.Time(node.Seen)) + if delta < self.initialRetryInterval { + delta = self.initialRetryInterval + } + if delta > self.purgeInterval { + // remove node + purge[cursor] = true + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) unreachable since %v. Removed", node.Addr, po, cursor, node.Seen) + continue ROW + } + + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) ready to be tried. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) + + // scheduling next check + interval = time.Duration(delta * time.Duration(self.connRetryExp)) + after = time.Now().Add(interval) + + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) selected as candidate connection %v. seen at %v (%v ago), selectable since %v, retry after %v (in %v)", node.Addr, po, cursor, rounds, node.Seen, delta, node.After, after, interval) + node.After = after + found = true + } // ROW + self.cursors[po] = cursor + self.delete(po, purge) + if found { + return node, need, proxLimit + } + } // ROUND + } // ROUNDS + + return nil, need, proxLimit +} + +// deletes the noderecords of a kaddb row corresponding to the indexes +// caller must hold the dblock +// the call is unsafe, no index checks +func (self *KadDb) delete(row int, purge []bool) { + var nodes []*NodeRecord + dbrow := self.Nodes[row] + for i, del := range purge { + if i == self.cursors[row] { + //reset cursor + self.cursors[row] = len(nodes) + } + // delete the entry to be purged + if del { + delete(self.index, dbrow[i].Addr) + continue + } + // otherwise append to new list + nodes = append(nodes, dbrow[i]) + } + self.Nodes[row] = nodes +} + +// save persists kaddb on disk (written to file on path in json format. +func (self *KadDb) save(path string, cb func(*NodeRecord, Node)) error { + defer self.lock.Unlock() + self.lock.Lock() + + var n int + + for _, b := range self.Nodes { + for _, node := range b { + n++ + node.After = time.Now() + node.Seen = time.Now() + if cb != nil { + cb(node, node.node) + } + } + } + + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + err = ioutil.WriteFile(path, data, os.ModePerm) + if err != nil { + glog.V(logger.Warn).Infof("unable to save kaddb with %v nodes to %v: err", n, path, err) + } else { + glog.V(logger.Info).Infof("saved kaddb with %v nodes to %v", n, path) + } + return err +} + +// Load(path) loads the node record database (kaddb) from file on path. +func (self *KadDb) load(path string, cb func(*NodeRecord, Node) error) (err error) { + defer self.lock.Unlock() + self.lock.Lock() + + var data []byte + data, err = ioutil.ReadFile(path) + if err != nil { + return + } + + err = json.Unmarshal(data, self) + if err != nil { + return + } + var n int + var purge []bool + for po, b := range self.Nodes { + purge = make([]bool, len(b)) + ROW: + for i, node := range b { + if cb != nil { + err = cb(node, node.node) + if err != nil { + purge[i] = true + continue ROW + } + } + n++ + if (node.After == time.Time{}) { + node.After = time.Now() + } + self.index[node.Addr] = node + } + self.delete(po, purge) + } + glog.V(logger.Info).Infof("loaded kaddb with %v nodes from %v", n, path) + + return +} + +// accessor for KAD offline db count +func (self *KadDb) count() int { + defer self.lock.Unlock() + self.lock.Lock() + return len(self.index) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go new file mode 100644 index 000000000..87c57cefe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go @@ -0,0 +1,429 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "fmt" + "sort" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + bucketSize = 4 + proxBinSize = 2 + maxProx = 8 + connRetryExp = 2 + maxPeers = 100 +) + +var ( + purgeInterval = 42 * time.Hour + initialRetryInterval = 42 * time.Millisecond + maxIdleInterval = 42 * 1000 * time.Millisecond + // maxIdleInterval = 42 * 10 0 * time.Millisecond +) + +type KadParams struct { + // adjustable parameters + MaxProx int + ProxBinSize int + BucketSize int + PurgeInterval time.Duration + InitialRetryInterval time.Duration + MaxIdleInterval time.Duration + ConnRetryExp int +} + +func NewKadParams() *KadParams { + return &KadParams{ + MaxProx: maxProx, + ProxBinSize: proxBinSize, + BucketSize: bucketSize, + PurgeInterval: purgeInterval, + InitialRetryInterval: initialRetryInterval, + MaxIdleInterval: maxIdleInterval, + ConnRetryExp: connRetryExp, + } +} + +// Kademlia is a table of active nodes +type Kademlia struct { + addr Address // immutable baseaddress of the table + *KadParams // Kademlia configuration parameters + proxLimit int // state, the PO of the first row of the most proximate bin + proxSize int // state, the number of peers in the most proximate bin + count int // number of active peers (w live connection) + buckets [][]Node // the actual bins + db *KadDb // kaddb, node record database + lock sync.RWMutex // mutex to access buckets +} + +type Node interface { + Addr() Address + Url() string + LastActive() time.Time + Drop() +} + +// public constructor +// add is the base address of the table +// params is KadParams configuration +func New(addr Address, params *KadParams) *Kademlia { + buckets := make([][]Node, params.MaxProx+1) + return &Kademlia{ + addr: addr, + KadParams: params, + buckets: buckets, + db: newKadDb(addr, params), + } +} + +// accessor for KAD base address +func (self *Kademlia) Addr() Address { + return self.addr +} + +// accessor for KAD active node count +func (self *Kademlia) Count() int { + defer self.lock.Unlock() + self.lock.Lock() + return self.count +} + +// accessor for KAD active node count +func (self *Kademlia) DBCount() int { + return self.db.count() +} + +// On is the entry point called when a new nodes is added +// unsafe in that node is not checked to be already active node (to be called once) +func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) { + glog.V(logger.Warn).Infof("%v", self) + defer self.lock.Unlock() + self.lock.Lock() + + index := self.proximityBin(node.Addr()) + record := self.db.findOrCreate(index, node.Addr(), node.Url()) + + if cb != nil { + err = cb(record, node) + glog.V(logger.Detail).Infof("cb(%v, %v) ->%v", record, node, err) + if err != nil { + return fmt.Errorf("unable to add node %v, callback error: %v", node.Addr(), err) + } + glog.V(logger.Debug).Infof("add node record %v with node %v", record, node) + } + + // insert in kademlia table of active nodes + bucket := self.buckets[index] + // if bucket is full insertion replaces the worst node + // TODO: give priority to peers with active traffic + if len(bucket) < self.BucketSize { // >= allows us to add peers beyond the bucketsize limitation + self.buckets[index] = append(bucket, node) + glog.V(logger.Debug).Infof("add node %v to table", node) + self.setProxLimit(index, true) + record.node = node + self.count++ + return nil + } + + // always rotate peers + idle := self.MaxIdleInterval + var pos int + var replaced Node + for i, p := range bucket { + idleInt := time.Since(p.LastActive()) + if idleInt > idle { + idle = idleInt + pos = i + replaced = p + } + } + if replaced == nil { + glog.V(logger.Debug).Infof("all peers wanted, PO%03d bucket full", index) + return fmt.Errorf("bucket full") + } + glog.V(logger.Debug).Infof("node %v replaced by %v (idle for %v > %v)", replaced, node, idle, self.MaxIdleInterval) + replaced.Drop() + // actually replace in the row. When off(node) is called, the peer is no longer in the row + bucket[pos] = node + // there is no change in bucket cardinalities so no prox limit adjustment is needed + record.node = node + self.count++ + return nil + +} + +// Off is the called when a node is taken offline (from the protocol main loop exit) +func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) { + self.lock.Lock() + defer self.lock.Unlock() + + index := self.proximityBin(node.Addr()) + bucket := self.buckets[index] + for i := 0; i < len(bucket); i++ { + if node.Addr() == bucket[i].Addr() { + self.buckets[index] = append(bucket[:i], bucket[(i+1):]...) + self.setProxLimit(index, false) + break + } + } + + record := self.db.index[node.Addr()] + // callback on remove + if cb != nil { + cb(record, record.node) + } + record.node = nil + self.count-- + glog.V(logger.Debug).Infof("remove node %v from table, population now is %v", node, self.count) + + return +} + +// proxLimit is dynamically adjusted so that +// 1) there is no empty buckets in bin < proxLimit and +// 2) the sum of all items are the minimum possible but higher than ProxBinSize +// adjust Prox (proxLimit and proxSize after an insertion/removal of nodes) +// caller holds the lock +func (self *Kademlia) setProxLimit(r int, on bool) { + // if the change is outside the core (PO lower) + // and the change does not leave a bucket empty then + // no adjustment needed + if r < self.proxLimit && len(self.buckets[r]) > 0 { + return + } + // if on=a node was added, then r must be within prox limit so increment cardinality + if on { + self.proxSize++ + curr := len(self.buckets[self.proxLimit]) + // if now core is big enough without the furthest bucket, then contract + // this can result in more than one bucket change + for self.proxSize >= self.ProxBinSize+curr && curr > 0 { + self.proxSize -= curr + self.proxLimit++ + curr = len(self.buckets[self.proxLimit]) + + glog.V(logger.Detail).Infof("proxbin contraction (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r) + } + return + } + // otherwise + if r >= self.proxLimit { + self.proxSize-- + } + // expand core by lowering prox limit until hit zero or cover the empty bucket or reached target cardinality + for (self.proxSize < self.ProxBinSize || r < self.proxLimit) && + self.proxLimit > 0 { + // + self.proxLimit-- + self.proxSize += len(self.buckets[self.proxLimit]) + glog.V(logger.Detail).Infof("proxbin expansion (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r) + } +} + +/* +returns the list of nodes belonging to the same proximity bin +as the target. The most proximate bin will be the union of the bins between +proxLimit and MaxProx. +*/ +func (self *Kademlia) FindClosest(target Address, max int) []Node { + self.lock.Lock() + defer self.lock.Unlock() + + r := nodesByDistance{ + target: target, + } + + po := self.proximityBin(target) + index := po + step := 1 + glog.V(logger.Detail).Infof("serving %v nodes at %v (PO%02d)", max, index, po) + + // if max is set to 0, just want a full bucket, dynamic number + min := max + // set limit to max + limit := max + if max == 0 { + min = 1 + limit = maxPeers + } + + var n int + for index >= 0 { + // add entire bucket + for _, p := range self.buckets[index] { + r.push(p, limit) + n++ + } + // terminate if index reached the bottom or enough peers > min + glog.V(logger.Detail).Infof("add %v -> %v (PO%02d, PO%03d)", len(self.buckets[index]), n, index, po) + if n >= min && (step < 0 || max == 0) { + break + } + // reach top most non-empty PO bucket, turn around + if index == self.MaxProx { + index = po + step = -1 + } + index += step + } + glog.V(logger.Detail).Infof("serve %d (<=%d) nodes for target lookup %v (PO%03d)", n, max, target, po) + return r.nodes +} + +func (self *Kademlia) Suggest() (*NodeRecord, bool, int) { + defer self.lock.RUnlock() + self.lock.RLock() + return self.db.findBest(self.BucketSize, func(i int) int { return len(self.buckets[i]) }) +} + +// adds node records to kaddb (persisted node record db) +func (self *Kademlia) Add(nrs []*NodeRecord) { + self.db.add(nrs, self.proximityBin) +} + +// nodesByDistance is a list of nodes, ordered by distance to target. +type nodesByDistance struct { + nodes []Node + target Address +} + +func sortedByDistanceTo(target Address, slice []Node) bool { + var last Address + for i, node := range slice { + if i > 0 { + if target.ProxCmp(node.Addr(), last) < 0 { + return false + } + } + last = node.Addr() + } + return true +} + +// push(node, max) adds the given node to the list, keeping the total size +// below max elements. +func (h *nodesByDistance) push(node Node, max int) { + // returns the firt index ix such that func(i) returns true + ix := sort.Search(len(h.nodes), func(i int) bool { + return h.target.ProxCmp(h.nodes[i].Addr(), node.Addr()) >= 0 + }) + + if len(h.nodes) < max { + h.nodes = append(h.nodes, node) + } + if ix < len(h.nodes) { + copy(h.nodes[ix+1:], h.nodes[ix:]) + h.nodes[ix] = node + } +} + +/* +Taking the proximity order relative to a fix point x classifies the points in +the space (n byte long byte sequences) into bins. Items in each are at +most half as distant from x as items in the previous bin. Given a sample of +uniformly distributed items (a hash function over arbitrary sequence) the +proximity scale maps onto series of subsets with cardinalities on a negative +exponential scale. + +It also has the property that any two item belonging to the same bin are at +most half as distant from each other as they are from x. + +If we think of random sample of items in the bins as connections in a network of interconnected nodes than relative proximity can serve as the basis for local +decisions for graph traversal where the task is to find a route between two +points. Since in every hop, the finite distance halves, there is +a guaranteed constant maximum limit on the number of hops needed to reach one +node from the other. +*/ + +func (self *Kademlia) proximityBin(other Address) (ret int) { + ret = proximity(self.addr, other) + if ret > self.MaxProx { + ret = self.MaxProx + } + return +} + +// provides keyrange for chunk db iteration +func (self *Kademlia) KeyRange(other Address) (start, stop Address) { + defer self.lock.RUnlock() + self.lock.RLock() + return KeyRange(self.addr, other, self.proxLimit) +} + +// save persists kaddb on disk (written to file on path in json format. +func (self *Kademlia) Save(path string, cb func(*NodeRecord, Node)) error { + return self.db.save(path, cb) +} + +// Load(path) loads the node record database (kaddb) from file on path. +func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err error) { + return self.db.load(path, cb) +} + +// kademlia table + kaddb table displayed with ascii +func (self *Kademlia) String() string { + defer self.lock.RUnlock() + self.lock.RLock() + defer self.db.lock.RUnlock() + self.db.lock.RLock() + + var rows []string + rows = append(rows, "=========================================================================") + rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.addr.String()[:6])) + rows = append(rows, fmt.Sprintf("population: %d (%d), proxLimit: %d, proxSize: %d", self.count, len(self.db.index), self.proxLimit, self.proxSize)) + rows = append(rows, fmt.Sprintf("MaxProx: %d, ProxBinSize: %d, BucketSize: %d", self.MaxProx, self.ProxBinSize, self.BucketSize)) + + for i, bucket := range self.buckets { + + if i == self.proxLimit { + rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i)) + } + row := []string{fmt.Sprintf("%03d", i), fmt.Sprintf("%2d", len(bucket))} + var k int + c := self.db.cursors[i] + for ; k < len(bucket); k++ { + p := bucket[(c+k)%len(bucket)] + row = append(row, p.Addr().String()[:6]) + if k == 4 { + break + } + } + for ; k < 4; k++ { + row = append(row, " ") + } + row = append(row, fmt.Sprintf("| %2d %2d", len(self.db.Nodes[i]), self.db.cursors[i])) + + for j, p := range self.db.Nodes[i] { + row = append(row, p.Addr.String()[:6]) + if j == 3 { + break + } + } + rows = append(rows, strings.Join(row, " ")) + if i == self.MaxProx { + } + } + rows = append(rows, "=========================================================================") + return strings.Join(rows, "\n") +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go new file mode 100644 index 000000000..d3858c424 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go @@ -0,0 +1,317 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/swarm/network/kademlia" + "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +/* +BZZ protocol Message Types and Message Data Types +*/ + +// bzz protocol message codes +const ( + statusMsg = iota // 0x01 + storeRequestMsg // 0x02 + retrieveRequestMsg // 0x03 + peersMsg // 0x04 + syncRequestMsg // 0x05 + deliveryRequestMsg // 0x06 + unsyncedKeysMsg // 0x07 + paymentMsg // 0x08 +) + +/* + Handshake + +* Version: 8 byte integer version of the protocol +* ID: arbitrary byte sequence client identifier human readable +* Addr: the address advertised by the node, format similar to DEVp2p wire protocol +* Swap: info for the swarm accounting protocol +* NetworkID: 8 byte integer network identifier +* Caps: swarm-specific capabilities, format identical to devp2p +* SyncState: syncronisation state (db iterator key and address space etc) persisted about the peer + +*/ +type statusMsgData struct { + Version uint64 + ID string + Addr *peerAddr + Swap *swap.SwapProfile + NetworkId uint64 +} + +func (self *statusMsgData) String() string { + return fmt.Sprintf("Status: Version: %v, ID: %v, Addr: %v, Swap: %v, NetworkId: %v", self.Version, self.ID, self.Addr, self.Swap, self.NetworkId) +} + +/* + store requests are forwarded to the peers in their kademlia proximity bin + if they are distant + if they are within our storage radius or have any incentive to store it + then attach your nodeID to the metadata + if the storage request is sufficiently close (within our proxLimit, i. e., the + last row of the routing table) +*/ +type storeRequestMsgData struct { + Key storage.Key // hash of datasize | data + SData []byte // the actual chunk Data + // optional + Id uint64 // request ID. if delivery, the ID is retrieve request ID + requestTimeout *time.Time // expiry for forwarding - [not serialised][not currently used] + storageTimeout *time.Time // expiry of content - [not serialised][not currently used] + from *peer // [not serialised] protocol registers the requester +} + +func (self storeRequestMsgData) String() string { + var from string + if self.from == nil { + from = "self" + } else { + from = self.from.Addr().String() + } + end := len(self.SData) + if len(self.SData) > 10 { + end = 10 + } + return fmt.Sprintf("from: %v, Key: %v; ID: %v, requestTimeout: %v, storageTimeout: %v, SData %x", from, self.Key, self.Id, self.requestTimeout, self.storageTimeout, self.SData[:end]) +} + +/* +Retrieve request + +Timeout in milliseconds. Note that zero timeout retrieval requests do not request forwarding, but prompt for a peers message response. therefore they serve also +as messages to retrieve peers. + +MaxSize specifies the maximum size that the peer will accept. This is useful in +particular if we allow storage and delivery of multichunk payload representing +the entire or partial subtree unfolding from the requested root key. +So when only interested in limited part of a stream (infinite trees) or only +testing chunk availability etc etc, we can indicate it by limiting the size here. + +Request ID can be newly generated or kept from the request originator. +If request ID Is missing or zero, the request is handled as a lookup only +prompting a peers response but not launching a search. Lookup requests are meant +to be used to bootstrap kademlia tables. + +In the special case that the key is the zero value as well, the remote peer's +address is assumed (the message is to be handled as a self lookup request). +The response is a PeersMsg with the peers in the kademlia proximity bin +corresponding to the address. +*/ + +type retrieveRequestMsgData struct { + Key storage.Key // target Key address of chunk to be retrieved + Id uint64 // request id, request is a lookup if missing or zero + MaxSize uint64 // maximum size of delivery accepted + MaxPeers uint64 // maximum number of peers returned + Timeout uint64 // the longest time we are expecting a response + timeout *time.Time // [not serialied] + from *peer // +} + +func (self retrieveRequestMsgData) String() string { + var from string + if self.from == nil { + from = "ourselves" + } else { + from = self.from.Addr().String() + } + var target []byte + if len(self.Key) > 3 { + target = self.Key[:4] + } + return fmt.Sprintf("from: %v, Key: %x; ID: %v, MaxSize: %v, MaxPeers: %d", from, target, self.Id, self.MaxSize, self.MaxPeers) +} + +// lookups are encoded by missing request ID +func (self retrieveRequestMsgData) isLookup() bool { + return self.Id == 0 +} + +// sets timeout fields +func (self retrieveRequestMsgData) setTimeout(t *time.Time) { + self.timeout = t + if t != nil { + self.Timeout = uint64(t.UnixNano()) + } else { + self.Timeout = 0 + } +} + +func (self retrieveRequestMsgData) getTimeout() (t *time.Time) { + if self.Timeout > 0 && self.timeout == nil { + timeout := time.Unix(int64(self.Timeout), 0) + t = &timeout + self.timeout = t + } + return +} + +// peerAddr is sent in StatusMsg as part of the handshake +type peerAddr struct { + IP net.IP + Port uint16 + ID []byte // the 64 byte NodeID (ECDSA Public Key) + Addr kademlia.Address +} + +// peerAddr pretty prints as enode +func (self peerAddr) String() string { + var nodeid discover.NodeID + copy(nodeid[:], self.ID) + return discover.NewNode(nodeid, self.IP, 0, self.Port).String() +} + +/* +peers Msg is one response to retrieval; it is always encouraged after a retrieval +request to respond with a list of peers in the same kademlia proximity bin. +The encoding of a peer is identical to that in the devp2p base protocol peers +messages: [IP, Port, NodeID] +note that a node's DPA address is not the NodeID but the hash of the NodeID. + +Timeout serves to indicate whether the responder is forwarding the query within +the timeout or not. + +NodeID serves as the owner of payment contracts and signer of proofs of transfer. + +The Key is the target (if response to a retrieval request) or missing (zero value) +peers address (hash of NodeID) if retrieval request was a self lookup. + +Peers message is requested by retrieval requests with a missing or zero value request ID +*/ +type peersMsgData struct { + Peers []*peerAddr // + Timeout uint64 // + timeout *time.Time // indicate whether responder is expected to deliver content + Key storage.Key // present if a response to a retrieval request + Id uint64 // present if a response to a retrieval request + from *peer +} + +// peers msg pretty printer +func (self peersMsgData) String() string { + var from string + if self.from == nil { + from = "ourselves" + } else { + from = self.from.Addr().String() + } + var target []byte + if len(self.Key) > 3 { + target = self.Key[:4] + } + return fmt.Sprintf("from: %v, Key: %x; ID: %v, Peers: %v", from, target, self.Id, self.Peers) +} + +func (self peersMsgData) setTimeout(t *time.Time) { + self.timeout = t + if t != nil { + self.Timeout = uint64(t.UnixNano()) + } else { + self.Timeout = 0 + } +} + +func (self peersMsgData) getTimeout() (t *time.Time) { + if self.Timeout > 0 && self.timeout == nil { + timeout := time.Unix(int64(self.Timeout), 0) + t = &timeout + self.timeout = t + } + return +} + +/* +syncRequest + +is sent after the handshake to initiate syncing +the syncState of the remote node is persisted in kaddb and set on the +peer/protocol instance when the node is registered by hive as online{ +*/ + +type syncRequestMsgData struct { + SyncState *syncState `rlp:"nil"` +} + +func (self *syncRequestMsgData) String() string { + return fmt.Sprintf("%v", self.SyncState) +} + +/* +deliveryRequest + +is sent once a batch of sync keys is filtered. The ones not found are +sent as a list of syncReuest (hash, priority) in the Deliver field. +When the source receives the sync request it continues to iterate +and fetch at most N items as yet unsynced. +At the same time responds with deliveries of the items. +*/ +type deliveryRequestMsgData struct { + Deliver []*syncRequest +} + +func (self *deliveryRequestMsgData) String() string { + return fmt.Sprintf("sync request for new chunks\ndelivery request for %v chunks", len(self.Deliver)) +} + +/* +unsyncedKeys + +is sent first after the handshake if SyncState iterator brings up hundreds, thousands? +and subsequently sent as a response to deliveryRequestMsgData. + +Syncing is the iterative process of exchanging unsyncedKeys and deliveryRequestMsgs +both ways. + +State contains the sync state sent by the source. When the source receives the +sync state it continues to iterate and fetch at most N items as yet unsynced. +At the same time responds with deliveries of the items. +*/ +type unsyncedKeysMsgData struct { + Unsynced []*syncRequest + State *syncState +} + +func (self *unsyncedKeysMsgData) String() string { + return fmt.Sprintf("sync: keys of %d new chunks (state %v) => synced: %v", len(self.Unsynced), self.State, self.State.Synced) +} + +/* +payment + +is sent when the swap balance is tilted in favour of the remote peer +and in absolute units exceeds the PayAt parameter in the remote peer's profile +*/ + +type paymentMsgData struct { + Units uint // units actually paid for (checked against amount by swap) + Promise *chequebook.Cheque // payment with cheque +} + +func (self *paymentMsgData) String() string { + return fmt.Sprintf("payment for %d units: %v", self.Units, self.Promise) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go new file mode 100644 index 000000000..5e65108d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go @@ -0,0 +1,554 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +/* +bzz implements the swarm wire protocol [bzz] (sister of eth and shh) +the protocol instance is launched on each peer by the network layer if the +bzz protocol handler is registered on the p2p server. + +The bzz protocol component speaks the bzz protocol +* handle the protocol handshake +* register peers in the KΛÐΞMLIΛ table via the hive logistic manager +* dispatch to hive for handling the DHT logic +* encode and decode requests for storage and retrieval +* handle sync protocol messages via the syncer +* talks the SWAP payment protocol (swap accounting is done within NetStore) +*/ + +import ( + "fmt" + "net" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/errs" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + bzzswap "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + Version = 0 + ProtocolLength = uint64(8) + ProtocolMaxMsgSize = 10 * 1024 * 1024 + NetworkId = 322 +) + +const ( + ErrMsgTooLarge = iota + ErrDecode + ErrInvalidMsgCode + ErrVersionMismatch + ErrNetworkIdMismatch + ErrNoStatusMsg + ErrExtraStatusMsg + ErrSwap + ErrSync + ErrUnwanted +) + +var errorToString = map[int]string{ + ErrMsgTooLarge: "Message too long", + ErrDecode: "Invalid message", + ErrInvalidMsgCode: "Invalid message code", + ErrVersionMismatch: "Protocol version mismatch", + ErrNetworkIdMismatch: "NetworkId mismatch", + ErrNoStatusMsg: "No status message", + ErrExtraStatusMsg: "Extra status message", + ErrSwap: "SWAP error", + ErrSync: "Sync error", + ErrUnwanted: "Unwanted peer", +} + +// bzz represents the swarm wire protocol +// an instance is running on each peer +type bzz struct { + selfID discover.NodeID // peer's node id used in peer advertising in handshake + key storage.Key // baseaddress as storage.Key + storage StorageHandler // handler storage/retrieval related requests coming via the bzz wire protocol + hive *Hive // the logistic manager, peerPool, routing service and peer handler + dbAccess *DbAccess // access to db storage counter and iterator for syncing + requestDb *storage.LDBDatabase // db to persist backlog of deliveries to aid syncing + remoteAddr *peerAddr // remote peers address + peer *p2p.Peer // the p2p peer object + rw p2p.MsgReadWriter // messageReadWriter to send messages to + errors *errs.Errors // errors table + backend chequebook.Backend + lastActive time.Time + + swap *swap.Swap // swap instance for the peer connection + swapParams *bzzswap.SwapParams // swap settings both local and remote + swapEnabled bool // flag to enable SWAP (will be set via Caps in handshake) + syncEnabled bool // flag to enable SYNC (will be set via Caps in handshake) + syncer *syncer // syncer instance for the peer connection + syncParams *SyncParams // syncer params + syncState *syncState // outgoing syncronisation state (contains reference to remote peers db counter) +} + +// interface type for handler of storage/retrieval related requests coming +// via the bzz wire protocol +// messages: UnsyncedKeys, DeliveryRequest, StoreRequest, RetrieveRequest +type StorageHandler interface { + HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error + HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer) error + HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) + HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer) +} + +/* +main entrypoint, wrappers starting a server that will run the bzz protocol +use this constructor to attach the protocol ("class") to server caps +This is done by node.Node#Register(func(node.ServiceContext) (Service, error)) +Service implements Protocols() which is an array of protocol constructors +at node startup the protocols are initialised +the Dev p2p layer then calls Run(p *p2p.Peer, rw p2p.MsgReadWriter) error +on each peer connection +The Run function of the Bzz protocol class creates a bzz instance +which will represent the peer for the swarm hive and all peer-aware components +*/ +func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams) (p2p.Protocol, error) { + + // a single global request db is created for all peer connections + // this is to persist delivery backlog and aid syncronisation + requestDb, err := storage.NewLDBDatabase(sy.RequestDbPath) + if err != nil { + return p2p.Protocol{}, fmt.Errorf("error setting up request db: %v", err) + } + + return p2p.Protocol{ + Name: "bzz", + Version: Version, + Length: ProtocolLength, + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, p, rw) + }, + }, nil +} + +/* +the main protocol loop that + * does the handshake by exchanging statusMsg + * if peer is valid and accepted, registers with the hive + * then enters into a forever loop handling incoming messages + * storage and retrieval related queries coming via bzz are dispatched to StorageHandler + * peer-related messages are dispatched to the hive + * payment related messages are relayed to SWAP service + * on disconnect, unregister the peer in the hive (note RemovePeer in the post-disconnect hook) + * whenever the loop terminates, the peer will disconnect with Subprotocol error + * whenever handlers return an error the loop terminates +*/ +func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) { + + self := &bzz{ + storage: depo, + backend: backend, + hive: hive, + dbAccess: dbaccess, + requestDb: requestDb, + peer: p, + rw: rw, + errors: &errs.Errors{ + Package: "BZZ", + Errors: errorToString, + }, + swapParams: sp, + syncParams: sy, + swapEnabled: hive.swapEnabled, + syncEnabled: true, + } + + // handle handshake + err = self.handleStatus() + if err != nil { + return err + } + defer func() { + // if the handler loop exits, the peer is disconnecting + // deregister the peer in the hive + self.hive.removePeer(&peer{bzz: self}) + if self.syncer != nil { + self.syncer.stop() // quits request db and delivery loops, save requests + } + if self.swap != nil { + self.swap.Stop() // quits chequebox autocash etc + } + }() + + // the main forever loop that handles incoming requests + for { + if self.hive.blockRead { + glog.V(logger.Warn).Infof("Cannot read network") + time.Sleep(100 * time.Millisecond) + continue + } + err = self.handle() + if err != nil { + return + } + } +} + +// TODO: may need to implement protocol drop only? don't want to kick off the peer +// if they are useful for other protocols +func (self *bzz) Drop() { + self.peer.Disconnect(p2p.DiscSubprotocolError) +} + +// one cycle of the main forever loop that handles and dispatches incoming messages +func (self *bzz) handle() error { + msg, err := self.rw.ReadMsg() + glog.V(logger.Debug).Infof("<- %v", msg) + if err != nil { + return err + } + if msg.Size > ProtocolMaxMsgSize { + return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + // make sure that the payload has been fully consumed + defer msg.Discard() + + switch msg.Code { + + case statusMsg: + // no extra status message allowed. The one needed already handled by + // handleStatus + glog.V(logger.Debug).Infof("Status message: %v", msg) + return self.protoError(ErrExtraStatusMsg, "") + + case storeRequestMsg: + // store requests are dispatched to netStore + var req storeRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + if len(req.SData) < 9 { + return self.protoError(ErrDecode, "<- %v: Data too short (%v)", msg) + } + // last Active time is set only when receiving chunks + self.lastActive = time.Now() + glog.V(logger.Detail).Infof("incoming store request: %s", req.String()) + // swap accounting is done within forwarding + self.storage.HandleStoreRequestMsg(&req, &peer{bzz: self}) + + case retrieveRequestMsg: + // retrieve Requests are dispatched to netStore + var req retrieveRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + req.from = &peer{bzz: self} + // if request is lookup and not to be delivered + if req.isLookup() { + glog.V(logger.Detail).Infof("self lookup for %v: responding with peers only...", req.from) + } else if req.Key == nil { + return self.protoError(ErrDecode, "protocol handler: req.Key == nil || req.Timeout == nil") + } else { + // swap accounting is done within netStore + self.storage.HandleRetrieveRequestMsg(&req, &peer{bzz: self}) + } + // direct response with peers, TODO: sort this out + self.hive.peers(&req) + + case peersMsg: + // response to lookups and immediate response to retrieve requests + // dispatches new peer data to the hive that adds them to KADDB + var req peersMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + req.from = &peer{bzz: self} + glog.V(logger.Detail).Infof("<- peer addresses: %v", req) + self.hive.HandlePeersMsg(&req, &peer{bzz: self}) + + case syncRequestMsg: + var req syncRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- sync request: %v", req) + self.lastActive = time.Now() + self.sync(req.SyncState) + + case unsyncedKeysMsg: + // coming from parent node offering + var req unsyncedKeysMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- unsynced keys : %s", req.String()) + err := self.storage.HandleUnsyncedKeysMsg(&req, &peer{bzz: self}) + self.lastActive = time.Now() + if err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + + case deliveryRequestMsg: + // response to syncKeysMsg hashes filtered not existing in db + // also relays the last synced state to the source + var req deliveryRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<-msg %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- delivery request: %s", req.String()) + err := self.storage.HandleDeliveryRequestMsg(&req, &peer{bzz: self}) + self.lastActive = time.Now() + if err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + + case paymentMsg: + // swap protocol message for payment, Units paid for, Cheque paid with + if self.swapEnabled { + var req paymentMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- payment: %s", req.String()) + self.swap.Receive(int(req.Units), req.Promise) + } + + default: + // no other message is allowed + return self.protoError(ErrInvalidMsgCode, "%v", msg.Code) + } + return nil +} + +func (self *bzz) handleStatus() (err error) { + + handshake := &statusMsgData{ + Version: uint64(Version), + ID: "honey", + Addr: self.selfAddr(), + NetworkId: uint64(NetworkId), + Swap: &bzzswap.SwapProfile{ + Profile: self.swapParams.Profile, + PayProfile: self.swapParams.PayProfile, + }, + } + + err = p2p.Send(self.rw, statusMsg, handshake) + if err != nil { + self.protoError(ErrNoStatusMsg, err.Error()) + } + + // read and handle remote status + var msg p2p.Msg + msg, err = self.rw.ReadMsg() + if err != nil { + return err + } + + if msg.Code != statusMsg { + self.protoError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, statusMsg) + } + + if msg.Size > ProtocolMaxMsgSize { + return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + + var status statusMsgData + if err := msg.Decode(&status); err != nil { + return self.protoError(ErrDecode, " %v: %v", msg, err) + } + + if status.NetworkId != NetworkId { + return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId) + } + + if Version != status.Version { + return self.protoError(ErrVersionMismatch, "%d (!= %d)", status.Version, Version) + } + + self.remoteAddr = self.peerAddr(status.Addr) + glog.V(logger.Detail).Infof("self: advertised IP: %v, peer advertised: %v, local address: %v\npeer: advertised IP: %v, remote address: %v\n", self.selfAddr(), self.remoteAddr, self.peer.LocalAddr(), status.Addr.IP, self.peer.RemoteAddr()) + + if self.swapEnabled { + // set remote profile for accounting + self.swap, err = bzzswap.NewSwap(self.swapParams, status.Swap, self.backend, self) + if err != nil { + return self.protoError(ErrSwap, "%v", err) + } + } + + glog.V(logger.Info).Infof("Peer %08x is capable (%d/%d)", self.remoteAddr.Addr[:4], status.Version, status.NetworkId) + err = self.hive.addPeer(&peer{bzz: self}) + if err != nil { + return self.protoError(ErrUnwanted, "%v", err) + } + + // hive sets syncstate so sync should start after node added + glog.V(logger.Info).Infof("syncronisation request sent with %v", self.syncState) + self.syncRequest() + + return nil +} + +func (self *bzz) sync(state *syncState) error { + // syncer setup + if self.syncer != nil { + return self.protoError(ErrSync, "sync request can only be sent once") + } + + cnt := self.dbAccess.counter() + remoteaddr := self.remoteAddr.Addr + start, stop := self.hive.kad.KeyRange(remoteaddr) + + // an explicitly received nil syncstate disables syncronisation + if state == nil { + self.syncEnabled = false + glog.V(logger.Warn).Infof("syncronisation disabled for peer %v", self) + state = &syncState{DbSyncState: &storage.DbSyncState{}, Synced: true} + } else { + state.synced = make(chan bool) + state.SessionAt = cnt + if storage.IsZeroKey(state.Stop) && state.Synced { + state.Start = storage.Key(start[:]) + state.Stop = storage.Key(stop[:]) + } + glog.V(logger.Debug).Infof("syncronisation requested by peer %v at state %v", self, state) + } + var err error + self.syncer, err = newSyncer( + self.requestDb, + storage.Key(remoteaddr[:]), + self.dbAccess, + self.unsyncedKeys, self.store, + self.syncParams, state, func() bool { return self.syncEnabled }, + ) + if err != nil { + return self.protoError(ErrSync, "%v", err) + } + glog.V(logger.Detail).Infof("syncer set for peer %v", self) + return nil +} + +func (self *bzz) String() string { + return self.remoteAddr.String() +} + +// repair reported address if IP missing +func (self *bzz) peerAddr(base *peerAddr) *peerAddr { + if base.IP.IsUnspecified() { + host, _, _ := net.SplitHostPort(self.peer.RemoteAddr().String()) + base.IP = net.ParseIP(host) + } + return base +} + +// returns self advertised node connection info (listening address w enodes) +// IP will get repaired on the other end if missing +// or resolved via ID by discovery at dialout +func (self *bzz) selfAddr() *peerAddr { + id := self.hive.id + host, port, _ := net.SplitHostPort(self.hive.listenAddr()) + intport, _ := strconv.Atoi(port) + addr := &peerAddr{ + Addr: self.hive.addr, + ID: id[:], + IP: net.ParseIP(host), + Port: uint16(intport), + } + return addr +} + +// outgoing messages +// send retrieveRequestMsg +func (self *bzz) retrieve(req *retrieveRequestMsgData) error { + return self.send(retrieveRequestMsg, req) +} + +// send storeRequestMsg +func (self *bzz) store(req *storeRequestMsgData) error { + return self.send(storeRequestMsg, req) +} + +func (self *bzz) syncRequest() error { + req := &syncRequestMsgData{} + if self.hive.syncEnabled { + glog.V(logger.Debug).Infof("syncronisation request to peer %v at state %v", self, self.syncState) + req.SyncState = self.syncState + } + if self.syncState == nil { + glog.V(logger.Warn).Infof("syncronisation disabled for peer %v at state %v", self, self.syncState) + } + return self.send(syncRequestMsg, req) +} + +// queue storeRequestMsg in request db +func (self *bzz) deliveryRequest(reqs []*syncRequest) error { + req := &deliveryRequestMsgData{ + Deliver: reqs, + } + return self.send(deliveryRequestMsg, req) +} + +// batch of syncRequests to send off +func (self *bzz) unsyncedKeys(reqs []*syncRequest, state *syncState) error { + req := &unsyncedKeysMsgData{ + Unsynced: reqs, + State: state, + } + return self.send(unsyncedKeysMsg, req) +} + +// send paymentMsg +func (self *bzz) Pay(units int, promise swap.Promise) { + req := &paymentMsgData{uint(units), promise.(*chequebook.Cheque)} + self.payment(req) +} + +// send paymentMsg +func (self *bzz) payment(req *paymentMsgData) error { + return self.send(paymentMsg, req) +} + +// sends peersMsg +func (self *bzz) peers(req *peersMsgData) error { + return self.send(peersMsg, req) +} + +func (self *bzz) protoError(code int, format string, params ...interface{}) (err *errs.Error) { + err = self.errors.New(code, format, params...) + err.Log(glog.V(logger.Info)) + return +} + +func (self *bzz) protoErrorDisconnect(err *errs.Error) { + err.Log(glog.V(logger.Info)) + if err.Fatal() { + self.peer.Disconnect(p2p.DiscSubprotocolError) + } +} + +func (self *bzz) send(msg uint64, data interface{}) error { + if self.hive.blockWrite { + return fmt.Errorf("network write blocked") + } + glog.V(logger.Detail).Infof("-> %v: %v (%T) to %v", msg, data, data, self) + err := p2p.Send(self.rw, msg, data) + if err != nil { + self.Drop() + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go new file mode 100644 index 000000000..cef32610f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go @@ -0,0 +1,390 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "encoding/binary" + "fmt" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" +) + +const counterKeyPrefix = 0x01 + +/* +syncDb is a queueing service for outgoing deliveries. +One instance per priority queue for each peer + +a syncDb instance maintains an in-memory buffer (of capacity bufferSize) +once its in-memory buffer is full it switches to persisting in db +and dbRead iterator iterates through the items keeping their order +once the db read catches up (there is no more items in the db) then +it switches back to in-memory buffer. + +when syncdb is stopped all items in the buffer are saved to the db +*/ +type syncDb struct { + start []byte // this syncdb starting index in requestdb + key storage.Key // remote peers address key + counterKey []byte // db key to persist counter + priority uint // priotity High|Medium|Low + buffer chan interface{} // incoming request channel + db *storage.LDBDatabase // underlying db (TODO should be interface) + done chan bool // chan to signal goroutines finished quitting + quit chan bool // chan to signal quitting to goroutines + total, dbTotal int // counts for one session + batch chan chan int // channel for batch requests + dbBatchSize uint // number of items before batch is saved +} + +// constructor needs a shared request db (leveldb) +// priority is used in the index key +// uses a buffer and a leveldb for persistent storage +// bufferSize, dbBatchSize are config parameters +func newSyncDb(db *storage.LDBDatabase, key storage.Key, priority uint, bufferSize, dbBatchSize uint, deliver func(interface{}, chan bool) bool) *syncDb { + start := make([]byte, 42) + start[1] = byte(priorities - priority) + copy(start[2:34], key) + + counterKey := make([]byte, 34) + counterKey[0] = counterKeyPrefix + copy(counterKey[1:], start[1:34]) + + syncdb := &syncDb{ + start: start, + key: key, + counterKey: counterKey, + priority: priority, + buffer: make(chan interface{}, bufferSize), + db: db, + done: make(chan bool), + quit: make(chan bool), + batch: make(chan chan int), + dbBatchSize: dbBatchSize, + } + glog.V(logger.Detail).Infof("syncDb[peer: %v, priority: %v] - initialised", key.Log(), priority) + + // starts the main forever loop reading from buffer + go syncdb.bufferRead(deliver) + return syncdb +} + +/* +bufferRead is a forever iterator loop that takes care of delivering +outgoing store requests reads from incoming buffer + +its argument is the deliver function taking the item as first argument +and a quit channel as second. +Closing of this channel is supposed to abort all waiting for delivery +(typically network write) + +The iteration switches between 2 modes, +* buffer mode reads the in-memory buffer and delivers the items directly +* db mode reads from the buffer and writes to the db, parallelly another +routine is started that reads from the db and delivers items + +If there is buffer contention in buffer mode (slow network, high upload volume) +syncdb switches to db mode and starts dbRead +Once db backlog is delivered, it reverts back to in-memory buffer + +It is automatically started when syncdb is initialised. + +It saves the buffer to db upon receiving quit signal. syncDb#stop() +*/ +func (self *syncDb) bufferRead(deliver func(interface{}, chan bool) bool) { + var buffer, db chan interface{} // channels representing the two read modes + var more bool + var req interface{} + var entry *syncDbEntry + var inBatch, inDb int + batch := new(leveldb.Batch) + var dbSize chan int + quit := self.quit + counterValue := make([]byte, 8) + + // counter is used for keeping the items in order, persisted to db + // start counter where db was at, 0 if not found + data, err := self.db.Get(self.counterKey) + var counter uint64 + if err == nil { + counter = binary.BigEndian.Uint64(data) + glog.V(logger.Detail).Infof("syncDb[%v/%v] - counter read from db at %v", self.key.Log(), self.priority, counter) + } else { + glog.V(logger.Detail).Infof("syncDb[%v/%v] - counter starts at %v", self.key.Log(), self.priority, counter) + } + +LOOP: + for { + // waiting for item next in the buffer, or quit signal or batch request + select { + // buffer only closes when writing to db + case req = <-buffer: + // deliver request : this is blocking on network write so + // it is passed the quit channel as argument, so that it returns + // if syncdb is stopped. In this case we need to save the item to the db + more = deliver(req, self.quit) + if !more { + glog.V(logger.Debug).Infof("syncDb[%v/%v] quit: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total) + // received quit signal, save request currently waiting delivery + // by switching to db mode and closing the buffer + buffer = nil + db = self.buffer + close(db) + quit = nil // needs to block the quit case in select + break // break from select, this item will be written to the db + } + self.total++ + glog.V(logger.Detail).Infof("syncDb[%v/%v] deliver (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total) + // by the time deliver returns, there were new writes to the buffer + // if buffer contention is detected, switch to db mode which drains + // the buffer so no process will block on pushing store requests + if len(buffer) == cap(buffer) { + glog.V(logger.Debug).Infof("syncDb[%v/%v] buffer full %v: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, cap(buffer), self.dbTotal, self.total) + buffer = nil + db = self.buffer + } + continue LOOP + + // incoming entry to put into db + case req, more = <-db: + if !more { + // only if quit is called, saved all the buffer + binary.BigEndian.PutUint64(counterValue, counter) + batch.Put(self.counterKey, counterValue) // persist counter in batch + self.writeSyncBatch(batch) // save batch + glog.V(logger.Detail).Infof("syncDb[%v/%v] quitting: save current batch to db", self.key.Log(), self.priority) + break LOOP + } + self.dbTotal++ + self.total++ + // otherwise break after select + case dbSize = <-self.batch: + // explicit request for batch + if inBatch == 0 && quit != nil { + // there was no writes since the last batch so db depleted + // switch to buffer mode + glog.V(logger.Debug).Infof("syncDb[%v/%v] empty db: switching to buffer", self.key.Log(), self.priority) + db = nil + buffer = self.buffer + dbSize <- 0 // indicates to 'caller' that batch has been written + inDb = 0 + continue LOOP + } + binary.BigEndian.PutUint64(counterValue, counter) + batch.Put(self.counterKey, counterValue) + glog.V(logger.Debug).Infof("syncDb[%v/%v] write batch %v/%v - %x - %x", self.key.Log(), self.priority, inBatch, counter, self.counterKey, counterValue) + batch = self.writeSyncBatch(batch) + dbSize <- inBatch // indicates to 'caller' that batch has been written + inBatch = 0 + continue LOOP + + // closing syncDb#quit channel is used to signal to all goroutines to quit + case <-quit: + // need to save backlog, so switch to db mode + db = self.buffer + buffer = nil + quit = nil + glog.V(logger.Detail).Infof("syncDb[%v/%v] quitting: save buffer to db", self.key.Log(), self.priority) + close(db) + continue LOOP + } + + // only get here if we put req into db + entry, err = self.newSyncDbEntry(req, counter) + if err != nil { + glog.V(logger.Warn).Infof("syncDb[%v/%v] saving request %v (#%v/%v) failed: %v", self.key.Log(), self.priority, req, inBatch, inDb, err) + continue LOOP + } + batch.Put(entry.key, entry.val) + glog.V(logger.Detail).Infof("syncDb[%v/%v] to batch %v '%v' (#%v/%v/%v)", self.key.Log(), self.priority, req, entry, inBatch, inDb, counter) + // if just switched to db mode and not quitting, then launch dbRead + // in a parallel go routine to send deliveries from db + if inDb == 0 && quit != nil { + glog.V(logger.Detail).Infof("syncDb[%v/%v] start dbRead") + go self.dbRead(true, counter, deliver) + } + inDb++ + inBatch++ + counter++ + // need to save the batch if it gets too large (== dbBatchSize) + if inBatch%int(self.dbBatchSize) == 0 { + batch = self.writeSyncBatch(batch) + } + } + glog.V(logger.Info).Infof("syncDb[%v:%v]: saved %v keys (saved counter at %v)", self.key.Log(), self.priority, inBatch, counter) + close(self.done) +} + +// writes the batch to the db and returns a new batch object +func (self *syncDb) writeSyncBatch(batch *leveldb.Batch) *leveldb.Batch { + err := self.db.Write(batch) + if err != nil { + glog.V(logger.Warn).Infof("syncDb[%v/%v] saving batch to db failed: %v", self.key.Log(), self.priority, err) + return batch + } + return new(leveldb.Batch) +} + +// abstract type for db entries (TODO could be a feature of Receipts) +type syncDbEntry struct { + key, val []byte +} + +func (self syncDbEntry) String() string { + return fmt.Sprintf("key: %x, value: %x", self.key, self.val) +} + +/* + dbRead is iterating over store requests to be sent over to the peer + this is mainly to prevent crashes due to network output buffer contention (???) + as well as to make syncronisation resilient to disconnects + the messages are supposed to be sent in the p2p priority queue. + + the request DB is shared between peers, but domains for each syncdb + are disjoint. dbkeys (42 bytes) are structured: + * 0: 0x00 (0x01 reserved for counter key) + * 1: priorities - priority (so that high priority can be replayed first) + * 2-33: peers address + * 34-41: syncdb counter to preserve order (this field is missing for the counter key) + + values (40 bytes) are: + * 0-31: key + * 32-39: request id + +dbRead needs a boolean to indicate if on first round all the historical +record is synced. Second argument to indicate current db counter +The third is the function to apply +*/ +func (self *syncDb) dbRead(useBatches bool, counter uint64, fun func(interface{}, chan bool) bool) { + key := make([]byte, 42) + copy(key, self.start) + binary.BigEndian.PutUint64(key[34:], counter) + var batches, n, cnt, total int + var more bool + var entry *syncDbEntry + var it iterator.Iterator + var del *leveldb.Batch + batchSizes := make(chan int) + + for { + // if useBatches is false, cnt is not set + if useBatches { + // this could be called before all cnt items sent out + // so that loop is not blocking while delivering + // only relevant if cnt is large + select { + case self.batch <- batchSizes: + case <-self.quit: + return + } + // wait for the write to finish and get the item count in the next batch + cnt = <-batchSizes + batches++ + if cnt == 0 { + // empty + return + } + } + it = self.db.NewIterator() + it.Seek(key) + if !it.Valid() { + copy(key, self.start) + useBatches = true + continue + } + del = new(leveldb.Batch) + glog.V(logger.Detail).Infof("syncDb[%v/%v]: new iterator: %x (batch %v, count %v)", self.key.Log(), self.priority, key, batches, cnt) + + for n = 0; !useBatches || n < cnt; it.Next() { + copy(key, it.Key()) + if len(key) == 0 || key[0] != 0 { + copy(key, self.start) + useBatches = true + break + } + val := make([]byte, 40) + copy(val, it.Value()) + entry = &syncDbEntry{key, val} + // glog.V(logger.Detail).Infof("syncDb[%v/%v] - %v, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, self.key.Log(), batches, total, self.dbTotal, self.total) + more = fun(entry, self.quit) + if !more { + // quit received when waiting to deliver entry, the entry will not be deleted + glog.V(logger.Detail).Infof("syncDb[%v/%v] batch %v quit after %v/%v items", self.key.Log(), self.priority, batches, n, cnt) + break + } + // since subsequent batches of the same db session are indexed incrementally + // deleting earlier batches can be delayed and parallelised + // this could be batch delete when db is idle (but added complexity esp when quitting) + del.Delete(key) + n++ + total++ + } + glog.V(logger.Debug).Infof("syncDb[%v/%v] - db session closed, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, batches, total, self.dbTotal, self.total) + self.db.Write(del) // this could be async called only when db is idle + it.Release() + } +} + +// +func (self *syncDb) stop() { + close(self.quit) + <-self.done +} + +// calculate a dbkey for the request, for the db to work +// see syncdb for db key structure +// polimorphic: accepted types, see syncer#addRequest +func (self *syncDb) newSyncDbEntry(req interface{}, counter uint64) (entry *syncDbEntry, err error) { + var key storage.Key + var chunk *storage.Chunk + var id uint64 + var ok bool + var sreq *storeRequestMsgData + + if key, ok = req.(storage.Key); ok { + id = generateId() + } else if chunk, ok = req.(*storage.Chunk); ok { + key = chunk.Key + id = generateId() + } else if sreq, ok = req.(*storeRequestMsgData); ok { + key = sreq.Key + id = sreq.Id + } else if entry, ok = req.(*syncDbEntry); !ok { + return nil, fmt.Errorf("type not allowed: %v (%T)", req, req) + } + + // order by peer > priority > seqid + // value is request id if exists + if entry == nil { + dbkey := make([]byte, 42) + dbval := make([]byte, 40) + + // encode key + copy(dbkey[:], self.start[:34]) // db peer + binary.BigEndian.PutUint64(dbkey[34:], counter) + // encode value + copy(dbval, key[:]) + binary.BigEndian.PutUint64(dbval[32:], id) + + entry = &syncDbEntry{dbkey, dbval} + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go new file mode 100644 index 000000000..e871666bd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go @@ -0,0 +1,778 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "path/filepath" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// syncer parameters (global, not peer specific) default values +const ( + requestDbBatchSize = 512 // size of batch before written to request db + keyBufferSize = 1024 // size of buffer for unsynced keys + syncBatchSize = 128 // maximum batchsize for outgoing requests + syncBufferSize = 128 // size of buffer for delivery requests + syncCacheSize = 1024 // cache capacity to store request queue in memory +) + +// priorities +const ( + Low = iota // 0 + Medium // 1 + High // 2 + priorities // 3 number of priority levels +) + +// request types +const ( + DeliverReq = iota // 0 + PushReq // 1 + PropagateReq // 2 + HistoryReq // 3 + BacklogReq // 4 +) + +// json serialisable struct to record the syncronisation state between 2 peers +type syncState struct { + *storage.DbSyncState // embeds the following 4 fields: + // Start Key // lower limit of address space + // Stop Key // upper limit of address space + // First uint64 // counter taken from last sync state + // Last uint64 // counter of remote peer dbStore at the time of last connection + SessionAt uint64 // set at the time of connection + LastSeenAt uint64 // set at the time of connection + Latest storage.Key // cursor of dbstore when last (continuously set by syncer) + Synced bool // true iff Sync is done up to the last disconnect + synced chan bool // signal that sync stage finished +} + +// wrapper of db-s to provide mockable custom local chunk store access to syncer +type DbAccess struct { + db *storage.DbStore + loc *storage.LocalStore +} + +func NewDbAccess(loc *storage.LocalStore) *DbAccess { + return &DbAccess{loc.DbStore.(*storage.DbStore), loc} +} + +// to obtain the chunks from key or request db entry only +func (self *DbAccess) get(key storage.Key) (*storage.Chunk, error) { + return self.loc.Get(key) +} + +// current storage counter of chunk db +func (self *DbAccess) counter() uint64 { + return self.db.Counter() +} + +// implemented by dbStoreSyncIterator +type keyIterator interface { + Next() storage.Key +} + +// generator function for iteration by address range and storage counter +func (self *DbAccess) iterator(s *syncState) keyIterator { + it, err := self.db.NewSyncIterator(*(s.DbSyncState)) + if err != nil { + return nil + } + return keyIterator(it) +} + +func (self syncState) String() string { + if self.Synced { + return fmt.Sprintf( + "session started at: %v, last seen at: %v, latest key: %v", + self.SessionAt, self.LastSeenAt, + self.Latest.Log(), + ) + } else { + return fmt.Sprintf( + "address: %v-%v, index: %v-%v, session started at: %v, last seen at: %v, latest key: %v", + self.Start.Log(), self.Stop.Log(), + self.First, self.Last, + self.SessionAt, self.LastSeenAt, + self.Latest.Log(), + ) + } +} + +// syncer parameters (global, not peer specific) +type SyncParams struct { + RequestDbPath string // path for request db (leveldb) + RequestDbBatchSize uint // nuber of items before batch is saved to requestdb + KeyBufferSize uint // size of key buffer + SyncBatchSize uint // maximum batchsize for outgoing requests + SyncBufferSize uint // size of buffer for + SyncCacheSize uint // cache capacity to store request queue in memory + SyncPriorities []uint // list of priority levels for req types 0-3 + SyncModes []bool // list of sync modes for for req types 0-3 +} + +// constructor with default values +func NewSyncParams(bzzdir string) *SyncParams { + return &SyncParams{ + RequestDbPath: filepath.Join(bzzdir, "requests"), + RequestDbBatchSize: requestDbBatchSize, + KeyBufferSize: keyBufferSize, + SyncBufferSize: syncBufferSize, + SyncBatchSize: syncBatchSize, + SyncCacheSize: syncCacheSize, + SyncPriorities: []uint{High, Medium, Medium, Low, Low}, + SyncModes: []bool{true, true, true, true, false}, + } +} + +// syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding +type syncer struct { + *SyncParams // sync parameters + syncF func() bool // if syncing is needed + key storage.Key // remote peers address key + state *syncState // sync state for our dbStore + syncStates chan *syncState // different stages of sync + deliveryRequest chan bool // one of two triggers needed to send unsyncedKeys + newUnsyncedKeys chan bool // one of two triggers needed to send unsynced keys + quit chan bool // signal to quit loops + + // DB related fields + dbAccess *DbAccess // access to dbStore + db *storage.LDBDatabase // delivery msg db + + // native fields + queues [priorities]*syncDb // in-memory cache / queues for sync reqs + keys [priorities]chan interface{} // buffer for unsynced keys + deliveries [priorities]chan *storeRequestMsgData // delivery + + // bzz protocol instance outgoing message callbacks (mockable for testing) + unsyncedKeys func([]*syncRequest, *syncState) error // send unsyncedKeysMsg + store func(*storeRequestMsgData) error // send storeRequestMsg +} + +// a syncer instance is linked to each peer connection +// constructor is called from protocol after successful handshake +// the returned instance is attached to the peer and can be called +// by the forwarder +func newSyncer( + db *storage.LDBDatabase, remotekey storage.Key, + dbAccess *DbAccess, + unsyncedKeys func([]*syncRequest, *syncState) error, + store func(*storeRequestMsgData) error, + params *SyncParams, + state *syncState, + syncF func() bool, +) (*syncer, error) { + + syncBufferSize := params.SyncBufferSize + keyBufferSize := params.KeyBufferSize + dbBatchSize := params.RequestDbBatchSize + + self := &syncer{ + syncF: syncF, + key: remotekey, + dbAccess: dbAccess, + syncStates: make(chan *syncState, 20), + deliveryRequest: make(chan bool, 1), + newUnsyncedKeys: make(chan bool, 1), + SyncParams: params, + state: state, + quit: make(chan bool), + unsyncedKeys: unsyncedKeys, + store: store, + } + + // initialising + for i := 0; i < priorities; i++ { + self.keys[i] = make(chan interface{}, keyBufferSize) + self.deliveries[i] = make(chan *storeRequestMsgData) + // initialise a syncdb instance for each priority queue + self.queues[i] = newSyncDb(db, remotekey, uint(i), syncBufferSize, dbBatchSize, self.deliver(uint(i))) + } + glog.V(logger.Info).Infof("syncer started: %v", state) + // launch chunk delivery service + go self.syncDeliveries() + // launch sync task manager + if self.syncF() { + go self.sync() + } + // process unsynced keys to broadcast + go self.syncUnsyncedKeys() + + return self, nil +} + +// metadata serialisation +func encodeSync(state *syncState) (*json.RawMessage, error) { + data, err := json.MarshalIndent(state, "", " ") + if err != nil { + return nil, err + } + meta := json.RawMessage(data) + return &meta, nil +} + +func decodeSync(meta *json.RawMessage) (*syncState, error) { + if meta == nil { + return nil, fmt.Errorf("unable to deserialise sync state from ") + } + data := []byte(*(meta)) + if len(data) == 0 { + return nil, fmt.Errorf("unable to deserialise sync state from ") + } + state := &syncState{DbSyncState: &storage.DbSyncState{}} + err := json.Unmarshal(data, state) + return state, err +} + +/* + sync implements the syncing script + * first all items left in the request Db are replayed + * type = StaleSync + * Mode: by default once again via confirmation roundtrip + * Priority: the items are replayed as the proirity specified for StaleSync + * but within the order respects earlier priority level of request + * after all items are consumed for a priority level, the the respective + queue for delivery requests is open (this way new reqs not written to db) + (TODO: this should be checked) + * the sync state provided by the remote peer is used to sync history + * all the backlog from earlier (aborted) syncing is completed starting from latest + * if Last < LastSeenAt then all items in between then process all + backlog from upto last disconnect + * if Last > 0 && + + sync is called from the syncer constructor and is not supposed to be used externally +*/ +func (self *syncer) sync() { + state := self.state + // sync finished + defer close(self.syncStates) + + // 0. first replay stale requests from request db + if state.SessionAt == 0 { + glog.V(logger.Debug).Infof("syncer[%v]: nothing to sync", self.key.Log()) + return + } + glog.V(logger.Debug).Infof("syncer[%v]: start replaying stale requests from request db", self.key.Log()) + for p := priorities - 1; p >= 0; p-- { + self.queues[p].dbRead(false, 0, self.replay()) + } + glog.V(logger.Debug).Infof("syncer[%v]: done replaying stale requests from request db", self.key.Log()) + + // unless peer is synced sync unfinished history beginning on + if !state.Synced { + start := state.Start + + if !storage.IsZeroKey(state.Latest) { + // 1. there is unfinished earlier sync + state.Start = state.Latest + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising backlog (unfinished sync: %v)", self.key.Log(), state) + // blocks while the entire history upto state is synced + self.syncState(state) + if state.Last < state.SessionAt { + state.First = state.Last + 1 + } + } + state.Latest = storage.ZeroKey + state.Start = start + // 2. sync up to last disconnect1 + if state.First < state.LastSeenAt { + state.Last = state.LastSeenAt + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising history upto last disconnect at %v: %v", self.key.Log(), state.LastSeenAt, state) + self.syncState(state) + state.First = state.LastSeenAt + } + state.Latest = storage.ZeroKey + + } else { + // synchronisation starts at end of last session + state.First = state.LastSeenAt + } + + // 3. sync up to current session start + // if there have been new chunks since last session + if state.LastSeenAt < state.SessionAt { + state.Last = state.SessionAt + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising history since last disconnect at %v up until session start at %v: %v", self.key.Log(), state.LastSeenAt, state.SessionAt, state) + // blocks until state syncing is finished + self.syncState(state) + } + glog.V(logger.Info).Infof("syncer[%v]: syncing all history complete", self.key.Log()) + +} + +// wait till syncronised block uptil state is synced +func (self *syncer) syncState(state *syncState) { + self.syncStates <- state + select { + case <-state.synced: + case <-self.quit: + } +} + +// stop quits both request processor and saves the request cache to disk +func (self *syncer) stop() { + close(self.quit) + glog.V(logger.Detail).Infof("syncer[%v]: stop and save sync request db backlog", self.key.Log()) + for _, db := range self.queues { + db.stop() + } +} + +// rlp serialisable sync request +type syncRequest struct { + Key storage.Key + Priority uint +} + +func (self *syncRequest) String() string { + return fmt.Sprintf("", self.Key.Log(), self.Priority) +} + +func (self *syncer) newSyncRequest(req interface{}, p int) (*syncRequest, error) { + key, _, _, _, err := parseRequest(req) + // TODO: if req has chunk, it should be put in a cache + // create + if err != nil { + return nil, err + } + return &syncRequest{key, uint(p)}, nil +} + +// serves historical items from the DB +// * read is on demand, blocking unless history channel is read +// * accepts sync requests (syncStates) to create new db iterator +// * closes the channel one iteration finishes +func (self *syncer) syncHistory(state *syncState) chan interface{} { + var n uint + history := make(chan interface{}) + glog.V(logger.Debug).Infof("syncer[%v]: syncing history between %v - %v for chunk addresses %v - %v", self.key.Log(), state.First, state.Last, state.Start, state.Stop) + it := self.dbAccess.iterator(state) + if it != nil { + go func() { + // signal end of the iteration ended + defer close(history) + IT: + for { + key := it.Next() + if key == nil { + break IT + } + select { + // blocking until history channel is read from + case history <- storage.Key(key): + n++ + glog.V(logger.Detail).Infof("syncer[%v]: history: %v (%v keys)", self.key.Log(), key.Log(), n) + state.Latest = key + case <-self.quit: + return + } + } + glog.V(logger.Debug).Infof("syncer[%v]: finished syncing history between %v - %v for chunk addresses %v - %v (at %v) (chunks = %v)", self.key.Log(), state.First, state.Last, state.Start, state.Stop, state.Latest, n) + }() + } + return history +} + +// triggers key syncronisation +func (self *syncer) sendUnsyncedKeys() { + select { + case self.deliveryRequest <- true: + default: + } +} + +// assembles a new batch of unsynced keys +// * keys are drawn from the key buffers in order of priority queue +// * if the queues of priority for History (HistoryReq) or higher are depleted, +// historical data is used so historical items are lower priority within +// their priority group. +// * Order of historical data is unspecified +func (self *syncer) syncUnsyncedKeys() { + // send out new + var unsynced []*syncRequest + var more, justSynced bool + var keyCount, historyCnt int + var history chan interface{} + + priority := High + keys := self.keys[priority] + var newUnsyncedKeys, deliveryRequest chan bool + keyCounts := make([]int, priorities) + histPrior := self.SyncPriorities[HistoryReq] + syncStates := self.syncStates + state := self.state + +LOOP: + for { + + var req interface{} + // select the highest priority channel to read from + // keys channels are buffered so the highest priority ones + // are checked first - integrity can only be guaranteed if writing + // is locked while selecting + if priority != High || len(keys) == 0 { + // selection is not needed if the High priority queue has items + keys = nil + PRIORITIES: + for priority = High; priority >= 0; priority-- { + // the first priority channel that is non-empty will be assigned to keys + if len(self.keys[priority]) > 0 { + glog.V(logger.Detail).Infof("syncer[%v]: reading request with priority %v", self.key.Log(), priority) + keys = self.keys[priority] + break PRIORITIES + } + glog.V(logger.Detail).Infof("syncer[%v/%v]: queue: [%v, %v, %v]", self.key.Log(), priority, len(self.keys[High]), len(self.keys[Medium]), len(self.keys[Low])) + // if the input queue is empty on this level, resort to history if there is any + if uint(priority) == histPrior && history != nil { + glog.V(logger.Detail).Infof("syncer[%v]: reading history for %v", self.key.Log(), self.key) + keys = history + break PRIORITIES + } + } + } + + // if peer ready to receive but nothing to send + if keys == nil && deliveryRequest == nil { + // if no items left and switch to waiting mode + glog.V(logger.Detail).Infof("syncer[%v]: buffers consumed. Waiting", self.key.Log()) + newUnsyncedKeys = self.newUnsyncedKeys + } + + // send msg iff + // * peer is ready to receive keys AND ( + // * all queues and history are depleted OR + // * batch full OR + // * all history have been consumed, synced) + if deliveryRequest == nil && + (justSynced || + len(unsynced) > 0 && keys == nil || + len(unsynced) == int(self.SyncBatchSize)) { + justSynced = false + // listen to requests + deliveryRequest = self.deliveryRequest + newUnsyncedKeys = nil // not care about data until next req comes in + // set sync to current counter + // (all nonhistorical outgoing traffic sheduled and persisted + state.LastSeenAt = self.dbAccess.counter() + state.Latest = storage.ZeroKey + glog.V(logger.Detail).Infof("syncer[%v]: sending %v", self.key.Log(), unsynced) + // send the unsynced keys + stateCopy := *state + err := self.unsyncedKeys(unsynced, &stateCopy) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: unable to send unsynced keys: %v", err) + } + self.state = state + glog.V(logger.Debug).Infof("syncer[%v]: --> %v keys sent: (total: %v (%v), history: %v), sent sync state: %v", self.key.Log(), len(unsynced), keyCounts, keyCount, historyCnt, stateCopy) + unsynced = nil + keys = nil + } + + // process item and add it to the batch + select { + case <-self.quit: + break LOOP + case req, more = <-keys: + if keys == history && !more { + glog.V(logger.Detail).Infof("syncer[%v]: syncing history segment complete", self.key.Log()) + // history channel is closed, waiting for new state (called from sync()) + syncStates = self.syncStates + state.Synced = true // this signals that the current segment is complete + select { + case state.synced <- false: + case <-self.quit: + break LOOP + } + justSynced = true + history = nil + } + case <-deliveryRequest: + glog.V(logger.Detail).Infof("syncer[%v]: peer ready to receive", self.key.Log()) + + // this 1 cap channel can wake up the loop + // signaling that peer is ready to receive unsynced Keys + // the channel is set to nil any further writes will be ignored + deliveryRequest = nil + + case <-newUnsyncedKeys: + glog.V(logger.Detail).Infof("syncer[%v]: new unsynced keys available", self.key.Log()) + // this 1 cap channel can wake up the loop + // signals that data is available to send if peer is ready to receive + newUnsyncedKeys = nil + keys = self.keys[High] + + case state, more = <-syncStates: + // this resets the state + if !more { + state = self.state + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) syncing complete upto %v)", self.key.Log(), priority, state) + state.Synced = true + syncStates = nil + } else { + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) syncing history upto %v priority %v)", self.key.Log(), priority, state, histPrior) + state.Synced = false + history = self.syncHistory(state) + // only one history at a time, only allow another one once the + // history channel is closed + syncStates = nil + } + } + if req == nil { + continue LOOP + } + + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) added to unsynced keys: %v", self.key.Log(), priority, req) + keyCounts[priority]++ + keyCount++ + if keys == history { + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) history item %v (synced = %v)", self.key.Log(), priority, req, state.Synced) + historyCnt++ + } + if sreq, err := self.newSyncRequest(req, priority); err == nil { + // extract key from req + glog.V(logger.Detail).Infof("syncer(priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced) + unsynced = append(unsynced, sreq) + } else { + glog.V(logger.Warn).Infof("syncer(priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, state.Synced, err) + } + + } +} + +// delivery loop +// takes into account priority, send store Requests with chunk (delivery) +// idle blocking if no new deliveries in any of the queues +func (self *syncer) syncDeliveries() { + var req *storeRequestMsgData + p := High + var deliveries chan *storeRequestMsgData + var msg *storeRequestMsgData + var err error + var c = [priorities]int{} + var n = [priorities]int{} + var total, success uint + + for { + deliveries = self.deliveries[p] + select { + case req = <-deliveries: + n[p]++ + c[p]++ + default: + if p == Low { + // blocking, depletion on all channels, no preference for priority + select { + case req = <-self.deliveries[High]: + n[High]++ + case req = <-self.deliveries[Medium]: + n[Medium]++ + case req = <-self.deliveries[Low]: + n[Low]++ + case <-self.quit: + return + } + p = High + } else { + p-- + continue + } + } + total++ + msg, err = self.newStoreRequestMsgData(req) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: failed to create store request for %v: %v", self.key.Log(), req, err) + } else { + err = self.store(msg) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: failed to deliver %v: %v", self.key.Log(), req, err) + } else { + success++ + glog.V(logger.Detail).Infof("syncer[%v]: %v successfully delivered", self.key.Log(), req) + } + } + if total%self.SyncBatchSize == 0 { + glog.V(logger.Debug).Infof("syncer[%v]: deliver Total: %v, Success: %v, High: %v/%v, Medium: %v/%v, Low %v/%v", self.key.Log(), total, success, c[High], n[High], c[Medium], n[Medium], c[Low], n[Low]) + } + } +} + +/* + addRequest handles requests for delivery + it accepts 4 types: + + * storeRequestMsgData: coming from netstore propagate response + * chunk: coming from forwarding (questionable: id?) + * key: from incoming syncRequest + * syncDbEntry: key,id encoded in db + + If sync mode is on for the type of request, then + it sends the request to the keys queue of the correct priority + channel buffered with capacity (SyncBufferSize) + + If sync mode is off then, requests are directly sent to deliveries +*/ +func (self *syncer) addRequest(req interface{}, ty int) { + // retrieve priority for request type name int8 + + priority := self.SyncPriorities[ty] + // sync mode for this type ON + if self.syncF() || ty == DeliverReq { + if self.SyncModes[ty] { + self.addKey(req, priority, self.quit) + } else { + self.addDelivery(req, priority, self.quit) + } + } +} + +// addKey queues sync request for sync confirmation with given priority +// ie the key will go out in an unsyncedKeys message +func (self *syncer) addKey(req interface{}, priority uint, quit chan bool) bool { + select { + case self.keys[priority] <- req: + // this wakes up the unsynced keys loop if idle + select { + case self.newUnsyncedKeys <- true: + default: + } + return true + case <-quit: + return false + } +} + +// addDelivery queues delivery request for with given priority +// ie the chunk will be delivered ASAP mod priority queueing handled by syncdb +// requests are persisted across sessions for correct sync +func (self *syncer) addDelivery(req interface{}, priority uint, quit chan bool) bool { + select { + case self.queues[priority].buffer <- req: + return true + case <-quit: + return false + } +} + +// doDelivery delivers the chunk for the request with given priority +// without queuing +func (self *syncer) doDelivery(req interface{}, priority uint, quit chan bool) bool { + msgdata, err := self.newStoreRequestMsgData(req) + if err != nil { + glog.V(logger.Warn).Infof("unable to deliver request %v: %v", msgdata, err) + return false + } + select { + case self.deliveries[priority] <- msgdata: + return true + case <-quit: + return false + } +} + +// returns the delivery function for given priority +// passed on to syncDb +func (self *syncer) deliver(priority uint) func(req interface{}, quit chan bool) bool { + return func(req interface{}, quit chan bool) bool { + return self.doDelivery(req, priority, quit) + } +} + +// returns the replay function passed on to syncDb +// depending on sync mode settings for BacklogReq, +// re play of request db backlog sends items via confirmation +// or directly delivers +func (self *syncer) replay() func(req interface{}, quit chan bool) bool { + sync := self.SyncModes[BacklogReq] + priority := self.SyncPriorities[BacklogReq] + // sync mode for this type ON + if sync { + return func(req interface{}, quit chan bool) bool { + return self.addKey(req, priority, quit) + } + } else { + return func(req interface{}, quit chan bool) bool { + return self.doDelivery(req, priority, quit) + } + + } +} + +// given a request, extends it to a full storeRequestMsgData +// polimorphic: see addRequest for the types accepted +func (self *syncer) newStoreRequestMsgData(req interface{}) (*storeRequestMsgData, error) { + + key, id, chunk, sreq, err := parseRequest(req) + if err != nil { + return nil, err + } + + if sreq == nil { + if chunk == nil { + var err error + chunk, err = self.dbAccess.get(key) + if err != nil { + return nil, err + } + } + + sreq = &storeRequestMsgData{ + Id: id, + Key: chunk.Key, + SData: chunk.SData, + } + } + + return sreq, nil +} + +// parse request types and extracts, key, id, chunk, request if available +// does not do chunk lookup ! +func parseRequest(req interface{}) (storage.Key, uint64, *storage.Chunk, *storeRequestMsgData, error) { + var key storage.Key + var entry *syncDbEntry + var chunk *storage.Chunk + var id uint64 + var ok bool + var sreq *storeRequestMsgData + var err error + + if key, ok = req.(storage.Key); ok { + id = generateId() + + } else if entry, ok = req.(*syncDbEntry); ok { + id = binary.BigEndian.Uint64(entry.val[32:]) + key = storage.Key(entry.val[:32]) + + } else if chunk, ok = req.(*storage.Chunk); ok { + key = chunk.Key + id = generateId() + + } else if sreq, ok = req.(*storeRequestMsgData); ok { + key = sreq.Key + } else { + err = fmt.Errorf("type not allowed: %v (%T)", req, req) + } + + return key, id, chunk, sreq, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go new file mode 100644 index 000000000..f72036d72 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go @@ -0,0 +1,283 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package swap + +import ( + "crypto/ecdsa" + "fmt" + "math/big" + "os" + "path/filepath" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "golang.org/x/net/context" +) + +// SwAP Swarm Accounting Protocol with +// SWAP^2 Strategies of Withholding Automatic Payments +// SWAP^3 Accreditation: payment via credit SWAP +// using chequebook pkg for delayed payments +// default parameters + +var ( + autoCashInterval = 300 * time.Second // default interval for autocash + autoCashThreshold = big.NewInt(50000000000000) // threshold that triggers autocash (wei) + autoDepositInterval = 300 * time.Second // default interval for autocash + autoDepositThreshold = big.NewInt(50000000000000) // threshold that triggers autodeposit (wei) + autoDepositBuffer = big.NewInt(100000000000000) // buffer that is surplus for fork protection etc (wei) + buyAt = big.NewInt(20000000000) // maximum chunk price host is willing to pay (wei) + sellAt = big.NewInt(20000000000) // minimum chunk price host requires (wei) + payAt = 100 // threshold that triggers payment {request} (units) + dropAt = 10000 // threshold that triggers disconnect (units) +) + +const ( + chequebookDeployRetries = 5 + chequebookDeployDelay = 1 * time.Second // delay between retries +) + +type SwapParams struct { + *swap.Params + *PayProfile +} + +type SwapProfile struct { + *swap.Profile + *PayProfile +} + +type PayProfile struct { + PublicKey string // check against signature of promise + Contract common.Address // address of chequebook contract + Beneficiary common.Address // recipient address for swarm sales revenue + privateKey *ecdsa.PrivateKey + publicKey *ecdsa.PublicKey + owner common.Address + chbook *chequebook.Chequebook + lock sync.RWMutex +} + +func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapParams { + pubkey := &prvkey.PublicKey + return &SwapParams{ + PayProfile: &PayProfile{ + PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)), + Contract: contract, + Beneficiary: crypto.PubkeyToAddress(*pubkey), + privateKey: prvkey, + publicKey: pubkey, + owner: crypto.PubkeyToAddress(*pubkey), + }, + Params: &swap.Params{ + Profile: &swap.Profile{ + BuyAt: buyAt, + SellAt: sellAt, + PayAt: uint(payAt), + DropAt: uint(dropAt), + }, + Strategy: &swap.Strategy{ + AutoCashInterval: autoCashInterval, + AutoCashThreshold: autoCashThreshold, + AutoDepositInterval: autoDepositInterval, + AutoDepositThreshold: autoDepositThreshold, + AutoDepositBuffer: autoDepositBuffer, + }, + }, + } +} + +// swap constructor, parameters +// * global chequebook, assume deployed service and +// * the balance is at buffer. +// swap.Add(n) called in netstore +// n > 0 called when sending chunks = receiving retrieve requests +// OR sending cheques. +// n < 0 called when receiving chunks = receiving delivery responses +// OR receiving cheques. + +func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend, proto swap.Protocol) (self *swap.Swap, err error) { + var ( + ctx = context.TODO() + ok bool + in *chequebook.Inbox + out *chequebook.Outbox + ) + + // check if remote chequebook is valid + // insolvent chequebooks suicide so will signal as invalid + // TODO: monitoring a chequebooks events + ok, err = chequebook.ValidateCode(ctx, backend, remote.Contract) + if !ok { + glog.V(logger.Info).Infof("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) + } else { + // remote contract valid, create inbox + in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend) + if err != nil { + glog.V(logger.Warn).Infof("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) + } + } + + // check if local chequebook contract is valid + ok, err = chequebook.ValidateCode(ctx, backend, local.Contract) + if !ok { + glog.V(logger.Warn).Infof("unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, local.owner.Hex(), err) + } else { + out = chequebook.NewOutbox(local.Chequebook(), remote.Beneficiary) + } + + pm := swap.Payment{ + In: in, + Out: out, + Buys: out != nil, + Sells: in != nil, + } + self, err = swap.New(local.Params, pm, proto) + if err != nil { + return + } + // remote profile given (first) in handshake + self.SetRemote(remote.Profile) + var buy, sell string + if self.Buys { + buy = "purchase from peer enabled at " + remote.SellAt.String() + " wei/chunk" + } else { + buy = "purchase from peer disabled" + } + if self.Sells { + sell = "selling to peer enabled at " + local.SellAt.String() + " wei/chunk" + } else { + sell = "selling to peer disabled" + } + glog.V(logger.Warn).Infof("SWAP arrangement with <%v>: %v; %v)", proto, buy, sell) + + return +} + +func (self *SwapParams) Chequebook() *chequebook.Chequebook { + defer self.lock.Unlock() + self.lock.Lock() + return self.chbook +} + +func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey { + return self.privateKey +} + +// func (self *SwapParams) PublicKey() *ecdsa.PublicKey { +// return self.publicKey +// } + +func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) { + self.privateKey = prvkey + self.publicKey = &prvkey.PublicKey +} + +// setChequebook(path, backend) wraps the +// chequebook initialiser and sets up autoDeposit to cover spending. +func (self *SwapParams) SetChequebook(ctx context.Context, backend chequebook.Backend, path string) error { + self.lock.Lock() + contract := self.Contract + self.lock.Unlock() + + valid, err := chequebook.ValidateCode(ctx, backend, contract) + if err != nil { + return err + } else if valid { + return self.newChequebookFromContract(path, backend) + } + return self.deployChequebook(ctx, backend, path) +} + +func (self *SwapParams) deployChequebook(ctx context.Context, backend chequebook.Backend, path string) error { + opts := bind.NewKeyedTransactor(self.privateKey) + opts.Value = self.AutoDepositBuffer + opts.Context = ctx + + glog.V(logger.Info).Infof("Deploying new chequebook (owner: %v)", opts.From.Hex()) + contract, err := deployChequebookLoop(opts, backend) + if err != nil { + glog.V(logger.Error).Infof("unable to deploy new chequebook: %v", err) + return err + } + glog.V(logger.Info).Infof("new chequebook deployed at %v (owner: %v)", contract.Hex(), opts.From.Hex()) + + // need to save config at this point + self.lock.Lock() + self.Contract = contract + err = self.newChequebookFromContract(path, backend) + self.lock.Unlock() + if err != nil { + glog.V(logger.Warn).Infof("error initialising cheque book (owner: %v): %v", opts.From.Hex(), err) + } + return err +} + +// repeatedly tries to deploy a chequebook. +func deployChequebookLoop(opts *bind.TransactOpts, backend chequebook.Backend) (addr common.Address, err error) { + var tx *types.Transaction + for try := 0; try < chequebookDeployRetries; try++ { + if try > 0 { + time.Sleep(chequebookDeployDelay) + } + if _, tx, _, err = contract.DeployChequebook(opts, backend); err != nil { + glog.V(logger.Warn).Infof("can't send chequebook deploy tx (try %d): %v", try, err) + continue + } + if addr, err = bind.WaitDeployed(opts.Context, backend, tx); err != nil { + glog.V(logger.Warn).Infof("chequebook deploy error (try %d): %v", try, err) + continue + } + return addr, nil + } + return addr, err +} + +// initialise the chequebook from a persisted json file or create a new one +// caller holds the lock +func (self *SwapParams) newChequebookFromContract(path string, backend chequebook.Backend) error { + hexkey := common.Bytes2Hex(self.Contract.Bytes()) + err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm) + if err != nil { + return fmt.Errorf("unable to create directory for chequebooks: %v", err) + } + + chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") + self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend, true) + + if err != nil { + self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend) + if err != nil { + glog.V(logger.Warn).Infof("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) + return fmt.Errorf("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) + } + } + + self.chbook.AutoDeposit(self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) + glog.V(logger.Info).Infof("auto deposit ON for %v -> %v: interval = %v, threshold = %v, buffer = %v)", crypto.PubkeyToAddress(*(self.publicKey)).Hex()[:8], self.Contract.Hex()[:8], self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go new file mode 100644 index 000000000..9d5da7c3a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go @@ -0,0 +1,254 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package swap + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// SwAP Swarm Accounting Protocol with +// Swift Automatic Payments +// a peer to peer micropayment system + +// public swap profile +// public parameters for SWAP, serializable config struct passed in handshake +type Profile struct { + BuyAt *big.Int // accepted max price for chunk + SellAt *big.Int // offered sale price for chunk + PayAt uint // threshold that triggers payment request + DropAt uint // threshold that triggers disconnect +} + +// Strategy encapsulates parameters relating to +// automatic deposit and automatic cashing +type Strategy struct { + AutoCashInterval time.Duration // default interval for autocash + AutoCashThreshold *big.Int // threshold that triggers autocash (wei) + AutoDepositInterval time.Duration // default interval for autocash + AutoDepositThreshold *big.Int // threshold that triggers autodeposit (wei) + AutoDepositBuffer *big.Int // buffer that is surplus for fork protection etc (wei) +} + +// Params extends the public profile with private parameters relating to +// automatic deposit and automatic cashing +type Params struct { + *Profile + *Strategy +} + +// Promise +// 3rd party Provable Promise of Payment +// issued by outPayment +// serialisable to send with Protocol +type Promise interface{} + +// interface for the peer protocol for testing or external alternative payment +type Protocol interface { + Pay(int, Promise) // units, payment proof + Drop() + String() string +} + +// interface for the (delayed) ougoing payment system with autodeposit +type OutPayment interface { + Issue(amount *big.Int) (promise Promise, err error) + AutoDeposit(interval time.Duration, threshold, buffer *big.Int) + Stop() +} + +// interface for the (delayed) incoming payment system with autocash +type InPayment interface { + Receive(promise Promise) (*big.Int, error) + AutoCash(cashInterval time.Duration, maxUncashed *big.Int) + Stop() +} + +// swap is the swarm accounting protocol instance +// * pairwise accounting and payments +type Swap struct { + lock sync.Mutex // mutex for balance access + balance int // units of chunk/retrieval request + local *Params // local peer's swap parameters + remote *Profile // remote peer's swap profile + proto Protocol // peer communication protocol + Payment +} + +type Payment struct { + Out OutPayment // outgoing payment handler + In InPayment // incoming payment handler + Buys, Sells bool +} + +// swap constructor +func New(local *Params, pm Payment, proto Protocol) (self *Swap, err error) { + + self = &Swap{ + local: local, + Payment: pm, + proto: proto, + } + + self.SetParams(local) + + return +} + +// entry point for setting remote swap profile (e.g from handshake or other message) +func (self *Swap) SetRemote(remote *Profile) { + defer self.lock.Unlock() + self.lock.Lock() + + self.remote = remote + if self.Sells && (remote.BuyAt.Cmp(common.Big0) <= 0 || self.local.SellAt.Cmp(common.Big0) <= 0 || remote.BuyAt.Cmp(self.local.SellAt) < 0) { + self.Out.Stop() + self.Sells = false + } + if self.Buys && (remote.SellAt.Cmp(common.Big0) <= 0 || self.local.BuyAt.Cmp(common.Big0) <= 0 || self.local.BuyAt.Cmp(self.remote.SellAt) < 0) { + self.In.Stop() + self.Buys = false + } + + glog.V(logger.Debug).Infof("<%v> remote profile set: pay at: %v, drop at: %v, buy at: %v, sell at: %v", self.proto, remote.PayAt, remote.DropAt, remote.BuyAt, remote.SellAt) + +} + +// to set strategy dynamically +func (self *Swap) SetParams(local *Params) { + defer self.lock.Unlock() + self.lock.Lock() + self.local = local + self.setParams(local) +} + +// caller holds the lock + +func (self *Swap) setParams(local *Params) { + + if self.Sells { + self.In.AutoCash(local.AutoCashInterval, local.AutoCashThreshold) + glog.V(logger.Info).Infof("<%v> set autocash to every %v, max uncashed limit: %v", self.proto, local.AutoCashInterval, local.AutoCashThreshold) + } else { + glog.V(logger.Info).Infof("<%v> autocash off (not selling)", self.proto) + } + if self.Buys { + self.Out.AutoDeposit(local.AutoDepositInterval, local.AutoDepositThreshold, local.AutoDepositBuffer) + glog.V(logger.Info).Infof("<%v> set autodeposit to every %v, pay at: %v, buffer: %v", self.proto, local.AutoDepositInterval, local.AutoDepositThreshold, local.AutoDepositBuffer) + } else { + glog.V(logger.Info).Infof("<%v> autodeposit off (not buying)", self.proto) + } +} + +// Add(n) +// n > 0 called when promised/provided n units of service +// n < 0 called when used/requested n units of service +func (self *Swap) Add(n int) error { + defer self.lock.Unlock() + self.lock.Lock() + self.balance += n + if !self.Sells && self.balance > 0 { + glog.V(logger.Detail).Infof("<%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance) + self.proto.Drop() + return fmt.Errorf("[SWAP] <%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance) + } + if !self.Buys && self.balance < 0 { + glog.V(logger.Detail).Infof("<%v> we cannot have debt (balance: %v)", self.proto, self.balance) + return fmt.Errorf("[SWAP] <%v> we cannot have debt (balance: %v)", self.proto, self.balance) + } + if self.balance >= int(self.local.DropAt) { + glog.V(logger.Detail).Infof("<%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt) + self.proto.Drop() + return fmt.Errorf("[SWAP] <%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt) + } else if self.balance <= -int(self.remote.PayAt) { + self.send() + } + return nil +} + +func (self *Swap) Balance() int { + defer self.lock.Unlock() + self.lock.Lock() + return self.balance +} + +// send(units) is called when payment is due +// In case of insolvency no promise is issued and sent, safe against fraud +// No return value: no error = payment is opportunistic = hang in till dropped +func (self *Swap) send() { + if self.local.BuyAt != nil && self.balance < 0 { + amount := big.NewInt(int64(-self.balance)) + amount.Mul(amount, self.remote.SellAt) + promise, err := self.Out.Issue(amount) + if err != nil { + glog.V(logger.Warn).Infof("<%v> cannot issue cheque (amount: %v, channel: %v): %v", self.proto, amount, self.Out, err) + } else { + glog.V(logger.Warn).Infof("<%v> cheque issued (amount: %v, channel: %v)", self.proto, amount, self.Out) + self.proto.Pay(-self.balance, promise) + self.balance = 0 + } + } +} + +// receive(units, promise) is called by the protocol when a payment msg is received +// returns error if promise is invalid. +func (self *Swap) Receive(units int, promise Promise) error { + if units <= 0 { + return fmt.Errorf("invalid units: %v <= 0", units) + } + + price := new(big.Int).SetInt64(int64(units)) + price.Mul(price, self.local.SellAt) + + amount, err := self.In.Receive(promise) + + if err != nil { + err = fmt.Errorf("invalid promise: %v", err) + } else if price.Cmp(amount) != 0 { + // verify amount = units * unit sale price + return fmt.Errorf("invalid amount: %v = %v * %v (units sent in msg * agreed sale unit price) != %v (signed in cheque)", price, units, self.local.SellAt, amount) + } + if err != nil { + glog.V(logger.Detail).Infof("<%v> invalid promise (amount: %v, channel: %v): %v", self.proto, amount, self.In, err) + return err + } + + // credit remote peer with units + self.Add(-units) + glog.V(logger.Detail).Infof("<%v> received promise (amount: %v, channel: %v): %v", self.proto, amount, self.In, promise) + + return nil +} + +// stop() causes autocash loop to terminate. +// Called after protocol handle loop terminates. +func (self *Swap) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.Buys { + self.Out.Stop() + } + if self.Sells { + self.In.Stop() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go new file mode 100644 index 000000000..c0f950de5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go @@ -0,0 +1,497 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + "sync" +) + +/* +The distributed storage implemented in this package requires fix sized chunks of content. + +Chunker is the interface to a component that is responsible for disassembling and assembling larger data. + +TreeChunker implements a Chunker based on a tree structure defined as follows: + +1 each node in the tree including the root and other branching nodes are stored as a chunk. + +2 branching nodes encode data contents that includes the size of the dataslice covered by its entire subtree under the node as well as the hash keys of all its children : +data_{i} := size(subtree_{i}) || key_{j} || key_{j+1} .... || key_{j+n-1} + +3 Leaf nodes encode an actual subslice of the input data. + +4 if data size is not more than maximum chunksize, the data is stored in a single chunk + key = hash(int64(size) + data) + +5 if data size is more than chunksize*branches^l, but no more than chunksize* + branches^(l+1), the data vector is split into slices of chunksize* + branches^l length (except the last one). + key = hash(int64(size) + key(slice0) + key(slice1) + ...) + + The underlying hash function is configurable +*/ + +const ( + defaultHash = "SHA3" // http://golang.org/pkg/hash/#Hash + // defaultHash = "SHA256" // http://golang.org/pkg/hash/#Hash + defaultBranches int64 = 128 + // hashSize int64 = hasherfunc.New().Size() // hasher knows about its own length in bytes + // chunksize int64 = branches * hashSize // chunk is defined as this +) + +/* +Tree chunker is a concrete implementation of data chunking. +This chunker works in a simple way, it builds a tree out of the document so that each node either represents a chunk of real data or a chunk of data representing an branching non-leaf node of the tree. In particular each such non-leaf chunk will represent is a concatenation of the hash of its respective children. This scheme simultaneously guarantees data integrity as well as self addressing. Abstract nodes are transparent since their represented size component is strictly greater than their maximum data size, since they encode a subtree. + +If all is well it is possible to implement this by simply composing readers so that no extra allocation or buffering is necessary for the data splitting and joining. This means that in principle there can be direct IO between : memory, file system, network socket (bzz peers storage request is read from the socket). In practice there may be need for several stages of internal buffering. +The hashing itself does use extra copies and allocation though, since it does need it. +*/ + +type ChunkerParams struct { + Branches int64 + Hash string +} + +func NewChunkerParams() *ChunkerParams { + return &ChunkerParams{ + Branches: defaultBranches, + Hash: defaultHash, + } +} + +type TreeChunker struct { + branches int64 + hashFunc Hasher + // calculated + hashSize int64 // self.hashFunc.New().Size() + chunkSize int64 // hashSize* branches + workerCount int +} + +func NewTreeChunker(params *ChunkerParams) (self *TreeChunker) { + self = &TreeChunker{} + self.hashFunc = MakeHashFunc(params.Hash) + self.branches = params.Branches + self.hashSize = int64(self.hashFunc().Size()) + self.chunkSize = self.hashSize * self.branches + self.workerCount = 1 + return +} + +// func (self *TreeChunker) KeySize() int64 { +// return self.hashSize +// } + +// String() for pretty printing +func (self *Chunk) String() string { + return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v", self.Key.Log(), self.Size, len(self.SData)) +} + +type hashJob struct { + key Key + chunk []byte + size int64 + parentWg *sync.WaitGroup +} + +func (self *TreeChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) { + + if self.chunkSize <= 0 { + panic("chunker must be initialised") + } + + jobC := make(chan *hashJob, 2*processors) + wg := &sync.WaitGroup{} + errC := make(chan error) + quitC := make(chan bool) + + // wwg = workers waitgroup keeps track of hashworkers spawned by this split call + if wwg != nil { + wwg.Add(1) + } + go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg) + + depth := 0 + treeSize := self.chunkSize + + // takes lowest depth such that chunksize*HashCount^(depth+1) > size + // power series, will find the order of magnitude of the data size in base hashCount or numbers of levels of branching in the resulting tree. + for ; treeSize < size; treeSize *= self.branches { + depth++ + } + + key := make([]byte, self.hashFunc().Size()) + // this waitgroup member is released after the root hash is calculated + wg.Add(1) + //launch actual recursive function passing the waitgroups + go self.split(depth, treeSize/self.branches, key, data, size, jobC, chunkC, errC, quitC, wg, swg, wwg) + + // closes internal error channel if all subprocesses in the workgroup finished + go func() { + // waiting for all threads to finish + wg.Wait() + // if storage waitgroup is non-nil, we wait for storage to finish too + if swg != nil { + swg.Wait() + } + close(errC) + }() + + select { + case err := <-errC: + if err != nil { + close(quitC) + return nil, err + } + //TODO: add a timeout + } + return key, nil +} + +func (self *TreeChunker) split(depth int, treeSize int64, key Key, data io.Reader, size int64, jobC chan *hashJob, chunkC chan *Chunk, errC chan error, quitC chan bool, parentWg, swg, wwg *sync.WaitGroup) { + + for depth > 0 && size < treeSize { + treeSize /= self.branches + depth-- + } + + if depth == 0 { + // leaf nodes -> content chunks + chunkData := make([]byte, size+8) + binary.LittleEndian.PutUint64(chunkData[0:8], uint64(size)) + var readBytes int64 + for readBytes < size { + n, err := data.Read(chunkData[8+readBytes:]) + readBytes += int64(n) + if err != nil && !(err == io.EOF && readBytes == size) { + errC <- err + return + } + } + select { + case jobC <- &hashJob{key, chunkData, size, parentWg}: + case <-quitC: + } + return + } + // dept > 0 + // intermediate chunk containing child nodes hashes + branchCnt := int64((size + treeSize - 1) / treeSize) + + var chunk []byte = make([]byte, branchCnt*self.hashSize+8) + var pos, i int64 + + binary.LittleEndian.PutUint64(chunk[0:8], uint64(size)) + + childrenWg := &sync.WaitGroup{} + var secSize int64 + for i < branchCnt { + // the last item can have shorter data + if size-pos < treeSize { + secSize = size - pos + } else { + secSize = treeSize + } + // the hash of that data + subTreeKey := chunk[8+i*self.hashSize : 8+(i+1)*self.hashSize] + + childrenWg.Add(1) + self.split(depth-1, treeSize/self.branches, subTreeKey, data, secSize, jobC, chunkC, errC, quitC, childrenWg, swg, wwg) + + i++ + pos += treeSize + } + // wait for all the children to complete calculating their hashes and copying them onto sections of the chunk + // parentWg.Add(1) + // go func() { + childrenWg.Wait() + if len(jobC) > self.workerCount && self.workerCount < processors { + if wwg != nil { + wwg.Add(1) + } + self.workerCount++ + go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg) + } + select { + case jobC <- &hashJob{key, chunk, size, parentWg}: + case <-quitC: + } +} + +func (self *TreeChunker) hashWorker(jobC chan *hashJob, chunkC chan *Chunk, errC chan error, quitC chan bool, swg, wwg *sync.WaitGroup) { + hasher := self.hashFunc() + if wwg != nil { + defer wwg.Done() + } + for { + select { + + case job, ok := <-jobC: + if !ok { + return + } + // now we got the hashes in the chunk, then hash the chunks + hasher.Reset() + self.hashChunk(hasher, job, chunkC, swg) + case <-quitC: + return + } + } +} + +// The treeChunkers own Hash hashes together +// - the size (of the subtree encoded in the Chunk) +// - the Chunk, ie. the contents read from the input reader +func (self *TreeChunker) hashChunk(hasher hash.Hash, job *hashJob, chunkC chan *Chunk, swg *sync.WaitGroup) { + hasher.Write(job.chunk) + h := hasher.Sum(nil) + newChunk := &Chunk{ + Key: h, + SData: job.chunk, + Size: job.size, + wg: swg, + } + + // report hash of this chunk one level up (keys corresponds to the proper subslice of the parent chunk) + copy(job.key, h) + // send off new chunk to storage + if chunkC != nil { + if swg != nil { + swg.Add(1) + } + } + job.parentWg.Done() + + if chunkC != nil { + chunkC <- newChunk + } +} + +// LazyChunkReader implements LazySectionReader +type LazyChunkReader struct { + key Key // root key + chunkC chan *Chunk // chunk channel to send retrieve requests on + chunk *Chunk // size of the entire subtree + off int64 // offset + chunkSize int64 // inherit from chunker + branches int64 // inherit from chunker + hashSize int64 // inherit from chunker +} + +// implements the Joiner interface +func (self *TreeChunker) Join(key Key, chunkC chan *Chunk) LazySectionReader { + + return &LazyChunkReader{ + key: key, + chunkC: chunkC, + chunkSize: self.chunkSize, + branches: self.branches, + hashSize: self.hashSize, + } +} + +// Size is meant to be called on the LazySectionReader +func (self *LazyChunkReader) Size(quitC chan bool) (n int64, err error) { + if self.chunk != nil { + return self.chunk.Size, nil + } + chunk := retrieve(self.key, self.chunkC, quitC) + if chunk == nil { + select { + case <-quitC: + return 0, errors.New("aborted") + default: + return 0, fmt.Errorf("root chunk not found for %v", self.key.Hex()) + } + } + self.chunk = chunk + return chunk.Size, nil +} + +// read at can be called numerous times +// concurrent reads are allowed +// Size() needs to be called synchronously on the LazyChunkReader first +func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) { + // this is correct, a swarm doc cannot be zero length, so no EOF is expected + if len(b) == 0 { + return 0, nil + } + quitC := make(chan bool) + size, err := self.Size(quitC) + if err != nil { + return 0, err + } + + errC := make(chan error) + + // } + var treeSize int64 + var depth int + // calculate depth and max treeSize + treeSize = self.chunkSize + for ; treeSize < size; treeSize *= self.branches { + depth++ + } + wg := sync.WaitGroup{} + wg.Add(1) + go self.join(b, off, off+int64(len(b)), depth, treeSize/self.branches, self.chunk, &wg, errC, quitC) + go func() { + wg.Wait() + close(errC) + }() + + err = <-errC + if err != nil { + close(quitC) + + return 0, err + } + if off+int64(len(b)) >= size { + return len(b), io.EOF + } + return len(b), nil +} + +func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, treeSize int64, chunk *Chunk, parentWg *sync.WaitGroup, errC chan error, quitC chan bool) { + defer parentWg.Done() + // return NewDPA(&LocalStore{}) + + // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + // find appropriate block level + for chunk.Size < treeSize && depth > 0 { + treeSize /= self.branches + depth-- + } + + // leaf chunk found + if depth == 0 { + extra := 8 + eoff - int64(len(chunk.SData)) + if extra > 0 { + eoff -= extra + } + copy(b, chunk.SData[8+off:8+eoff]) + return // simply give back the chunks reader for content chunks + } + + // subtree + start := off / treeSize + end := (eoff + treeSize - 1) / treeSize + + wg := &sync.WaitGroup{} + defer wg.Wait() + + for i := start; i < end; i++ { + soff := i * treeSize + roff := soff + seoff := soff + treeSize + + if soff < off { + soff = off + } + if seoff > eoff { + seoff = eoff + } + if depth > 1 { + wg.Wait() + } + wg.Add(1) + go func(j int64) { + childKey := chunk.SData[8+j*self.hashSize : 8+(j+1)*self.hashSize] + chunk := retrieve(childKey, self.chunkC, quitC) + if chunk == nil { + select { + case errC <- fmt.Errorf("chunk %v-%v not found", off, off+treeSize): + case <-quitC: + } + return + } + if soff < off { + soff = off + } + self.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/self.branches, chunk, wg, errC, quitC) + }(i) + } //for +} + +// the helper method submits chunks for a key to a oueue (DPA) and +// block until they time out or arrive +// abort if quitC is readable +func retrieve(key Key, chunkC chan *Chunk, quitC chan bool) *Chunk { + chunk := &Chunk{ + Key: key, + C: make(chan bool), // close channel to signal data delivery + } + // submit chunk for retrieval + select { + case chunkC <- chunk: // submit retrieval request, someone should be listening on the other side (or we will time out globally) + case <-quitC: + return nil + } + // waiting for the chunk retrieval + select { // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + case <-quitC: + // this is how we control process leakage (quitC is closed once join is finished (after timeout)) + return nil + case <-chunk.C: // bells are ringing, data have been delivered + } + if len(chunk.SData) == 0 { + return nil // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + } + return chunk +} + +// Read keeps a cursor so cannot be called simulateously, see ReadAt +func (self *LazyChunkReader) Read(b []byte) (read int, err error) { + read, err = self.ReadAt(b, self.off) + + self.off += int64(read) + return +} + +// completely analogous to standard SectionReader implementation +var errWhence = errors.New("Seek: invalid whence") +var errOffset = errors.New("Seek: invalid offset") + +func (s *LazyChunkReader) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, errWhence + case 0: + offset += 0 + case 1: + offset += s.off + case 2: + if s.chunk == nil { + return 0, fmt.Errorf("seek from the end requires rootchunk for size. call Size first") + } + offset += s.chunk.Size + } + + if offset < 0 { + return 0, errOffset + } + s.off = offset + return offset, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go new file mode 100644 index 000000000..2532490cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go @@ -0,0 +1,99 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +// this is a clone of an earlier state of the ethereum ethdb/database +// no need for queueing/caching + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/compression/rle" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" +) + +const openFileLimit = 128 + +type LDBDatabase struct { + db *leveldb.DB + comp bool +} + +func NewLDBDatabase(file string) (*LDBDatabase, error) { + // Open the db + db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: openFileLimit}) + if err != nil { + return nil, err + } + + database := &LDBDatabase{db: db, comp: false} + + return database, nil +} + +func (self *LDBDatabase) Put(key []byte, value []byte) { + if self.comp { + value = rle.Compress(value) + } + + err := self.db.Put(key, value, nil) + if err != nil { + fmt.Println("Error put", err) + } +} + +func (self *LDBDatabase) Get(key []byte) ([]byte, error) { + dat, err := self.db.Get(key, nil) + if err != nil { + return nil, err + } + + if self.comp { + return rle.Decompress(dat) + } + + return dat, nil +} + +func (self *LDBDatabase) Delete(key []byte) error { + return self.db.Delete(key, nil) +} + +func (self *LDBDatabase) LastKnownTD() []byte { + data, _ := self.Get([]byte("LTD")) + + if len(data) == 0 { + data = []byte{0x0} + } + + return data +} + +func (self *LDBDatabase) NewIterator() iterator.Iterator { + return self.db.NewIterator(nil, nil) +} + +func (self *LDBDatabase) Write(batch *leveldb.Batch) error { + return self.db.Write(batch, nil) +} + +func (self *LDBDatabase) Close() { + // Close the leveldb database + self.db.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go new file mode 100644 index 000000000..5ecc5c500 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go @@ -0,0 +1,473 @@ +// 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 . + +// disk storage layer for the package bzz +// DbStore implements the ChunkStore interface and is used by the DPA as +// persistent storage of chunks +// it implements purging based on access count allowing for external control of +// max capacity + +package storage + +import ( + "bytes" + "encoding/binary" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" +) + +const ( + defaultDbCapacity = 5000000 + defaultRadius = 0 // not yet used + + gcArraySize = 10000 + gcArrayFreeRatio = 0.1 + + // key prefixes for leveldb storage + kpIndex = 0 + kpData = 1 +) + +var ( + keyAccessCnt = []byte{2} + keyEntryCnt = []byte{3} + keyDataIdx = []byte{4} + keyGCPos = []byte{5} +) + +type gcItem struct { + idx uint64 + value uint64 + idxKey []byte +} + +type DbStore struct { + db *LDBDatabase + + // this should be stored in db, accessed transactionally + entryCnt, accessCnt, dataIdx, capacity uint64 + + gcPos, gcStartPos []byte + gcArray []*gcItem + + hashfunc Hasher + + lock sync.Mutex +} + +func NewDbStore(path string, hash Hasher, capacity uint64, radius int) (s *DbStore, err error) { + s = new(DbStore) + + s.hashfunc = hash + + s.db, err = NewLDBDatabase(path) + if err != nil { + return + } + + s.setCapacity(capacity) + + s.gcStartPos = make([]byte, 1) + s.gcStartPos[0] = kpIndex + s.gcArray = make([]*gcItem, gcArraySize) + + data, _ := s.db.Get(keyEntryCnt) + s.entryCnt = BytesToU64(data) + data, _ = s.db.Get(keyAccessCnt) + s.accessCnt = BytesToU64(data) + data, _ = s.db.Get(keyDataIdx) + s.dataIdx = BytesToU64(data) + s.gcPos, _ = s.db.Get(keyGCPos) + if s.gcPos == nil { + s.gcPos = s.gcStartPos + } + return +} + +type dpaDBIndex struct { + Idx uint64 + Access uint64 +} + +func BytesToU64(data []byte) uint64 { + if len(data) < 8 { + return 0 + } + return binary.LittleEndian.Uint64(data) +} + +func U64ToBytes(val uint64) []byte { + data := make([]byte, 8) + binary.LittleEndian.PutUint64(data, val) + return data +} + +func getIndexGCValue(index *dpaDBIndex) uint64 { + return index.Access +} + +func (s *DbStore) updateIndexAccess(index *dpaDBIndex) { + index.Access = s.accessCnt +} + +func getIndexKey(hash Key) []byte { + HashSize := len(hash) + key := make([]byte, HashSize+1) + key[0] = 0 + copy(key[1:], hash[:]) + return key +} + +func getDataKey(idx uint64) []byte { + key := make([]byte, 9) + key[0] = 1 + binary.BigEndian.PutUint64(key[1:9], idx) + + return key +} + +func encodeIndex(index *dpaDBIndex) []byte { + data, _ := rlp.EncodeToBytes(index) + return data +} + +func encodeData(chunk *Chunk) []byte { + return chunk.SData +} + +func decodeIndex(data []byte, index *dpaDBIndex) { + dec := rlp.NewStream(bytes.NewReader(data), 0) + dec.Decode(index) +} + +func decodeData(data []byte, chunk *Chunk) { + chunk.SData = data + chunk.Size = int64(binary.LittleEndian.Uint64(data[0:8])) +} + +func gcListPartition(list []*gcItem, left int, right int, pivotIndex int) int { + pivotValue := list[pivotIndex].value + dd := list[pivotIndex] + list[pivotIndex] = list[right] + list[right] = dd + storeIndex := left + for i := left; i < right; i++ { + if list[i].value < pivotValue { + dd = list[storeIndex] + list[storeIndex] = list[i] + list[i] = dd + storeIndex++ + } + } + dd = list[storeIndex] + list[storeIndex] = list[right] + list[right] = dd + return storeIndex +} + +func gcListSelect(list []*gcItem, left int, right int, n int) int { + if left == right { + return left + } + pivotIndex := (left + right) / 2 + pivotIndex = gcListPartition(list, left, right, pivotIndex) + if n == pivotIndex { + return n + } else { + if n < pivotIndex { + return gcListSelect(list, left, pivotIndex-1, n) + } else { + return gcListSelect(list, pivotIndex+1, right, n) + } + } +} + +func (s *DbStore) collectGarbage(ratio float32) { + it := s.db.NewIterator() + it.Seek(s.gcPos) + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + gcnt := 0 + + for (gcnt < gcArraySize) && (uint64(gcnt) < s.entryCnt) { + + if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { + it.Seek(s.gcStartPos) + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + } + + if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { + break + } + + gci := new(gcItem) + gci.idxKey = s.gcPos + var index dpaDBIndex + decodeIndex(it.Value(), &index) + gci.idx = index.Idx + // the smaller, the more likely to be gc'd + gci.value = getIndexGCValue(&index) + s.gcArray[gcnt] = gci + gcnt++ + it.Next() + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + } + it.Release() + + cutidx := gcListSelect(s.gcArray, 0, gcnt-1, int(float32(gcnt)*ratio)) + cutval := s.gcArray[cutidx].value + + // fmt.Print(gcnt, " ", s.entryCnt, " ") + + // actual gc + for i := 0; i < gcnt; i++ { + if s.gcArray[i].value <= cutval { + batch := new(leveldb.Batch) + batch.Delete(s.gcArray[i].idxKey) + batch.Delete(getDataKey(s.gcArray[i].idx)) + s.entryCnt-- + batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) + s.db.Write(batch) + } + } + + // fmt.Println(s.entryCnt) + + s.db.Put(keyGCPos, s.gcPos) +} + +func (s *DbStore) Counter() uint64 { + s.lock.Lock() + defer s.lock.Unlock() + return s.dataIdx +} + +func (s *DbStore) Put(chunk *Chunk) { + s.lock.Lock() + defer s.lock.Unlock() + + ikey := getIndexKey(chunk.Key) + var index dpaDBIndex + + if s.tryAccessIdx(ikey, &index) { + if chunk.dbStored != nil { + close(chunk.dbStored) + } + return // already exists, only update access + } + + data := encodeData(chunk) + //data := ethutil.Encode([]interface{}{entry}) + + if s.entryCnt >= s.capacity { + s.collectGarbage(gcArrayFreeRatio) + } + + batch := new(leveldb.Batch) + + batch.Put(getDataKey(s.dataIdx), data) + + index.Idx = s.dataIdx + s.updateIndexAccess(&index) + + idata := encodeIndex(&index) + batch.Put(ikey, idata) + + batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) + s.entryCnt++ + batch.Put(keyDataIdx, U64ToBytes(s.dataIdx)) + s.dataIdx++ + batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) + s.accessCnt++ + + s.db.Write(batch) + if chunk.dbStored != nil { + close(chunk.dbStored) + } + glog.V(logger.Detail).Infof("DbStore.Put: %v. db storage counter: %v ", chunk.Key.Log(), s.dataIdx) +} + +// try to find index; if found, update access cnt and return true +func (s *DbStore) tryAccessIdx(ikey []byte, index *dpaDBIndex) bool { + idata, err := s.db.Get(ikey) + if err != nil { + return false + } + decodeIndex(idata, index) + + batch := new(leveldb.Batch) + + batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) + s.accessCnt++ + s.updateIndexAccess(index) + idata = encodeIndex(index) + batch.Put(ikey, idata) + + s.db.Write(batch) + + return true +} + +func (s *DbStore) Get(key Key) (chunk *Chunk, err error) { + s.lock.Lock() + defer s.lock.Unlock() + + var index dpaDBIndex + + if s.tryAccessIdx(getIndexKey(key), &index) { + var data []byte + data, err = s.db.Get(getDataKey(index.Idx)) + if err != nil { + return + } + + hasher := s.hashfunc() + hasher.Write(data) + hash := hasher.Sum(nil) + if bytes.Compare(hash, key) != 0 { + s.db.Delete(getDataKey(index.Idx)) + err = fmt.Errorf("invalid chunk. hash=%x, key=%v", hash, key[:]) + return + } + + chunk = &Chunk{ + Key: key, + } + decodeData(data, chunk) + } else { + err = notFound + } + + return + +} + +func (s *DbStore) updateAccessCnt(key Key) { + + s.lock.Lock() + defer s.lock.Unlock() + + var index dpaDBIndex + s.tryAccessIdx(getIndexKey(key), &index) // result_chn == nil, only update access cnt + +} + +func (s *DbStore) setCapacity(c uint64) { + + s.lock.Lock() + defer s.lock.Unlock() + + s.capacity = c + + if s.entryCnt > c { + var ratio float32 + ratio = float32(1.01) - float32(c)/float32(s.entryCnt) + if ratio < gcArrayFreeRatio { + ratio = gcArrayFreeRatio + } + if ratio > 1 { + ratio = 1 + } + for s.entryCnt > c { + s.collectGarbage(ratio) + } + } +} + +func (s *DbStore) getEntryCnt() uint64 { + return s.entryCnt +} + +func (s *DbStore) close() { + s.db.Close() +} + +// describes a section of the DbStore representing the unsynced +// domain relevant to a peer +// Start - Stop designate a continuous area Keys in an address space +// typically the addresses closer to us than to the peer but not closer +// another closer peer in between +// From - To designates a time interval typically from the last disconnect +// till the latest connection (real time traffic is relayed) +type DbSyncState struct { + Start, Stop Key + First, Last uint64 +} + +// implements the syncer iterator interface +// iterates by storage index (~ time of storage = first entry to db) +type dbSyncIterator struct { + it iterator.Iterator + DbSyncState +} + +// initialises a sync iterator from a syncToken (passed in with the handshake) +func (self *DbStore) NewSyncIterator(state DbSyncState) (si *dbSyncIterator, err error) { + if state.First > state.Last { + return nil, fmt.Errorf("no entries found") + } + si = &dbSyncIterator{ + it: self.db.NewIterator(), + DbSyncState: state, + } + si.it.Seek(getIndexKey(state.Start)) + return si, nil +} + +// walk the area from Start to Stop and returns items within time interval +// First to Last +func (self *dbSyncIterator) Next() (key Key) { + for self.it.Valid() { + dbkey := self.it.Key() + if dbkey[0] != 0 { + break + } + key = Key(make([]byte, len(dbkey)-1)) + copy(key[:], dbkey[1:]) + if bytes.Compare(key[:], self.Start) <= 0 { + self.it.Next() + continue + } + if bytes.Compare(key[:], self.Stop) > 0 { + break + } + var index dpaDBIndex + decodeIndex(self.it.Value(), &index) + self.it.Next() + if (index.Idx >= self.First) && (index.Idx < self.Last) { + return + } + } + self.it.Release() + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go new file mode 100644 index 000000000..31b6c54ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go @@ -0,0 +1,239 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "errors" + "io" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +DPA provides the client API entrypoints Store and Retrieve to store and retrieve +It can store anything that has a byte slice representation, so files or serialised objects etc. + +Storage: DPA calls the Chunker to segment the input datastream of any size to a merkle hashed tree of chunks. The key of the root block is returned to the client. + +Retrieval: given the key of the root block, the DPA retrieves the block chunks and reconstructs the original data and passes it back as a lazy reader. A lazy reader is a reader with on-demand delayed processing, i.e. the chunks needed to reconstruct a large file are only fetched and processed if that particular part of the document is actually read. + +As the chunker produces chunks, DPA dispatches them to its own chunk store +implementation for storage or retrieval. +*/ + +const ( + storeChanCapacity = 100 + retrieveChanCapacity = 100 + singletonSwarmDbCapacity = 50000 + singletonSwarmCacheCapacity = 500 + maxStoreProcesses = 8 + maxRetrieveProcesses = 8 +) + +var ( + notFound = errors.New("not found") +) + +type DPA struct { + ChunkStore + storeC chan *Chunk + retrieveC chan *Chunk + Chunker Chunker + + lock sync.Mutex + running bool + wg *sync.WaitGroup + quitC chan bool +} + +// for testing locally +func NewLocalDPA(datadir string) (*DPA, error) { + + hash := MakeHashFunc("SHA256") + + dbStore, err := NewDbStore(datadir, hash, singletonSwarmDbCapacity, 0) + if err != nil { + return nil, err + } + + return NewDPA(&LocalStore{ + NewMemStore(dbStore, singletonSwarmCacheCapacity), + dbStore, + }, NewChunkerParams()), nil +} + +func NewDPA(store ChunkStore, params *ChunkerParams) *DPA { + chunker := NewTreeChunker(params) + return &DPA{ + Chunker: chunker, + ChunkStore: store, + } +} + +// Public API. Main entry point for document retrieval directly. Used by the +// FS-aware API and httpaccess +// Chunk retrieval blocks on netStore requests with a timeout so reader will +// report error if retrieval of chunks within requested range time out. +func (self *DPA) Retrieve(key Key) LazySectionReader { + return self.Chunker.Join(key, self.retrieveC) +} + +// Public API. Main entry point for document storage directly. Used by the +// FS-aware API and httpaccess +func (self *DPA) Store(data io.Reader, size int64, swg *sync.WaitGroup, wwg *sync.WaitGroup) (key Key, err error) { + return self.Chunker.Split(data, size, self.storeC, swg, wwg) +} + +func (self *DPA) Start() { + self.lock.Lock() + defer self.lock.Unlock() + if self.running { + return + } + self.running = true + self.retrieveC = make(chan *Chunk, retrieveChanCapacity) + self.storeC = make(chan *Chunk, storeChanCapacity) + self.quitC = make(chan bool) + self.storeLoop() + self.retrieveLoop() +} + +func (self *DPA) Stop() { + self.lock.Lock() + defer self.lock.Unlock() + if !self.running { + return + } + self.running = false + close(self.quitC) +} + +// retrieveLoop dispatches the parallel chunk retrieval requests received on the +// retrieve channel to its ChunkStore (NetStore or LocalStore) +func (self *DPA) retrieveLoop() { + for i := 0; i < maxRetrieveProcesses; i++ { + go self.retrieveWorker() + } + glog.V(logger.Detail).Infof("dpa: retrieve loop spawning %v workers", maxRetrieveProcesses) +} + +func (self *DPA) retrieveWorker() { + for chunk := range self.retrieveC { + glog.V(logger.Detail).Infof("dpa: retrieve loop : chunk %v", chunk.Key.Log()) + storedChunk, err := self.Get(chunk.Key) + if err == notFound { + glog.V(logger.Detail).Infof("chunk %v not found", chunk.Key.Log()) + } else if err != nil { + glog.V(logger.Detail).Infof("error retrieving chunk %v: %v", chunk.Key.Log(), err) + } else { + chunk.SData = storedChunk.SData + chunk.Size = storedChunk.Size + } + close(chunk.C) + + select { + case <-self.quitC: + return + default: + } + } +} + +// storeLoop dispatches the parallel chunk store request processors +// received on the store channel to its ChunkStore (NetStore or LocalStore) +func (self *DPA) storeLoop() { + for i := 0; i < maxStoreProcesses; i++ { + go self.storeWorker() + } + glog.V(logger.Detail).Infof("dpa: store spawning %v workers", maxStoreProcesses) +} + +func (self *DPA) storeWorker() { + + for chunk := range self.storeC { + self.Put(chunk) + if chunk.wg != nil { + glog.V(logger.Detail).Infof("dpa: store processor %v", chunk.Key.Log()) + chunk.wg.Done() + + } + select { + case <-self.quitC: + return + default: + } + } +} + +// DpaChunkStore implements the ChunkStore interface, +// this chunk access layer assumed 2 chunk stores +// local storage eg. LocalStore and network storage eg., NetStore +// access by calling network is blocking with a timeout + +type dpaChunkStore struct { + n int + localStore ChunkStore + netStore ChunkStore +} + +func NewDpaChunkStore(localStore, netStore ChunkStore) *dpaChunkStore { + return &dpaChunkStore{0, localStore, netStore} +} + +// Get is the entrypoint for local retrieve requests +// waits for response or times out +func (self *dpaChunkStore) Get(key Key) (chunk *Chunk, err error) { + chunk, err = self.netStore.Get(key) + // timeout := time.Now().Add(searchTimeout) + if chunk.SData != nil { + glog.V(logger.Detail).Infof("DPA.Get: %v found locally, %d bytes", key.Log(), len(chunk.SData)) + return + } + // TODO: use self.timer time.Timer and reset with defer disableTimer + timer := time.After(searchTimeout) + select { + case <-timer: + glog.V(logger.Detail).Infof("DPA.Get: %v request time out ", key.Log()) + err = notFound + case <-chunk.Req.C: + glog.V(logger.Detail).Infof("DPA.Get: %v retrieved, %d bytes (%p)", key.Log(), len(chunk.SData), chunk) + } + return +} + +// Put is the entrypoint for local store requests coming from storeLoop +func (self *dpaChunkStore) Put(entry *Chunk) { + chunk, err := self.localStore.Get(entry.Key) + if err != nil { + glog.V(logger.Detail).Infof("DPA.Put: %v new chunk. call netStore.Put", entry.Key.Log()) + chunk = entry + } else if chunk.SData == nil { + glog.V(logger.Detail).Infof("DPA.Put: %v request entry found", entry.Key.Log()) + chunk.SData = entry.SData + chunk.Size = entry.Size + } else { + glog.V(logger.Detail).Infof("DPA.Put: %v chunk already known", entry.Key.Log()) + return + } + // from this point on the storage logic is the same with network storage requests + glog.V(logger.Detail).Infof("DPA.Put %v: %v", self.n, chunk.Key.Log()) + self.n++ + self.netStore.Put(chunk) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go new file mode 100644 index 000000000..541462f0c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "encoding/binary" +) + +// LocalStore is a combination of inmemory db over a disk persisted db +// implements a Get/Put with fallback (caching) logic using any 2 ChunkStores +type LocalStore struct { + memStore ChunkStore + DbStore ChunkStore +} + +// This constructor uses MemStore and DbStore as components +func NewLocalStore(hash Hasher, params *StoreParams) (*LocalStore, error) { + dbStore, err := NewDbStore(params.ChunkDbPath, hash, params.DbCapacity, params.Radius) + if err != nil { + return nil, err + } + return &LocalStore{ + memStore: NewMemStore(dbStore, params.CacheCapacity), + DbStore: dbStore, + }, nil +} + +// LocalStore is itself a chunk store +// unsafe, in that the data is not integrity checked +func (self *LocalStore) Put(chunk *Chunk) { + chunk.dbStored = make(chan bool) + self.memStore.Put(chunk) + if chunk.wg != nil { + chunk.wg.Add(1) + } + go func() { + self.DbStore.Put(chunk) + if chunk.wg != nil { + chunk.wg.Done() + } + }() +} + +// Get(chunk *Chunk) looks up a chunk in the local stores +// This method is blocking until the chunk is retrieved +// so additional timeout may be needed to wrap this call if +// ChunkStores are remote and can have long latency +func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) { + chunk, err = self.memStore.Get(key) + if err == nil { + return + } + chunk, err = self.DbStore.Get(key) + if err != nil { + return + } + chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + self.memStore.Put(chunk) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go new file mode 100644 index 000000000..f133bd7d3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go @@ -0,0 +1,316 @@ +// 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 . + +// memory storage layer for the package blockhash + +package storage + +import ( + "sync" +) + +const ( + memTreeLW = 2 // log2(subtree count) of the subtrees + memTreeFLW = 14 // log2(subtree count) of the root layer + dbForceUpdateAccessCnt = 1000 + defaultCacheCapacity = 5000 +) + +type MemStore struct { + memtree *memTree + entryCnt, capacity uint // stored entries + accessCnt uint64 // access counter; oldest is thrown away when full + dbAccessCnt uint64 + dbStore *DbStore + lock sync.Mutex +} + +/* +a hash prefix subtree containing subtrees or one storage entry (but never both) + +- access[0] stores the smallest (oldest) access count value in this subtree +- if it contains more subtrees and its subtree count is at least 4, access[1:2] + stores the smallest access count in the first and second halves of subtrees + (so that access[0] = min(access[1], access[2]) +- likewise, if subtree count is at least 8, + access[1] = min(access[3], access[4]) + access[2] = min(access[5], access[6]) + (access[] is a binary tree inside the multi-bit leveled hash tree) +*/ + +func NewMemStore(d *DbStore, capacity uint) (m *MemStore) { + m = &MemStore{} + m.memtree = newMemTree(memTreeFLW, nil, 0) + m.dbStore = d + m.setCapacity(capacity) + return +} + +type memTree struct { + subtree []*memTree + parent *memTree + parentIdx uint + + bits uint // log2(subtree count) + width uint // subtree count + + entry *Chunk // if subtrees are present, entry should be nil + lastDBaccess uint64 + access []uint64 +} + +func newMemTree(b uint, parent *memTree, pidx uint) (node *memTree) { + node = new(memTree) + node.bits = b + node.width = 1 << uint(b) + node.subtree = make([]*memTree, node.width) + node.access = make([]uint64, node.width-1) + node.parent = parent + node.parentIdx = pidx + if parent != nil { + parent.subtree[pidx] = node + } + + return node +} + +func (node *memTree) updateAccess(a uint64) { + aidx := uint(0) + var aa uint64 + oa := node.access[0] + for node.access[aidx] == oa { + node.access[aidx] = a + if aidx > 0 { + aa = node.access[((aidx-1)^1)+1] + aidx = (aidx - 1) >> 1 + } else { + pidx := node.parentIdx + node = node.parent + if node == nil { + return + } + nn := node.subtree[pidx^1] + if nn != nil { + aa = nn.access[0] + } else { + aa = 0 + } + aidx = (node.width + pidx - 2) >> 1 + } + + if (aa != 0) && (aa < a) { + a = aa + } + } +} + +func (s *MemStore) setCapacity(c uint) { + s.lock.Lock() + defer s.lock.Unlock() + + for c < s.entryCnt { + s.removeOldest() + } + s.capacity = c +} + +func (s *MemStore) getEntryCnt() uint { + return s.entryCnt +} + +// entry (not its copy) is going to be in MemStore +func (s *MemStore) Put(entry *Chunk) { + if s.capacity == 0 { + return + } + + s.lock.Lock() + defer s.lock.Unlock() + + if s.entryCnt >= s.capacity { + s.removeOldest() + } + + s.accessCnt++ + + node := s.memtree + bitpos := uint(0) + for node.entry == nil { + l := entry.Key.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + bitpos += node.bits + node = st + break + } + bitpos += node.bits + node = st + } + + if node.entry != nil { + + if node.entry.Key.isEqual(entry.Key) { + node.updateAccess(s.accessCnt) + if entry.SData == nil { + entry.Size = node.entry.Size + entry.SData = node.entry.SData + } + if entry.Req == nil { + entry.Req = node.entry.Req + } + entry.C = node.entry.C + node.entry = entry + return + } + + for node.entry != nil { + + l := node.entry.Key.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + } + st.entry = node.entry + node.entry = nil + st.updateAccess(node.access[0]) + + l = entry.Key.bits(bitpos, node.bits) + st = node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + } + bitpos += node.bits + node = st + + } + } + + node.entry = entry + node.lastDBaccess = s.dbAccessCnt + node.updateAccess(s.accessCnt) + s.entryCnt++ + + return +} + +func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) { + s.lock.Lock() + defer s.lock.Unlock() + + node := s.memtree + bitpos := uint(0) + for node.entry == nil { + l := hash.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + return nil, notFound + } + bitpos += node.bits + node = st + } + + if node.entry.Key.isEqual(hash) { + s.accessCnt++ + node.updateAccess(s.accessCnt) + chunk = node.entry + if s.dbAccessCnt-node.lastDBaccess > dbForceUpdateAccessCnt { + s.dbAccessCnt++ + node.lastDBaccess = s.dbAccessCnt + if s.dbStore != nil { + s.dbStore.updateAccessCnt(hash) + } + } + } else { + err = notFound + } + + return +} + +func (s *MemStore) removeOldest() { + node := s.memtree + + for node.entry == nil { + + aidx := uint(0) + av := node.access[aidx] + + for aidx < node.width/2-1 { + if av == node.access[aidx*2+1] { + node.access[aidx] = node.access[aidx*2+2] + aidx = aidx*2 + 1 + } else if av == node.access[aidx*2+2] { + node.access[aidx] = node.access[aidx*2+1] + aidx = aidx*2 + 2 + } else { + panic(nil) + } + } + pidx := aidx*2 + 2 - node.width + if (node.subtree[pidx] != nil) && (av == node.subtree[pidx].access[0]) { + if node.subtree[pidx+1] != nil { + node.access[aidx] = node.subtree[pidx+1].access[0] + } else { + node.access[aidx] = 0 + } + } else if (node.subtree[pidx+1] != nil) && (av == node.subtree[pidx+1].access[0]) { + if node.subtree[pidx] != nil { + node.access[aidx] = node.subtree[pidx].access[0] + } else { + node.access[aidx] = 0 + } + pidx++ + } else { + panic(nil) + } + + //fmt.Println(pidx) + node = node.subtree[pidx] + + } + + if node.entry.dbStored != nil { + <-node.entry.dbStored + } + + if node.entry.SData != nil { + node.entry = nil + s.entryCnt-- + } + + node.access[0] = 0 + + //--- + + aidx := uint(0) + for { + aa := node.access[aidx] + if aidx > 0 { + aidx = (aidx - 1) >> 1 + } else { + pidx := node.parentIdx + node = node.parent + if node == nil { + return + } + aidx = (node.width + pidx - 2) >> 1 + } + if (aa != 0) && ((aa < node.access[aidx]) || (node.access[aidx] == 0)) { + node.access[aidx] = aa + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go new file mode 100644 index 000000000..334229aed --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go @@ -0,0 +1,134 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "path/filepath" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +NetStore is a cloud storage access abstaction layer for swarm +it contains the shared logic of network served chunk store/retrieval requests +both local (coming from DPA api) and remote (coming from peers via bzz protocol) +it implements the ChunkStore interface and embeds LocalStore + +It is called by the bzz protocol instances via Depo (the store/retrieve request handler) +a protocol instance is running on each peer, so this is heavily parallelised. +NetStore falls back to a backend (CloudStorage interface) +implemented by bzz/network/forwarder. forwarder or IPFS or IPΞS +*/ +type NetStore struct { + hashfunc Hasher + localStore *LocalStore + cloud CloudStore + lock sync.Mutex +} + +// backend engine for cloud store +// It can be aggregate dispatching to several parallel implementations: +// bzz/network/forwarder. forwarder or IPFS or IPΞS +type CloudStore interface { + Store(*Chunk) + Deliver(*Chunk) + Retrieve(*Chunk) +} + +type StoreParams struct { + ChunkDbPath string + DbCapacity uint64 + CacheCapacity uint + Radius int +} + +func NewStoreParams(path string) (self *StoreParams) { + return &StoreParams{ + ChunkDbPath: filepath.Join(path, "chunks"), + DbCapacity: defaultDbCapacity, + CacheCapacity: defaultCacheCapacity, + Radius: defaultRadius, + } +} + +// netstore contructor, takes path argument that is used to initialise dbStore, +// the persistent (disk) storage component of LocalStore +// the second argument is the hive, the connection/logistics manager for the node +func NewNetStore(hash Hasher, lstore *LocalStore, cloud CloudStore, params *StoreParams) *NetStore { + return &NetStore{ + hashfunc: hash, + localStore: lstore, + cloud: cloud, + } +} + +const ( + // maximum number of peers that a retrieved message is delivered to + requesterCount = 3 +) + +var ( + // timeout interval before retrieval is timed out + searchTimeout = 3 * time.Second +) + +// store logic common to local and network chunk store requests +// ~ unsafe put in localdb no check if exists no extra copy no hash validation +// the chunk is forced to propagate (Cloud.Store) even if locally found! +// caller needs to make sure if that is wanted +func (self *NetStore) Put(entry *Chunk) { + self.localStore.Put(entry) + + // handle deliveries + if entry.Req != nil { + glog.V(logger.Detail).Infof("NetStore.Put: localStore.Put %v hit existing request...delivering", entry.Key.Log()) + // closing C singals to other routines (local requests) + // that the chunk is has been retrieved + close(entry.Req.C) + // deliver the chunk to requesters upstream + go self.cloud.Deliver(entry) + } else { + glog.V(logger.Detail).Infof("NetStore.Put: localStore.Put %v stored locally", entry.Key.Log()) + // handle propagating store requests + // go self.cloud.Store(entry) + go self.cloud.Store(entry) + } +} + +// retrieve logic common for local and network chunk retrieval requests +func (self *NetStore) Get(key Key) (*Chunk, error) { + var err error + chunk, err := self.localStore.Get(key) + if err == nil { + if chunk.Req == nil { + glog.V(logger.Detail).Infof("NetStore.Get: %v found locally", key) + } else { + glog.V(logger.Detail).Infof("NetStore.Get: %v hit on an existing request", key) + // no need to launch again + } + return chunk, err + } + // no data and no request status + glog.V(logger.Detail).Infof("NetStore.Get: %v not found locally. open new request", key) + chunk = NewChunk(key, newRequestStatus(key)) + self.localStore.memStore.Put(chunk) + go self.cloud.Retrieve(chunk) + return chunk, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go new file mode 100644 index 000000000..79e1927b9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go @@ -0,0 +1,201 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + processors = 8 +) + +type Tree struct { + Chunks int64 + Levels []map[int64]*Node + Lock sync.RWMutex +} + +type Node struct { + Pending int64 + Size uint64 + Children []common.Hash + Last bool +} + +func (self *Node) String() string { + var children []string + for _, node := range self.Children { + children = append(children, node.Hex()) + } + return fmt.Sprintf("pending: %v, size: %v, last :%v, children: %v", self.Pending, self.Size, self.Last, strings.Join(children, ", ")) +} + +type Task struct { + Index int64 // Index of the chunk being processed + Size uint64 + Data []byte // Binary blob of the chunk + Last bool +} + +type PyramidChunker struct { + hashFunc Hasher + chunkSize int64 + hashSize int64 + branches int64 + workerCount int +} + +func NewPyramidChunker(params *ChunkerParams) (self *PyramidChunker) { + self = &PyramidChunker{} + self.hashFunc = MakeHashFunc(params.Hash) + self.branches = params.Branches + self.hashSize = int64(self.hashFunc().Size()) + self.chunkSize = self.hashSize * self.branches + self.workerCount = 1 + return +} + +func (self *PyramidChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) { + + chunks := (size + self.chunkSize - 1) / self.chunkSize + depth := int(math.Ceil(math.Log(float64(chunks))/math.Log(float64(self.branches)))) + 1 + + results := Tree{ + Chunks: chunks, + Levels: make([]map[int64]*Node, depth), + } + for i := 0; i < depth; i++ { + results.Levels[i] = make(map[int64]*Node) + } + // Create a pool of workers to crunch through the file + tasks := make(chan *Task, 2*processors) + pend := new(sync.WaitGroup) + abortC := make(chan bool) + for i := 0; i < processors; i++ { + pend.Add(1) + go self.processor(pend, swg, tasks, chunkC, &results) + } + // Feed the chunks into the task pool + read := 0 + for index := 0; ; index++ { + buffer := make([]byte, self.chunkSize+8) + n, err := data.Read(buffer[8:]) + read += n + last := int64(read) == size || err == io.ErrUnexpectedEOF || err == io.EOF + if err != nil && !last { + close(abortC) + break + } + binary.LittleEndian.PutUint64(buffer[:8], uint64(n)) + pend.Add(1) + select { + case tasks <- &Task{Index: int64(index), Size: uint64(n), Data: buffer[:n+8], Last: last}: + case <-abortC: + return nil, err + } + if last { + break + } + } + // Wait for the workers and return + close(tasks) + pend.Wait() + + key := results.Levels[0][0].Children[0][:] + return key, nil +} + +func (self *PyramidChunker) processor(pend, swg *sync.WaitGroup, tasks chan *Task, chunkC chan *Chunk, results *Tree) { + defer pend.Done() + + // Start processing leaf chunks ad infinitum + hasher := self.hashFunc() + for task := range tasks { + depth, pow := len(results.Levels)-1, self.branches + size := task.Size + data := task.Data + var node *Node + for depth >= 0 { + // New chunk received, reset the hasher and start processing + hasher.Reset() + if node == nil { // Leaf node, hash the data chunk + hasher.Write(task.Data) + } else { // Internal node, hash the children + size = node.Size + data = make([]byte, hasher.Size()*len(node.Children)+8) + binary.LittleEndian.PutUint64(data[:8], size) + + hasher.Write(data[:8]) + for i, hash := range node.Children { + copy(data[i*hasher.Size()+8:], hash[:]) + hasher.Write(hash[:]) + } + } + hash := hasher.Sum(nil) + last := task.Last || (node != nil) && node.Last + // Insert the subresult into the memoization tree + results.Lock.Lock() + if node = results.Levels[depth][task.Index/pow]; node == nil { + // Figure out the pending tasks + pending := self.branches + if task.Index/pow == results.Chunks/pow { + pending = (results.Chunks + pow/self.branches - 1) / (pow / self.branches) % self.branches + } + node = &Node{pending, 0, make([]common.Hash, pending), last} + results.Levels[depth][task.Index/pow] = node + } + node.Pending-- + i := task.Index / (pow / self.branches) % self.branches + if last { + node.Last = true + } + copy(node.Children[i][:], hash) + node.Size += size + left := node.Pending + if chunkC != nil { + if swg != nil { + swg.Add(1) + } + select { + case chunkC <- &Chunk{Key: hash, SData: data, wg: swg}: + // case <- self.quitC + } + } + if depth+1 < len(results.Levels) { + delete(results.Levels[depth+1], task.Index/(pow/self.branches)) + } + + results.Lock.Unlock() + // If there's more work to be done, leave for others + if left > 0 { + break + } + // We're the last ones in this batch, merge the children together + depth-- + pow *= self.branches + } + pend.Done() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go new file mode 100644 index 000000000..0dcbc0100 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go @@ -0,0 +1,232 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "bytes" + "crypto" + "fmt" + "hash" + "io" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" +) + +type Hasher func() hash.Hash + +// Peer is the recorded as Source on the chunk +// should probably not be here? but network should wrap chunk object +type Peer interface{} + +type Key []byte + +func (x Key) Size() uint { + return uint(len(x)) +} + +func (x Key) isEqual(y Key) bool { + return bytes.Compare(x, y) == 0 +} + +func (h Key) bits(i, j uint) uint { + ii := i >> 3 + jj := i & 7 + if ii >= h.Size() { + return 0 + } + + if jj+j <= 8 { + return uint((h[ii] >> jj) & ((1 << j) - 1)) + } + + res := uint(h[ii] >> jj) + jj = 8 - jj + j -= jj + for j != 0 { + ii++ + if j < 8 { + res += uint(h[ii]&((1<. + +package swarm + +import ( + "bytes" + "crypto/ecdsa" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/contracts/ens" + "github.com/ethereum/go-ethereum/crypto" + "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/p2p/discover" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/swarm/api" + httpapi "github.com/ethereum/go-ethereum/swarm/api/http" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/storage" + "golang.org/x/net/context" +) + +// the swarm stack +type Swarm struct { + config *api.Config // swarm configuration + api *api.Api // high level api layer (fs/manifest) + dns api.Resolver // DNS registrar + dbAccess *network.DbAccess // access to local chunk db iterator and storage counter + storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends + dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support + depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage + cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud) + hive *network.Hive // the logistic manager + backend chequebook.Backend // simple blockchain Backend + privateKey *ecdsa.PrivateKey + swapEnabled bool +} + +type SwarmAPI struct { + Api *api.Api + Backend chequebook.Backend + PrvKey *ecdsa.PrivateKey +} + +func (self *Swarm) API() *SwarmAPI { + return &SwarmAPI{ + Api: self.api, + Backend: self.backend, + PrvKey: self.privateKey, + } +} + +// creates a new swarm service instance +// implements node.Service +func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.Config, swapEnabled, syncEnabled bool) (self *Swarm, err error) { + if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { + return nil, fmt.Errorf("empty public key") + } + if bytes.Equal(common.FromHex(config.BzzKey), storage.ZeroKey) { + return nil, fmt.Errorf("empty bzz key") + } + + self = &Swarm{ + config: config, + swapEnabled: swapEnabled, + backend: backend, + privateKey: config.Swap.PrivateKey(), + } + glog.V(logger.Debug).Infof("Setting up Swarm service components") + + hash := storage.MakeHashFunc(config.ChunkerParams.Hash) + lstore, err := storage.NewLocalStore(hash, config.StoreParams) + if err != nil { + return + } + + // setup local store + glog.V(logger.Debug).Infof("Set up local storage") + + self.dbAccess = network.NewDbAccess(lstore) + glog.V(logger.Debug).Infof("Set up local db access (iterator/counter)") + + // set up the kademlia hive + self.hive = network.NewHive( + common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address) + config.HiveParams, // configuration parameters + swapEnabled, // SWAP enabled + syncEnabled, // syncronisation enabled + ) + glog.V(logger.Debug).Infof("Set up swarm network with Kademlia hive") + + // setup cloud storage backend + cloud := network.NewForwarder(self.hive) + glog.V(logger.Debug).Infof("-> set swarm forwarder as cloud storage backend") + // setup cloud storage internal access layer + + self.storage = storage.NewNetStore(hash, lstore, cloud, config.StoreParams) + glog.V(logger.Debug).Infof("-> swarm net store shared access layer to Swarm Chunk Store") + + // set up Depo (storage handler = cloud storage access layer for incoming remote requests) + self.depo = network.NewDepo(hash, lstore, self.storage) + glog.V(logger.Debug).Infof("-> REmote Access to CHunks") + + // set up DPA, the cloud storage local access layer + dpaChunkStore := storage.NewDpaChunkStore(lstore, self.storage) + glog.V(logger.Debug).Infof("-> Local Access to Swarm") + // Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage + self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams) + glog.V(logger.Debug).Infof("-> Content Store API") + + // set up high level api + transactOpts := bind.NewKeyedTransactor(self.privateKey) + + self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend) + if err != nil { + return nil, err + } + glog.V(logger.Debug).Infof("-> Swarm Domain Name Registrar @ address %v", config.EnsRoot.Hex()) + + self.api = api.NewApi(self.dpa, self.dns) + // Manifests for Smart Hosting + glog.V(logger.Debug).Infof("-> Web3 virtual server API") + + return self, nil +} + +/* +Start is called when the stack is started +* starts the network kademlia hive peer management +* (starts netStore level 0 api) +* starts DPA level 1 api (chunking -> store/retrieve requests) +* (starts level 2 api) +* starts http proxy server +* registers url scheme handlers for bzz, etc +* TODO: start subservices like sword, swear, swarmdns +*/ +// implements the node.Service interface +func (self *Swarm) Start(net *p2p.Server) error { + connectPeer := func(url string) error { + node, err := discover.ParseNode(url) + if err != nil { + return fmt.Errorf("invalid node URL: %v", err) + } + net.AddPeer(node) + return nil + } + // set chequebook + if self.swapEnabled { + ctx := context.Background() // The initial setup has no deadline. + err := self.SetChequebook(ctx) + if err != nil { + return fmt.Errorf("Unable to set chequebook for SWAP: %v", err) + } + glog.V(logger.Debug).Infof("-> cheque book for SWAP: %v", self.config.Swap.Chequebook()) + } else { + glog.V(logger.Debug).Infof("SWAP disabled: no cheque book set") + } + + glog.V(logger.Warn).Infof("Starting Swarm service") + self.hive.Start( + discover.PubkeyID(&net.PrivateKey.PublicKey), + func() string { return net.ListenAddr }, + connectPeer, + ) + glog.V(logger.Info).Infof("Swarm network started on bzz address: %v", self.hive.Addr()) + + self.dpa.Start() + glog.V(logger.Debug).Infof("Swarm DPA started") + + // start swarm http proxy server + if self.config.Port != "" { + go httpapi.StartHttpServer(self.api, self.config.Port) + } + glog.V(logger.Debug).Infof("Swarm http proxy started on port: %v", self.config.Port) + + return nil +} + +// implements the node.Service interface +// stops all component services. +func (self *Swarm) Stop() error { + self.dpa.Stop() + self.hive.Stop() + if ch := self.config.Swap.Chequebook(); ch != nil { + ch.Stop() + ch.Save() + } + return self.config.Save() +} + +// implements the node.Service interface +func (self *Swarm) Protocols() []p2p.Protocol { + proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams) + if err != nil { + return nil + } + return []p2p.Protocol{proto} +} + +// implements node.Service +// Apis returns the RPC Api descriptors the Swarm implementation offers +func (self *Swarm) APIs() []rpc.API { + return []rpc.API{ + // public APIs + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewStorage(self.api), + Public: true, + }, + { + Namespace: "bzz", + Version: "0.1", + Service: &Info{self.config, chequebook.ContractParams}, + Public: true, + }, + // admin APIs + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewFileSystem(self.api), + Public: false}, + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewControl(self.api, self.hive), + Public: false, + }, + { + Namespace: "chequebook", + Version: chequebook.Version, + Service: chequebook.NewApi(self.config.Swap.Chequebook), + Public: false, + }, + // {Namespace, Version, api.NewAdmin(self), false}, + } +} + +func (self *Swarm) Api() *api.Api { + return self.api +} + +// SetChequebook ensures that the local checquebook is set up on chain. +func (self *Swarm) SetChequebook(ctx context.Context) error { + err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path) + if err != nil { + return err + } + glog.V(logger.Info).Infof("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex()) + self.config.Save() + self.hive.DropAll() + return nil +} + +// Local swarm without netStore +func NewLocalSwarm(datadir, port string) (self *Swarm, err error) { + + prvKey, err := crypto.GenerateKey() + if err != nil { + return + } + + config, err := api.NewConfig(datadir, common.Address{}, prvKey) + if err != nil { + return + } + config.Port = port + + dpa, err := storage.NewLocalDPA(datadir) + if err != nil { + return + } + + self = &Swarm{ + api: api.NewApi(dpa, nil), + config: config, + } + + return +} + +// serialisable info about swarm +type Info struct { + *api.Config + *chequebook.Params +} + +func (self *Info) Info() *Info { + return self +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/arc.go b/vendor/github.com/ethereum/go-ethereum/trie/arc.go deleted file mode 100644 index fc7a3259f..000000000 --- a/vendor/github.com/ethereum/go-ethereum/trie/arc.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2015 Hans Alexander Gugel -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// This file contains a modified version of package arc from -// https://github.com/alexanderGugel/arc -// -// It implements the ARC (Adaptive Replacement Cache) algorithm as detailed in -// https://www.usenix.org/legacy/event/fast03/tech/full_papers/megiddo/megiddo.pdf - -package trie - -import ( - "container/list" - "sync" -) - -type arc struct { - p int - c int - t1 *list.List - b1 *list.List - t2 *list.List - b2 *list.List - cache map[string]*entry - mutex sync.Mutex -} - -type entry struct { - key hashNode - value node - ll *list.List - el *list.Element -} - -// newARC returns a new Adaptive Replacement Cache with the -// given capacity. -func newARC(c int) *arc { - return &arc{ - c: c, - t1: list.New(), - b1: list.New(), - t2: list.New(), - b2: list.New(), - cache: make(map[string]*entry, c), - } -} - -// Clear clears the cache -func (a *arc) Clear() { - a.mutex.Lock() - defer a.mutex.Unlock() - a.p = 0 - a.t1 = list.New() - a.b1 = list.New() - a.t2 = list.New() - a.b2 = list.New() - a.cache = make(map[string]*entry, a.c) -} - -// Put inserts a new key-value pair into the cache. -// This optimizes future access to this entry (side effect). -func (a *arc) Put(key hashNode, value node) bool { - a.mutex.Lock() - defer a.mutex.Unlock() - ent, ok := a.cache[string(key)] - if ok != true { - ent = &entry{key: key, value: value} - a.req(ent) - a.cache[string(key)] = ent - } else { - ent.value = value - a.req(ent) - } - return ok -} - -// Get retrieves a previously via Set inserted entry. -// This optimizes future access to this entry (side effect). -func (a *arc) Get(key hashNode) (value node, ok bool) { - a.mutex.Lock() - defer a.mutex.Unlock() - ent, ok := a.cache[string(key)] - if ok { - a.req(ent) - return ent.value, ent.value != nil - } - return nil, false -} - -func (a *arc) req(ent *entry) { - if ent.ll == a.t1 || ent.ll == a.t2 { - // Case I - ent.setMRU(a.t2) - } else if ent.ll == a.b1 { - // Case II - // Cache Miss in t1 and t2 - - // Adaptation - var d int - if a.b1.Len() >= a.b2.Len() { - d = 1 - } else { - d = a.b2.Len() / a.b1.Len() - } - a.p = a.p + d - if a.p > a.c { - a.p = a.c - } - - a.replace(ent) - ent.setMRU(a.t2) - } else if ent.ll == a.b2 { - // Case III - // Cache Miss in t1 and t2 - - // Adaptation - var d int - if a.b2.Len() >= a.b1.Len() { - d = 1 - } else { - d = a.b1.Len() / a.b2.Len() - } - a.p = a.p - d - if a.p < 0 { - a.p = 0 - } - - a.replace(ent) - ent.setMRU(a.t2) - } else if ent.ll == nil { - // Case IV - - if a.t1.Len()+a.b1.Len() == a.c { - // Case A - if a.t1.Len() < a.c { - a.delLRU(a.b1) - a.replace(ent) - } else { - a.delLRU(a.t1) - } - } else if a.t1.Len()+a.b1.Len() < a.c { - // Case B - if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c { - if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c { - a.delLRU(a.b2) - } - a.replace(ent) - } - } - - ent.setMRU(a.t1) - } -} - -func (a *arc) delLRU(list *list.List) { - lru := list.Back() - list.Remove(lru) - delete(a.cache, string(lru.Value.(*entry).key)) -} - -func (a *arc) replace(ent *entry) { - if a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) { - lru := a.t1.Back().Value.(*entry) - lru.value = nil - lru.setMRU(a.b1) - } else { - lru := a.t2.Back().Value.(*entry) - lru.value = nil - lru.setMRU(a.b2) - } -} - -func (e *entry) setLRU(list *list.List) { - e.detach() - e.ll = list - e.el = e.ll.PushBack(e) -} - -func (e *entry) setMRU(list *list.List) { - e.detach() - e.ll = list - e.el = e.ll.PushFront(e) -} - -func (e *entry) detach() { - if e.ll != nil { - e.ll.Remove(e.el) - } -} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/hasher.go b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go new file mode 100644 index 000000000..e6261819c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go @@ -0,0 +1,173 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + "hash" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/rlp" +) + +type hasher struct { + tmp *bytes.Buffer + sha hash.Hash + cachegen, cachelimit uint16 +} + +// hashers live in a global pool. +var hasherPool = sync.Pool{ + New: func() interface{} { + return &hasher{tmp: new(bytes.Buffer), sha: sha3.NewKeccak256()} + }, +} + +func newHasher(cachegen, cachelimit uint16) *hasher { + h := hasherPool.Get().(*hasher) + h.cachegen, h.cachelimit = cachegen, cachelimit + return h +} + +func returnHasherToPool(h *hasher) { + hasherPool.Put(h) +} + +// hash collapses a node down into a hash node, also returning a copy of the +// original node initialzied with the computed hash to replace the original one. +func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { + // If we're not storing the node, just hashing, use avaialble cached data + if hash, dirty := n.cache(); hash != nil { + if db == nil { + return hash, n, nil + } + if n.canUnload(h.cachegen, h.cachelimit) { + // Unload the node from cache. All of its subnodes will have a lower or equal + // cache generation number. + cacheUnloadCounter.Inc(1) + return hash, hash, nil + } + if !dirty { + return hash, n, nil + } + } + // Trie not processed yet or needs storage, walk the children + collapsed, cached, err := h.hashChildren(n, db) + if err != nil { + return hashNode{}, n, err + } + hashed, err := h.store(collapsed, db, force) + if err != nil { + return hashNode{}, n, err + } + // Cache the hash of the ndoe for later reuse and remove + // the dirty flag in commit mode. It's fine to assign these values directly + // without copying the node first because hashChildren copies it. + cachedHash, _ := hashed.(hashNode) + switch cn := cached.(type) { + case *shortNode: + cn.flags.hash = cachedHash + if db != nil { + cn.flags.dirty = false + } + case *fullNode: + cn.flags.hash = cachedHash + if db != nil { + cn.flags.dirty = false + } + } + return hashed, cached, nil +} + +// hashChildren replaces the children of a node with their hashes if the encoded +// size of the child is larger than a hash, returning the collapsed node as well +// as a replacement for the original node with the child hashes cached in. +func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { + var err error + + switch n := original.(type) { + case *shortNode: + // Hash the short node's child, caching the newly hashed subtree + collapsed, cached := n.copy(), n.copy() + collapsed.Key = compactEncode(n.Key) + cached.Key = common.CopyBytes(n.Key) + + if _, ok := n.Val.(valueNode); !ok { + collapsed.Val, cached.Val, err = h.hash(n.Val, db, false) + if err != nil { + return original, original, err + } + } + if collapsed.Val == nil { + collapsed.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings. + } + return collapsed, cached, nil + + case *fullNode: + // Hash the full node's children, caching the newly hashed subtrees + collapsed, cached := n.copy(), n.copy() + + for i := 0; i < 16; i++ { + if n.Children[i] != nil { + collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false) + if err != nil { + return original, original, err + } + } else { + collapsed.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings. + } + } + cached.Children[16] = n.Children[16] + if collapsed.Children[16] == nil { + collapsed.Children[16] = valueNode(nil) + } + return collapsed, cached, nil + + default: + // Value and hash nodes don't have children so they're left as were + return n, original, nil + } +} + +func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { + // Don't store hashes or empty nodes. + if _, isHash := n.(hashNode); n == nil || isHash { + return n, nil + } + // Generate the RLP encoding of the node + h.tmp.Reset() + if err := rlp.Encode(h.tmp, n); err != nil { + panic("encode error: " + err.Error()) + } + + if h.tmp.Len() < 32 && !force { + return n, nil // Nodes smaller than 32 bytes are stored inside their parent + } + // Larger nodes are replaced by their hash and stored in the database. + hash, _ := n.cache() + if hash == nil { + h.sha.Reset() + h.sha.Write(h.tmp.Bytes()) + hash = hashNode(h.sha.Sum(nil)) + } + if db != nil { + return hash, db.Put(hash, h.tmp.Bytes()) + } + return hash, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/iterator.go b/vendor/github.com/ethereum/go-ethereum/trie/iterator.go index 88c4cee7f..afde6e19e 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/iterator.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/iterator.go @@ -16,18 +16,13 @@ package trie -import ( - "bytes" - "fmt" +import "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -// Iterator is a key-value trie iterator to traverse the data contents. +// Iterator is a key-value trie iterator that traverses a Trie. type Iterator struct { - trie *Trie + trie *Trie + nodeIt *NodeIterator + keyBuf []byte Key []byte // Current data key on which the iterator is positioned on Value []byte // Current data value on which the iterator is positioned on @@ -35,119 +30,45 @@ type Iterator struct { // NewIterator creates a new key-value iterator. func NewIterator(trie *Trie) *Iterator { - return &Iterator{trie: trie, Key: nil} + return &Iterator{ + trie: trie, + nodeIt: NewNodeIterator(trie), + keyBuf: make([]byte, 0, 64), + Key: nil, + } } -// Next moves the iterator forward with one key-value entry. -func (self *Iterator) Next() bool { - isIterStart := false - if self.Key == nil { - isIterStart = true - self.Key = make([]byte, 32) +// Next moves the iterator forward one key-value entry. +func (it *Iterator) Next() bool { + for it.nodeIt.Next() { + if it.nodeIt.Leaf { + it.Key = it.makeKey() + it.Value = it.nodeIt.LeafBlob + return true + } } - - key := remTerm(compactHexDecode(self.Key)) - k := self.next(self.trie.root, key, isIterStart) - - self.Key = []byte(decodeCompact(k)) - - return len(k) > 0 + it.Key = nil + it.Value = nil + return false } -func (self *Iterator) next(node interface{}, key []byte, isIterStart bool) []byte { - if node == nil { - return nil +func (it *Iterator) makeKey() []byte { + key := it.keyBuf[:0] + for _, se := range it.nodeIt.stack { + switch node := se.node.(type) { + case *fullNode: + if se.child <= 16 { + key = append(key, byte(se.child)) + } + case *shortNode: + if hasTerm(node.Key) { + key = append(key, node.Key[:len(node.Key)-1]...) + } else { + key = append(key, node.Key...) + } + } } - - switch node := node.(type) { - case fullNode: - if len(key) > 0 { - k := self.next(node.Children[key[0]], key[1:], isIterStart) - if k != nil { - return append([]byte{key[0]}, k...) - } - } - - var r byte - if len(key) > 0 { - r = key[0] + 1 - } - - for i := r; i < 16; i++ { - k := self.key(node.Children[i]) - if k != nil { - return append([]byte{i}, k...) - } - } - - case shortNode: - k := remTerm(node.Key) - if vnode, ok := node.Val.(valueNode); ok { - switch bytes.Compare([]byte(k), key) { - case 0: - if isIterStart { - self.Value = vnode - return k - } - case 1: - self.Value = vnode - return k - } - } else { - cnode := node.Val - - var ret []byte - skey := key[len(k):] - if bytes.HasPrefix(key, k) { - ret = self.next(cnode, skey, isIterStart) - } else if bytes.Compare(k, key[:len(k)]) > 0 { - return self.key(node) - } - - if ret != nil { - return append(k, ret...) - } - } - - case hashNode: - rn, err := self.trie.resolveHash(node, nil, nil) - if err != nil && glog.V(logger.Error) { - glog.Errorf("Unhandled trie error: %v", err) - } - return self.next(rn, key, isIterStart) - } - return nil -} - -func (self *Iterator) key(node interface{}) []byte { - switch node := node.(type) { - case shortNode: - // Leaf node - k := remTerm(node.Key) - if vnode, ok := node.Val.(valueNode); ok { - self.Value = vnode - return k - } - return append(k, self.key(node.Val)...) - case fullNode: - if node.Children[16] != nil { - self.Value = node.Children[16].(valueNode) - return []byte{16} - } - for i := 0; i < 16; i++ { - k := self.key(node.Children[i]) - if k != nil { - return append([]byte{byte(i)}, k...) - } - } - case hashNode: - rn, err := self.trie.resolveHash(node, nil, nil) - if err != nil && glog.V(logger.Error) { - glog.Errorf("Unhandled trie error: %v", err) - } - return self.key(rn) - } - return nil + return decodeCompact(key) } // nodeIteratorState represents the iteration state at one particular node of the @@ -199,25 +120,27 @@ func (it *NodeIterator) Next() bool { // step moves the iterator to the next node of the trie. func (it *NodeIterator) step() error { - // Abort if we reached the end of the iteration if it.trie == nil { + // Abort if we reached the end of the iteration return nil } - // Initialize the iterator if we've just started, or pop off the old node otherwise if len(it.stack) == 0 { - // Always start with a collapsed root + // Initialize the iterator if we've just started. root := it.trie.Hash() - it.stack = append(it.stack, &nodeIteratorState{node: hashNode(root[:]), child: -1}) - if it.stack[0].node == nil { - return fmt.Errorf("root node missing: %x", it.trie.Hash()) + state := &nodeIteratorState{node: it.trie.root, child: -1} + if root != emptyRoot { + state.hash = root } + it.stack = append(it.stack, state) } else { + // Continue iterating at the previous node otherwise. it.stack = it.stack[:len(it.stack)-1] if len(it.stack) == 0 { it.trie = nil return nil } } + // Continue iteration to the next child for { parent := it.stack[len(it.stack)-1] @@ -225,24 +148,34 @@ func (it *NodeIterator) step() error { if (ancestor == common.Hash{}) { ancestor = parent.parent } - if node, ok := parent.node.(fullNode); ok { + if node, ok := parent.node.(*fullNode); ok { // Full node, traverse all children, then the node itself if parent.child >= len(node.Children) { break } for parent.child++; parent.child < len(node.Children); parent.child++ { if current := node.Children[parent.child]; current != nil { - it.stack = append(it.stack, &nodeIteratorState{node: current, parent: ancestor, child: -1}) + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(node.flags.hash), + node: current, + parent: ancestor, + child: -1, + }) break } } - } else if node, ok := parent.node.(shortNode); ok { + } else if node, ok := parent.node.(*shortNode); ok { // Short node, traverse the pointer singleton child, then the node itself if parent.child >= 0 { break } parent.child++ - it.stack = append(it.stack, &nodeIteratorState{node: node.Val, parent: ancestor, child: -1}) + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(node.flags.hash), + node: node.Val, + parent: ancestor, + child: -1, + }) } else if hash, ok := parent.node.(hashNode); ok { // Hash node, resolve the hash child from the database, then the node itself if parent.child >= 0 { @@ -254,7 +187,12 @@ func (it *NodeIterator) step() error { if err != nil { return err } - it.stack = append(it.stack, &nodeIteratorState{hash: common.BytesToHash(hash), node: node, parent: ancestor, child: -1}) + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(hash), + node: node, + parent: ancestor, + child: -1, + }) } else { break } diff --git a/vendor/github.com/ethereum/go-ethereum/trie/node.go b/vendor/github.com/ethereum/go-ethereum/trie/node.go index b97d370be..4aa0cab65 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/node.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/node.go @@ -30,42 +30,60 @@ var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b type node interface { fstring(string) string cache() (hashNode, bool) + canUnload(cachegen, cachelimit uint16) bool } type ( fullNode struct { Children [17]node // Actual trie node data to encode/decode (needs custom encoder) - hash hashNode // Cached hash of the node to prevent rehashing (may be nil) - dirty bool // Cached flag whether the node's new or already stored + flags nodeFlag } shortNode struct { Key []byte Val node - hash hashNode // Cached hash of the node to prevent rehashing (may be nil) - dirty bool // Cached flag whether the node's new or already stored + flags nodeFlag } hashNode []byte valueNode []byte ) // EncodeRLP encodes a full node into the consensus RLP format. -func (n fullNode) EncodeRLP(w io.Writer) error { +func (n *fullNode) EncodeRLP(w io.Writer) error { return rlp.Encode(w, n.Children) } -// Cache accessors to retrieve precalculated values (avoid lengthy type switches). -func (n fullNode) cache() (hashNode, bool) { return n.hash, n.dirty } -func (n shortNode) cache() (hashNode, bool) { return n.hash, n.dirty } -func (n hashNode) cache() (hashNode, bool) { return nil, true } -func (n valueNode) cache() (hashNode, bool) { return nil, true } +func (n *fullNode) copy() *fullNode { copy := *n; return © } +func (n *shortNode) copy() *shortNode { copy := *n; return © } + +// nodeFlag contains caching-related metadata about a node. +type nodeFlag struct { + hash hashNode // cached hash of the node (may be nil) + gen uint16 // cache generation counter + dirty bool // whether the node has changes that must be written to the database +} + +// canUnload tells whether a node can be unloaded. +func (n *nodeFlag) canUnload(cachegen, cachelimit uint16) bool { + return !n.dirty && cachegen-n.gen >= cachelimit +} + +func (n *fullNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } +func (n *shortNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } +func (n hashNode) canUnload(uint16, uint16) bool { return false } +func (n valueNode) canUnload(uint16, uint16) bool { return false } + +func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } +func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } +func (n hashNode) cache() (hashNode, bool) { return nil, true } +func (n valueNode) cache() (hashNode, bool) { return nil, true } // Pretty printing. -func (n fullNode) String() string { return n.fstring("") } -func (n shortNode) String() string { return n.fstring("") } -func (n hashNode) String() string { return n.fstring("") } -func (n valueNode) String() string { return n.fstring("") } +func (n *fullNode) String() string { return n.fstring("") } +func (n *shortNode) String() string { return n.fstring("") } +func (n hashNode) String() string { return n.fstring("") } +func (n valueNode) String() string { return n.fstring("") } -func (n fullNode) fstring(ind string) string { +func (n *fullNode) fstring(ind string) string { resp := fmt.Sprintf("[\n%s ", ind) for i, node := range n.Children { if node == nil { @@ -76,7 +94,7 @@ func (n fullNode) fstring(ind string) string { } return resp + fmt.Sprintf("\n%s] ", ind) } -func (n shortNode) fstring(ind string) string { +func (n *shortNode) fstring(ind string) string { return fmt.Sprintf("{%x: %v} ", n.Key, n.Val.fstring(ind+" ")) } func (n hashNode) fstring(ind string) string { @@ -86,8 +104,8 @@ func (n valueNode) fstring(ind string) string { return fmt.Sprintf("%x ", []byte(n)) } -func mustDecodeNode(hash, buf []byte) node { - n, err := decodeNode(hash, buf) +func mustDecodeNode(hash, buf []byte, cachegen uint16) node { + n, err := decodeNode(hash, buf, cachegen) if err != nil { panic(fmt.Sprintf("node %x: %v", hash, err)) } @@ -95,7 +113,7 @@ func mustDecodeNode(hash, buf []byte) node { } // decodeNode parses the RLP encoding of a trie node. -func decodeNode(hash, buf []byte) (node, error) { +func decodeNode(hash, buf []byte, cachegen uint16) (node, error) { if len(buf) == 0 { return nil, io.ErrUnexpectedEOF } @@ -105,21 +123,22 @@ func decodeNode(hash, buf []byte) (node, error) { } switch c, _ := rlp.CountValues(elems); c { case 2: - n, err := decodeShort(hash, buf, elems) + n, err := decodeShort(hash, buf, elems, cachegen) return n, wrapError(err, "short") case 17: - n, err := decodeFull(hash, buf, elems) + n, err := decodeFull(hash, buf, elems, cachegen) return n, wrapError(err, "full") default: return nil, fmt.Errorf("invalid number of list elements: %v", c) } } -func decodeShort(hash, buf, elems []byte) (node, error) { +func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) { kbuf, rest, err := rlp.SplitString(elems) if err != nil { return nil, err } + flag := nodeFlag{hash: hash, gen: cachegen} key := compactDecode(kbuf) if key[len(key)-1] == 16 { // value node @@ -127,19 +146,19 @@ func decodeShort(hash, buf, elems []byte) (node, error) { if err != nil { return nil, fmt.Errorf("invalid value node: %v", err) } - return shortNode{key, valueNode(val), hash, false}, nil + return &shortNode{key, append(valueNode{}, val...), flag}, nil } - r, _, err := decodeRef(rest) + r, _, err := decodeRef(rest, cachegen) if err != nil { return nil, wrapError(err, "val") } - return shortNode{key, r, hash, false}, nil + return &shortNode{key, r, flag}, nil } -func decodeFull(hash, buf, elems []byte) (fullNode, error) { - n := fullNode{hash: hash} +func decodeFull(hash, buf, elems []byte, cachegen uint16) (*fullNode, error) { + n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}} for i := 0; i < 16; i++ { - cld, rest, err := decodeRef(elems) + cld, rest, err := decodeRef(elems, cachegen) if err != nil { return n, wrapError(err, fmt.Sprintf("[%d]", i)) } @@ -150,14 +169,14 @@ func decodeFull(hash, buf, elems []byte) (fullNode, error) { return n, err } if len(val) > 0 { - n.Children[16] = valueNode(val) + n.Children[16] = append(valueNode{}, val...) } return n, nil } const hashLen = len(common.Hash{}) -func decodeRef(buf []byte) (node, []byte, error) { +func decodeRef(buf []byte, cachegen uint16) (node, []byte, error) { kind, val, rest, err := rlp.Split(buf) if err != nil { return nil, buf, err @@ -170,13 +189,13 @@ func decodeRef(buf []byte) (node, []byte, error) { err := fmt.Errorf("oversized embedded node (size is %d bytes, want size < %d)", size, hashLen) return nil, buf, err } - n, err := decodeNode(nil, buf) + n, err := decodeNode(nil, buf, cachegen) return n, rest, err case kind == rlp.String && len(val) == 0: // empty node return nil, rest, nil case kind == rlp.String && len(val) == 32: - return hashNode(val), rest, nil + return append(hashNode{}, val...), rest, nil default: return nil, nil, fmt.Errorf("invalid RLP string size %d (want 0 or 32)", len(val)) } diff --git a/vendor/github.com/ethereum/go-ethereum/trie/proof.go b/vendor/github.com/ethereum/go-ethereum/trie/proof.go index 5135de047..bea5e5c09 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/proof.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/proof.go @@ -44,7 +44,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue { tn := t.root for len(key) > 0 && tn != nil { switch n := tn.(type) { - case shortNode: + case *shortNode: if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { // The trie doesn't contain the key. tn = nil @@ -53,7 +53,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue { key = key[len(n.Key):] } nodes = append(nodes, n) - case fullNode: + case *fullNode: tn = n.Children[key[0]] key = key[1:] nodes = append(nodes, n) @@ -70,15 +70,13 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue { panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) } } - if t.hasher == nil { - t.hasher = newHasher() - } + hasher := newHasher(0, 0) proof := make([]rlp.RawValue, 0, len(nodes)) for i, n := range nodes { // Don't bother checking for errors here since hasher panics // if encoding doesn't work and we're not writing to any database. - n, _, _ = t.hasher.hashChildren(n, nil) - hn, _ := t.hasher.store(n, nil, false) + n, _, _ = hasher.hashChildren(n, nil) + hn, _ := hasher.store(n, nil, false) if _, ok := hn.(hashNode); ok || i == 0 { // If the node's database encoding is a hash (or is the // root node), it becomes a proof element. @@ -103,7 +101,7 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value if !bytes.Equal(sha.Sum(nil), wantHash) { return nil, fmt.Errorf("bad proof node %d: hash mismatch", i) } - n, err := decodeNode(wantHash, buf) + n, err := decodeNode(wantHash, buf, 0) if err != nil { return nil, fmt.Errorf("bad proof node %d: %v", i, err) } @@ -132,13 +130,13 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value func get(tn node, key []byte) ([]byte, node) { for len(key) > 0 { switch n := tn.(type) { - case shortNode: + case *shortNode: if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { return nil, nil } tn = n.Val key = key[len(n.Key):] - case fullNode: + case *fullNode: tn = n.Children[key[0]] key = key[1:] case hashNode: diff --git a/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go index 1d027c102..4d9ebe4d3 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go @@ -17,16 +17,15 @@ package trie import ( - "hash" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) var secureKeyPrefix = []byte("secure-key-") +const secureKeyLength = 11 + 32 // Length of the above prefix + 32byte hash + // SecureTrie wraps a trie with key hashing. In a secure trie, all // access operations hash the key using keccak256. This prevents // calling code from creating long chains of nodes that @@ -38,12 +37,11 @@ var secureKeyPrefix = []byte("secure-key-") // // SecureTrie is not safe for concurrent use. type SecureTrie struct { - *Trie - - hash hash.Hash - hashKeyBuf []byte - secKeyBuf []byte - secKeyCache map[string][]byte + trie Trie + hashKeyBuf [secureKeyLength]byte + secKeyBuf [200]byte + secKeyCache map[string][]byte + secKeyCacheOwner *SecureTrie // Pointer to self, replace the key cache on mismatch } // NewSecure creates a trie with an existing root node from db. @@ -51,8 +49,12 @@ type SecureTrie struct { // If root is the zero hash or the sha3 hash of an empty string, the // trie is initially empty. Otherwise, New will panic if db is nil // and returns MissingNodeError if the root node cannot be found. +// // Accessing the trie loads nodes from db on demand. -func NewSecure(root common.Hash, db Database) (*SecureTrie, error) { +// Loaded nodes are kept around until their 'cache generation' expires. +// A new cache generation is created by each call to Commit. +// cachelimit sets the number of past cache generations to keep. +func NewSecure(root common.Hash, db Database, cachelimit uint16) (*SecureTrie, error) { if db == nil { panic("NewSecure called with nil database") } @@ -60,10 +62,8 @@ func NewSecure(root common.Hash, db Database) (*SecureTrie, error) { if err != nil { return nil, err } - return &SecureTrie{ - Trie: trie, - secKeyCache: make(map[string][]byte), - }, nil + trie.SetCacheLimit(cachelimit) + return &SecureTrie{trie: *trie}, nil } // Get returns the value for key stored in the trie. @@ -80,7 +80,7 @@ func (t *SecureTrie) Get(key []byte) []byte { // The value bytes must not be modified by the caller. // If a node was not found in the database, a MissingNodeError is returned. func (t *SecureTrie) TryGet(key []byte) ([]byte, error) { - return t.Trie.TryGet(t.hashKey(key)) + return t.trie.TryGet(t.hashKey(key)) } // Update associates key with value in the trie. Subsequent calls to @@ -105,11 +105,11 @@ func (t *SecureTrie) Update(key, value []byte) { // If a node was not found in the database, a MissingNodeError is returned. func (t *SecureTrie) TryUpdate(key, value []byte) error { hk := t.hashKey(key) - err := t.Trie.TryUpdate(hk, value) + err := t.trie.TryUpdate(hk, value) if err != nil { return err } - t.secKeyCache[string(hk)] = common.CopyBytes(key) + t.getSecKeyCache()[string(hk)] = common.CopyBytes(key) return nil } @@ -124,17 +124,17 @@ func (t *SecureTrie) Delete(key []byte) { // If a node was not found in the database, a MissingNodeError is returned. func (t *SecureTrie) TryDelete(key []byte) error { hk := t.hashKey(key) - delete(t.secKeyCache, string(hk)) - return t.Trie.TryDelete(hk) + delete(t.getSecKeyCache(), string(hk)) + return t.trie.TryDelete(hk) } // GetKey returns the sha3 preimage of a hashed key that was // previously used to store a value. func (t *SecureTrie) GetKey(shaKey []byte) []byte { - if key, ok := t.secKeyCache[string(shaKey)]; ok { + if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { return key } - key, _ := t.Trie.db.Get(t.secKey(shaKey)) + key, _ := t.trie.db.Get(t.secKey(shaKey)) return key } @@ -144,7 +144,23 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte { // Committing flushes nodes from memory. Subsequent Get calls will load nodes // from the database. func (t *SecureTrie) Commit() (root common.Hash, err error) { - return t.CommitTo(t.db) + return t.CommitTo(t.trie.db) +} + +func (t *SecureTrie) Hash() common.Hash { + return t.trie.Hash() +} + +func (t *SecureTrie) Root() []byte { + return t.trie.Root() +} + +func (t *SecureTrie) Iterator() *Iterator { + return t.trie.Iterator() +} + +func (t *SecureTrie) NodeIterator() *NodeIterator { + return NewNodeIterator(&t.trie) } // CommitTo writes all nodes and the secure hash pre-images to the given database. @@ -154,7 +170,7 @@ func (t *SecureTrie) Commit() (root common.Hash, err error) { // the trie's database. Calling code must ensure that the changes made to db are // written back to the trie's attached database before using the trie. func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { - if len(t.secKeyCache) > 0 { + if len(t.getSecKeyCache()) > 0 { for hk, key := range t.secKeyCache { if err := db.Put(t.secKey([]byte(hk)), key); err != nil { return common.Hash{}, err @@ -162,27 +178,37 @@ func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { } t.secKeyCache = make(map[string][]byte) } - n, clean, err := t.hashRoot(db) - if err != nil { - return (common.Hash{}), err - } - t.root = clean - return common.BytesToHash(n.(hashNode)), nil + return t.trie.CommitTo(db) } +// secKey returns the database key for the preimage of key, as an ephemeral buffer. +// The caller must not hold onto the return value because it will become +// invalid on the next call to hashKey or secKey. func (t *SecureTrie) secKey(key []byte) []byte { - t.secKeyBuf = append(t.secKeyBuf[:0], secureKeyPrefix...) - t.secKeyBuf = append(t.secKeyBuf, key...) - return t.secKeyBuf + buf := append(t.secKeyBuf[:0], secureKeyPrefix...) + buf = append(buf, key...) + return buf } +// hashKey returns the hash of key as an ephemeral buffer. +// The caller must not hold onto the return value because it will become +// invalid on the next call to hashKey or secKey. func (t *SecureTrie) hashKey(key []byte) []byte { - if t.hash == nil { - t.hash = sha3.NewKeccak256() - t.hashKeyBuf = make([]byte, 32) - } - t.hash.Reset() - t.hash.Write(key) - t.hashKeyBuf = t.hash.Sum(t.hashKeyBuf[:0]) - return t.hashKeyBuf + h := newHasher(0, 0) + h.sha.Reset() + h.sha.Write(key) + buf := h.sha.Sum(t.hashKeyBuf[:0]) + returnHasherToPool(h) + return buf +} + +// getSecKeyCache returns the current secure key cache, creating a new one if +// ownership changed (i.e. the current secure trie is a copy of another owning +// the actual cache). +func (t *SecureTrie) getSecKeyCache() map[string][]byte { + if t != t.secKeyCacheOwner { + t.secKeyCacheOwner = t + t.secKeyCache = make(map[string][]byte) + } + return t.secKeyCache } diff --git a/vendor/github.com/ethereum/go-ethereum/trie/sync.go b/vendor/github.com/ethereum/go-ethereum/trie/sync.go index 6e9e029b9..2158ab750 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/sync.go @@ -31,9 +31,9 @@ var ErrNotRequested = errors.New("not requested") // request represents a scheduled or already in-flight state retrieval request. type request struct { - hash common.Hash // Hash of the node data content to retrieve - data []byte // Data content of the node, cached until all subtrees complete - object *node // Target node to populate with retrieved data (hashnode originally) + hash common.Hash // Hash of the node data content to retrieve + data []byte // Data content of the node, cached until all subtrees complete + raw bool // Whether this is a raw entry (code) or a trie node parents []*request // Parent state nodes referencing this entry (notify all upon completion) depth int // Depth level within the trie the node is located to prioritise DFS @@ -82,13 +82,11 @@ func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, c } key := root.Bytes() blob, _ := s.database.Get(key) - if local, err := decodeNode(key, blob); local != nil && err == nil { + if local, err := decodeNode(key, blob, 0); local != nil && err == nil { return } // Assemble the new sub-trie sync request - node := node(hashNode(root.Bytes())) req := &request{ - object: &node, hash: root, depth: depth, callback: callback, @@ -120,6 +118,7 @@ func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) // Assemble the new sub-trie sync request req := &request{ hash: hash, + raw: true, depth: depth, } // If this sub-trie has a designated parent, link them together @@ -143,35 +142,40 @@ func (s *TrieSync) Missing(max int) []common.Hash { return requests } -// Process injects a batch of retrieved trie nodes data. -func (s *TrieSync) Process(results []SyncResult) (int, error) { +// Process injects a batch of retrieved trie nodes data, returning if something +// was committed to the database and also the index of an entry if processing of +// it failed. +func (s *TrieSync) Process(results []SyncResult) (bool, int, error) { + committed := false + for i, item := range results { // If the item was not requested, bail out request := s.requests[item.Hash] if request == nil { - return i, ErrNotRequested + return committed, i, ErrNotRequested } // If the item is a raw entry request, commit directly - if request.object == nil { + if request.raw { request.data = item.Data s.commit(request, nil) + committed = true continue } // Decode the node data content and update the request - node, err := decodeNode(item.Hash[:], item.Data) + node, err := decodeNode(item.Hash[:], item.Data, 0) if err != nil { - return i, err + return committed, i, err } - *request.object = node request.data = item.Data // Create and schedule a request for all the children nodes - requests, err := s.children(request) + requests, err := s.children(request, node) if err != nil { - return i, err + return committed, i, err } if len(requests) == 0 && request.deps == 0 { s.commit(request, nil) + committed = true continue } request.deps += len(requests) @@ -179,7 +183,7 @@ func (s *TrieSync) Process(results []SyncResult) (int, error) { s.schedule(child) } } - return 0, nil + return committed, 0, nil } // Pending returns the number of state entries currently pending for download. @@ -203,25 +207,25 @@ func (s *TrieSync) schedule(req *request) { // children retrieves all the missing children of a state trie entry for future // retrieval scheduling. -func (s *TrieSync) children(req *request) ([]*request, error) { +func (s *TrieSync) children(req *request, object node) ([]*request, error) { // Gather all the children of the node, irrelevant whether known or not type child struct { - node *node + node node depth int } children := []child{} - switch node := (*req.object).(type) { - case shortNode: + switch node := (object).(type) { + case *shortNode: children = []child{{ - node: &node.Val, + node: node.Val, depth: req.depth + len(node.Key), }} - case fullNode: + case *fullNode: for i := 0; i < 17; i++ { if node.Children[i] != nil { children = append(children, child{ - node: &node.Children[i], + node: node.Children[i], depth: req.depth + 1, }) } @@ -234,23 +238,21 @@ func (s *TrieSync) children(req *request) ([]*request, error) { for _, child := range children { // Notify any external watcher of a new key/value node if req.callback != nil { - if node, ok := (*child.node).(valueNode); ok { + if node, ok := (child.node).(valueNode); ok { if err := req.callback(node, req.hash); err != nil { return nil, err } } } // If the child references another node, resolve or schedule - if node, ok := (*child.node).(hashNode); ok { + if node, ok := (child.node).(hashNode); ok { // Try to resolve the node from the local database blob, _ := s.database.Get(node) - if local, err := decodeNode(node[:], blob); local != nil && err == nil { - *child.node = local + if local, err := decodeNode(node[:], blob, 0); local != nil && err == nil { continue } // Locally unknown node, schedule for retrieval requests = append(requests, &request{ - object: child.node, hash: common.BytesToHash(node), parents: []*request{req}, depth: child.depth, diff --git a/vendor/github.com/ethereum/go-ethereum/trie/trie.go b/vendor/github.com/ethereum/go-ethereum/trie/trie.go index a530e7b2a..035a80e74 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/trie.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/trie.go @@ -20,32 +20,42 @@ package trie import ( "bytes" "fmt" - "hash" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rlp" + "github.com/rcrowley/go-metrics" ) -const defaultCacheCapacity = 800 - var ( - // The global cache stores decoded trie nodes by hash as they get loaded. - globalCache = newARC(defaultCacheCapacity) - // This is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // This is the known hash of an empty state trie entry. - emptyState = crypto.Keccak256Hash(nil) + emptyState common.Hash ) -// ClearGlobalCache clears the global trie cache -func ClearGlobalCache() { - globalCache.Clear() +var ( + cacheMissCounter = metrics.NewRegisteredCounter("trie/cachemiss", nil) + cacheUnloadCounter = metrics.NewRegisteredCounter("trie/cacheunload", nil) +) + +// CacheMisses retrieves a global counter measuring the number of cache misses +// the trie did since process startup. This isn't useful for anything apart from +// trie debugging purposes. +func CacheMisses() int64 { + return cacheMissCounter.Count() +} + +// CacheUnloads retrieves a global counter measuring the number of cache unloads +// the trie did since process startup. This isn't useful for anything apart from +// trie debugging purposes. +func CacheUnloads() int64 { + return cacheUnloadCounter.Count() +} + +func init() { + sha3.NewKeccak256().Sum(emptyState[:0]) } // Database must be implemented by backing stores for the trie. @@ -72,7 +82,23 @@ type Trie struct { root node db Database originalRoot common.Hash - *hasher + + // Cache generation values. + // cachegen increase by one with each commit operation. + // new nodes are tagged with the current generation and unloaded + // when their generation is older than than cachegen-cachelimit. + cachegen, cachelimit uint16 +} + +// SetCacheLimit sets the number of 'cache generations' to keep. +// A cache generations is created by a call to Commit. +func (t *Trie) SetCacheLimit(l uint16) { + t.cachelimit = l +} + +// newFlag returns the cache flag value for a newly created node. +func (t *Trie) newFlag() nodeFlag { + return nodeFlag{dirty: true, gen: t.cachegen} } // New creates a trie with an existing root node from db. @@ -87,13 +113,11 @@ func New(root common.Hash, db Database) (*Trie, error) { if db == nil { panic("trie.New: cannot use existing root without a database") } - if v, _ := trie.db.Get(root[:]); len(v) == 0 { - return nil, &MissingNodeError{ - RootHash: root, - NodeHash: root, - } + rootnode, err := trie.resolveHash(root[:], nil, nil) + if err != nil { + return nil, err } - trie.root = hashNode(root.Bytes()) + trie.root = rootnode } return trie, nil } @@ -118,32 +142,49 @@ func (t *Trie) Get(key []byte) []byte { // If a node was not found in the database, a MissingNodeError is returned. func (t *Trie) TryGet(key []byte) ([]byte, error) { key = compactHexDecode(key) - pos := 0 - tn := t.root - for pos < len(key) { - switch n := tn.(type) { - case shortNode: - if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { - return nil, nil - } - tn = n.Val - pos += len(n.Key) - case fullNode: - tn = n.Children[key[pos]] - pos++ - case nil: - return nil, nil - case hashNode: - var err error - tn, err = t.resolveHash(n, key[:pos], key[pos:]) - if err != nil { - return nil, err - } - default: - panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) - } + value, newroot, didResolve, err := t.tryGet(t.root, key, 0) + if err == nil && didResolve { + t.root = newroot + } + return value, err +} + +func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) { + switch n := (origNode).(type) { + case nil: + return nil, nil, false, nil + case valueNode: + return n, n, false, nil + case *shortNode: + if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { + // key not found in trie + return nil, n, false, nil + } + value, newnode, didResolve, err = t.tryGet(n.Val, key, pos+len(n.Key)) + if err == nil && didResolve { + n = n.copy() + n.Val = newnode + n.flags.gen = t.cachegen + } + return value, n, didResolve, err + case *fullNode: + value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1) + if err == nil && didResolve { + n = n.copy() + n.flags.gen = t.cachegen + n.Children[key[pos]] = newnode + } + return value, n, didResolve, err + case hashNode: + child, err := t.resolveHash(n, key[:pos], key[pos:]) + if err != nil { + return nil, n, true, err + } + value, newnode, _, err := t.tryGet(child, key, pos) + return value, newnode, true, err + default: + panic(fmt.Sprintf("%T: invalid node: %v", origNode, origNode)) } - return tn.(valueNode), nil } // Update associates key with value in the trie. Subsequent calls to @@ -192,22 +233,19 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error return true, value, nil } switch n := n.(type) { - case shortNode: + case *shortNode: matchlen := prefixLen(key, n.Key) // If the whole key matches, keep this short node as is // and only update the value. if matchlen == len(n.Key) { dirty, nn, err := t.insert(n.Val, append(prefix, key[:matchlen]...), key[matchlen:], value) - if err != nil { - return false, nil, err + if !dirty || err != nil { + return false, n, err } - if !dirty { - return false, n, nil - } - return true, shortNode{n.Key, nn, nil, true}, nil + return true, &shortNode{n.Key, nn, t.newFlag()}, nil } // Otherwise branch out at the index where they differ. - branch := fullNode{dirty: true} + branch := &fullNode{flags: t.newFlag()} var err error _, branch.Children[n.Key[matchlen]], err = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val) if err != nil { @@ -222,21 +260,20 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error return true, branch, nil } // Otherwise, replace it with a short node leading up to the branch. - return true, shortNode{key[:matchlen], branch, nil, true}, nil + return true, &shortNode{key[:matchlen], branch, t.newFlag()}, nil - case fullNode: + case *fullNode: dirty, nn, err := t.insert(n.Children[key[0]], append(prefix, key[0]), key[1:], value) - if err != nil { - return false, nil, err + if !dirty || err != nil { + return false, n, err } - if !dirty { - return false, n, nil - } - n.Children[key[0]], n.hash, n.dirty = nn, nil, true + n = n.copy() + n.flags = t.newFlag() + n.Children[key[0]] = nn return true, n, nil case nil: - return true, shortNode{key, value, nil, true}, nil + return true, &shortNode{key, value, t.newFlag()}, nil case hashNode: // We've hit a part of the trie that isn't loaded yet. Load @@ -247,11 +284,8 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error return false, nil, err } dirty, nn, err := t.insert(rn, prefix, key, value) - if err != nil { - return false, nil, err - } - if !dirty { - return false, rn, nil + if !dirty || err != nil { + return false, rn, err } return true, nn, nil @@ -284,7 +318,7 @@ func (t *Trie) TryDelete(key []byte) error { // nodes on the way up after deleting recursively. func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { switch n := n.(type) { - case shortNode: + case *shortNode: matchlen := prefixLen(key, n.Key) if matchlen < len(n.Key) { return false, n, nil // don't replace n on mismatch @@ -297,34 +331,30 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { // subtrie must contain at least two other values with keys // longer than n.Key. dirty, child, err := t.delete(n.Val, append(prefix, key[:len(n.Key)]...), key[len(n.Key):]) - if err != nil { - return false, nil, err - } - if !dirty { - return false, n, nil + if !dirty || err != nil { + return false, n, err } switch child := child.(type) { - case shortNode: + case *shortNode: // Deleting from the subtrie reduced it to another // short node. Merge the nodes to avoid creating a // shortNode{..., shortNode{...}}. Use concat (which // always creates a new slice) instead of append to // avoid modifying n.Key since it might be shared with // other nodes. - return true, shortNode{concat(n.Key, child.Key...), child.Val, nil, true}, nil + return true, &shortNode{concat(n.Key, child.Key...), child.Val, t.newFlag()}, nil default: - return true, shortNode{n.Key, child, nil, true}, nil + return true, &shortNode{n.Key, child, t.newFlag()}, nil } - case fullNode: + case *fullNode: dirty, nn, err := t.delete(n.Children[key[0]], append(prefix, key[0]), key[1:]) - if err != nil { - return false, nil, err + if !dirty || err != nil { + return false, n, err } - if !dirty { - return false, n, nil - } - n.Children[key[0]], n.hash, n.dirty = nn, nil, true + n = n.copy() + n.flags = t.newFlag() + n.Children[key[0]] = nn // Check how many non-nil entries are left after deleting and // reduce the full node to a short node if only one entry is @@ -358,18 +388,21 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { if err != nil { return false, nil, err } - if cnode, ok := cnode.(shortNode); ok { + if cnode, ok := cnode.(*shortNode); ok { k := append([]byte{byte(pos)}, cnode.Key...) - return true, shortNode{k, cnode.Val, nil, true}, nil + return true, &shortNode{k, cnode.Val, t.newFlag()}, nil } } // Otherwise, n is replaced by a one-nibble short node // containing the child. - return true, shortNode{[]byte{byte(pos)}, n.Children[pos], nil, true}, nil + return true, &shortNode{[]byte{byte(pos)}, n.Children[pos], t.newFlag()}, nil } // n still contains at least two values and cannot be reduced. return true, n, nil + case valueNode: + return true, nil, nil + case nil: return false, nil, nil @@ -382,11 +415,8 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { return false, nil, err } dirty, nn, err := t.delete(rn, prefix, key) - if err != nil { - return false, nil, err - } - if !dirty { - return false, rn, nil + if !dirty || err != nil { + return false, rn, err } return true, nn, nil @@ -410,9 +440,8 @@ func (t *Trie) resolve(n node, prefix, suffix []byte) (node, error) { } func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { - if v, ok := globalCache.Get(n); ok { - return v, nil - } + cacheMissCounter.Inc(1) + enc, err := t.db.Get(n) if err != nil || enc == nil { return nil, &MissingNodeError{ @@ -423,10 +452,7 @@ func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { SuffixLen: len(suffix), } } - dec := mustDecodeNode(n, enc) - if dec != nil { - globalCache.Put(n, dec) - } + dec := mustDecodeNode(n, enc, t.cachegen) return dec, nil } @@ -467,6 +493,7 @@ func (t *Trie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { return (common.Hash{}), err } t.root = cached + t.cachegen++ return common.BytesToHash(hash.(hashNode)), nil } @@ -474,127 +501,7 @@ func (t *Trie) hashRoot(db DatabaseWriter) (node, node, error) { if t.root == nil { return hashNode(emptyRoot.Bytes()), nil, nil } - if t.hasher == nil { - t.hasher = newHasher() - } - return t.hasher.hash(t.root, db, true) -} - -type hasher struct { - tmp *bytes.Buffer - sha hash.Hash -} - -func newHasher() *hasher { - return &hasher{tmp: new(bytes.Buffer), sha: sha3.NewKeccak256()} -} - -// hash collapses a node down into a hash node, also returning a copy of the -// original node initialzied with the computed hash to replace the original one. -func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { - // If we're not storing the node, just hashing, use avaialble cached data - if hash, dirty := n.cache(); hash != nil && (db == nil || !dirty) { - return hash, n, nil - } - // Trie not processed yet or needs storage, walk the children - collapsed, cached, err := h.hashChildren(n, db) - if err != nil { - return hashNode{}, n, err - } - hashed, err := h.store(collapsed, db, force) - if err != nil { - return hashNode{}, n, err - } - // Cache the hash and RLP blob of the ndoe for later reuse - if hash, ok := hashed.(hashNode); ok && !force { - switch cached := cached.(type) { - case shortNode: - cached.hash = hash - if db != nil { - cached.dirty = false - } - return hashed, cached, nil - case fullNode: - cached.hash = hash - if db != nil { - cached.dirty = false - } - return hashed, cached, nil - } - } - return hashed, cached, nil -} - -// hashChildren replaces the children of a node with their hashes if the encoded -// size of the child is larger than a hash, returning the collapsed node as well -// as a replacement for the original node with the child hashes cached in. -func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { - var err error - - switch n := original.(type) { - case shortNode: - // Hash the short node's child, caching the newly hashed subtree - cached := n - cached.Key = common.CopyBytes(cached.Key) - - n.Key = compactEncode(n.Key) - if _, ok := n.Val.(valueNode); !ok { - if n.Val, cached.Val, err = h.hash(n.Val, db, false); err != nil { - return n, original, err - } - } - if n.Val == nil { - n.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings. - } - return n, cached, nil - - case fullNode: - // Hash the full node's children, caching the newly hashed subtrees - cached := fullNode{dirty: n.dirty} - - for i := 0; i < 16; i++ { - if n.Children[i] != nil { - if n.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false); err != nil { - return n, original, err - } - } else { - n.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings. - } - } - cached.Children[16] = n.Children[16] - if n.Children[16] == nil { - n.Children[16] = valueNode(nil) - } - return n, cached, nil - - default: - // Value and hash nodes don't have children so they're left as were - return n, original, nil - } -} - -func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { - // Don't store hashes or empty nodes. - if _, isHash := n.(hashNode); n == nil || isHash { - return n, nil - } - // Generate the RLP encoding of the node - h.tmp.Reset() - if err := rlp.Encode(h.tmp, n); err != nil { - panic("encode error: " + err.Error()) - } - if h.tmp.Len() < 32 && !force { - return n, nil // Nodes smaller than 32 bytes are stored inside their parent - } - // Larger nodes are replaced by their hash and stored in the database. - hash, _ := n.cache() - if hash == nil { - h.sha.Reset() - h.sha.Write(h.tmp.Bytes()) - hash = hashNode(h.sha.Sum(nil)) - } - if db != nil { - return hash, db.Put(hash, h.tmp.Bytes()) - } - return hash, nil + h := newHasher(t.cachegen, t.cachelimit) + defer returnHasherToPool(h) + return h.hash(t.root, db, true) } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor.conf b/vendor/github.com/ethereum/go-ethereum/vendor.conf new file mode 100644 index 000000000..30c96e81b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor.conf @@ -0,0 +1,42 @@ +# package +github.com/ethereum/go-ethereum + +# import +github.com/Azure/azure-sdk-for-go v5.0.0-beta-5-gbd73d95 +github.com/aristanetworks/goarista ockafka-v0.0.2-7-g306a19f +github.com/cespare/cp 165db2f +github.com/davecgh/go-spew v1.0.0-9-g346938d +github.com/ethereum/ethash v23.1-249-g214d4c0 +github.com/fatih/color v1.1.0-4-gbf82308 +github.com/gizak/termui d29684e +github.com/golang/snappy d9eb7a3 +github.com/hashicorp/golang-lru 0a025b7 +github.com/huin/goupnp 949b8a7 +github.com/jackpal/go-nat-pmp v1.0.1-4-g1fa385a +github.com/mattn/go-colorable v0.0.6-7-g6e26b35 +github.com/mattn/go-isatty 66b8e73 +github.com/mattn/go-runewidth v0.0.1-10-g737072b +github.com/mitchellh/go-wordwrap ad45545 +github.com/nsf/termbox-go b6acae5 +github.com/pborman/uuid v1.0-17-g3d4f2ba +github.com/peterh/liner 3c5f577 +github.com/rcrowley/go-metrics ab2277b +github.com/rjeczalik/notify 7e20c15 +github.com/robertkrimen/otto bf1c379 +github.com/rs/cors v1.0 +github.com/rs/xhandler v1.0-1-ged27b6f +github.com/syndtr/goleveldb 6b4daa5 +golang.org/x/crypto 9477e0b +golang.org/x/net 4bb47a1 +golang.org/x/sys c200b10 +golang.org/x/text a8b3843 +golang.org/x/tools 1529f88 +gopkg.in/check.v1 4f90aea +gopkg.in/fatih/set.v0 v0.1.0-3-g27c4092 +gopkg.in/karalabe/cookiejar.v2 8dcd6a7 +gopkg.in/natefinch/npipe.v2 c1b8fa8 +gopkg.in/sourcemap.v1 v1.0.3 +gopkg.in/urfave/cli.v1 v1.18.1 + +# exclude +-golang.org/x/net/context diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go b/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go new file mode 100644 index 000000000..50a8eb34a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/shhapi/api.go @@ -0,0 +1,504 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package shhapi + +import ( + "encoding/json" + "errors" + "fmt" + mathrand "math/rand" + + "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" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/whisper/whisperv5" +) + +var whisperOffLineErr = errors.New("whisper is offline") + +// PublicWhisperAPI provides the whisper RPC service. +type PublicWhisperAPI struct { + whisper *whisperv5.Whisper +} + +// NewPublicWhisperAPI create a new RPC whisper service. +func NewPublicWhisperAPI() *PublicWhisperAPI { + w := whisperv5.NewWhisper(nil) + return &PublicWhisperAPI{whisper: w} +} + +// APIs returns the RPC descriptors the Whisper implementation offers +func APIs() []rpc.API { + return []rpc.API{ + { + Namespace: whisperv5.ProtocolName, + Version: whisperv5.ProtocolVersionStr, + Service: NewPublicWhisperAPI(), + Public: true, + }, + } +} + +// Version returns the Whisper version this node offers. +func (api *PublicWhisperAPI) Version() (*rpc.HexNumber, error) { + if api.whisper == nil { + return rpc.NewHexNumber(0), whisperOffLineErr + } + return rpc.NewHexNumber(api.whisper.Version()), nil +} + +// MarkPeerTrusted marks specific peer trusted, which will allow it +// to send historic (expired) messages. +func (api *PublicWhisperAPI) MarkPeerTrusted(peerID rpc.HexBytes) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.MarkPeerTrusted(peerID) +} + +// RequestHistoricMessages requests the peer to deliver the old (expired) messages. +// data contains parameters (time frame, payment details, etc.), required +// by the remote email-like server. Whisper is not aware about the data format, +// it will just forward the raw data to the server. +func (api *PublicWhisperAPI) RequestHistoricMessages(peerID rpc.HexBytes, data rpc.HexBytes) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.RequestHistoricMessages(peerID, data) +} + +// HasIdentity checks if the whisper node is configured with the private key +// of the specified public pair. +func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) { + if api.whisper == nil { + return false, whisperOffLineErr + } + return api.whisper.HasIdentity(identity), nil +} + +// DeleteIdentity deletes the specifies key if it exists. +func (api *PublicWhisperAPI) DeleteIdentity(identity string) error { + if api.whisper == nil { + return whisperOffLineErr + } + api.whisper.DeleteIdentity(identity) + return nil +} + +// NewIdentity generates a new cryptographic identity for the client, and injects +// it into the known identities for message decryption. +func (api *PublicWhisperAPI) NewIdentity() (string, error) { + if api.whisper == nil { + return "", whisperOffLineErr + } + identity := api.whisper.NewIdentity() + return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil +} + +// GenerateSymKey generates a random symmetric key and stores it under +// the 'name' id. Will be used in the future for session key exchange. +func (api *PublicWhisperAPI) GenerateSymKey(name string) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.GenerateSymKey(name) +} + +// AddSymKey stores the key under the 'name' id. +func (api *PublicWhisperAPI) AddSymKey(name string, key []byte) error { + if api.whisper == nil { + return whisperOffLineErr + } + return api.whisper.AddSymKey(name, key) +} + +// HasSymKey returns true if there is a key associated with the name string. +// Otherwise returns false. +func (api *PublicWhisperAPI) HasSymKey(name string) (bool, error) { + if api.whisper == nil { + return false, whisperOffLineErr + } + res := api.whisper.HasSymKey(name) + return res, nil +} + +// DeleteSymKey deletes the key associated with the name string if it exists. +func (api *PublicWhisperAPI) DeleteSymKey(name string) error { + if api.whisper == nil { + return whisperOffLineErr + } + api.whisper.DeleteSymKey(name) + return nil +} + +// NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. +// Returns the ID of the newly created Filter. +func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (*rpc.HexNumber, error) { + if api.whisper == nil { + return nil, whisperOffLineErr + } + + filter := whisperv5.Filter{ + Src: crypto.ToECDSAPub(args.From), + KeySym: api.whisper.GetSymKey(args.KeyName), + PoW: args.PoW, + Messages: make(map[common.Hash]*whisperv5.ReceivedMessage), + AcceptP2P: args.AcceptP2P, + } + + if len(filter.KeySym) > 0 { + filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) + } + + for _, t := range args.Topics { + filter.Topics = append(filter.Topics, t) + } + + if len(args.Topics) == 0 { + info := "NewFilter: at least one topic must be specified" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.KeyName) != 0 && len(filter.KeySym) == 0 { + info := "NewFilter: key was not found by name: " + args.KeyName + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.To) == 0 && len(filter.KeySym) == 0 { + info := "NewFilter: filter must contain either symmetric or asymmetric key" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.To) != 0 && len(filter.KeySym) != 0 { + info := "NewFilter: filter must not contain both symmetric and asymmetric key" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + + if len(args.To) > 0 { + dst := crypto.ToECDSAPub(args.To) + if !whisperv5.ValidatePublicKey(dst) { + info := "NewFilter: Invalid 'To' address" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) + if filter.KeyAsym == nil { + info := "NewFilter: non-existent identity provided" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + } + + if len(args.From) > 0 { + if !whisperv5.ValidatePublicKey(filter.Src) { + info := "NewFilter: Invalid 'From' address" + glog.V(logger.Error).Infof(info) + return nil, errors.New(info) + } + } + + id := api.whisper.Watch(&filter) + return rpc.NewHexNumber(id), nil +} + +// UninstallFilter disables and removes an existing filter. +func (api *PublicWhisperAPI) UninstallFilter(filterId rpc.HexNumber) { + api.whisper.Unwatch(filterId.Int()) +} + +// GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. +func (api *PublicWhisperAPI) GetFilterChanges(filterId rpc.HexNumber) []WhisperMessage { + f := api.whisper.GetFilter(filterId.Int()) + if f != nil { + newMail := f.Retrieve() + return toWhisperMessages(newMail) + } + return toWhisperMessages(nil) +} + +// GetMessages retrieves all the known messages that match a specific filter. +func (api *PublicWhisperAPI) GetMessages(filterId rpc.HexNumber) []WhisperMessage { + all := api.whisper.Messages(filterId.Int()) + return toWhisperMessages(all) +} + +// toWhisperMessages converts a Whisper message to a RPC whisper message. +func toWhisperMessages(messages []*whisperv5.ReceivedMessage) []WhisperMessage { + msgs := make([]WhisperMessage, len(messages)) + for i, msg := range messages { + msgs[i] = NewWhisperMessage(msg) + } + return msgs +} + +// Post creates a whisper message and injects it into the network for distribution. +func (api *PublicWhisperAPI) Post(args PostArgs) error { + if api.whisper == nil { + return whisperOffLineErr + } + + params := whisperv5.MessageParams{ + TTL: args.TTL, + Dst: crypto.ToECDSAPub(args.To), + KeySym: api.whisper.GetSymKey(args.KeyName), + Topic: args.Topic, + Payload: args.Payload, + Padding: args.Padding, + WorkTime: args.WorkTime, + PoW: args.PoW, + } + + if len(args.From) > 0 { + pub := crypto.ToECDSAPub(args.From) + if !whisperv5.ValidatePublicKey(pub) { + info := "Post: Invalid 'From' address" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + params.Src = api.whisper.GetIdentity(string(args.From)) + if params.Src == nil { + info := "Post: non-existent identity provided" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + } + + filter := api.whisper.GetFilter(args.FilterID) + if filter == nil && args.FilterID > -1 { + info := fmt.Sprintf("Post: wrong filter id %d", args.FilterID) + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if filter != nil { + // get the missing fields from the filter + if params.KeySym == nil && filter.KeySym != nil { + params.KeySym = filter.KeySym + } + if params.Src == nil && filter.Src != nil { + params.Src = filter.KeyAsym + } + if (params.Topic == whisperv5.TopicType{}) { + sz := len(filter.Topics) + if sz < 1 { + info := fmt.Sprintf("Post: no topics in filter # %d", args.FilterID) + glog.V(logger.Error).Infof(info) + return errors.New(info) + } else if sz == 1 { + params.Topic = filter.Topics[0] + } else { + // choose randomly + rnd := mathrand.Intn(sz) + params.Topic = filter.Topics[rnd] + } + } + } + + // validate + if len(args.KeyName) != 0 && len(params.KeySym) == 0 { + info := "Post: key was not found by name: " + args.KeyName + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if len(args.To) == 0 && len(args.KeyName) == 0 { + info := "Post: message must be encrypted either symmetrically or asymmetrically" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if len(args.To) != 0 && len(args.KeyName) != 0 { + info := "Post: ambigous encryption method requested" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if len(args.To) > 0 { + if !whisperv5.ValidatePublicKey(params.Dst) { + info := "Post: Invalid 'To' address" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + } + + // encrypt and send + message := whisperv5.NewSentMessage(¶ms) + envelope, err := message.Wrap(¶ms) + if err != nil { + glog.V(logger.Error).Infof(err.Error()) + return err + } + if len(envelope.Data) > whisperv5.MaxMessageLength { + info := "Post: message is too big" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + if (envelope.Topic == whisperv5.TopicType{} && envelope.IsSymmetric()) { + info := "Post: topic is missing for symmetric encryption" + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + + if args.PeerID != nil { + return api.whisper.SendP2PMessage(args.PeerID, envelope) + } + + return api.whisper.Send(envelope) +} + +type PostArgs struct { + TTL uint32 `json:"ttl"` + From rpc.HexBytes `json:"from"` + To rpc.HexBytes `json:"to"` + KeyName string `json:"keyname"` + Topic whisperv5.TopicType `json:"topic"` + Padding rpc.HexBytes `json:"padding"` + Payload rpc.HexBytes `json:"payload"` + WorkTime uint32 `json:"worktime"` + PoW float64 `json:"pow"` + FilterID int `json:"filter"` + PeerID rpc.HexBytes `json:"directP2P"` +} + +func (args *PostArgs) UnmarshalJSON(data []byte) (err error) { + var obj struct { + TTL uint32 `json:"ttl"` + From rpc.HexBytes `json:"from"` + To rpc.HexBytes `json:"to"` + KeyName string `json:"keyname"` + Topic whisperv5.TopicType `json:"topic"` + Payload rpc.HexBytes `json:"payload"` + Padding rpc.HexBytes `json:"padding"` + WorkTime uint32 `json:"worktime"` + PoW float64 `json:"pow"` + FilterID rpc.HexBytes `json:"filter"` + PeerID rpc.HexBytes `json:"directP2P"` + } + + if err := json.Unmarshal(data, &obj); err != nil { + return err + } + + args.TTL = obj.TTL + args.From = obj.From + args.To = obj.To + args.KeyName = obj.KeyName + args.Topic = obj.Topic + args.Payload = obj.Payload + args.Padding = obj.Padding + args.WorkTime = obj.WorkTime + args.PoW = obj.PoW + args.FilterID = -1 + args.PeerID = obj.PeerID + + if obj.FilterID != nil { + x := whisperv5.BytesToIntBigEndian(obj.FilterID) + args.FilterID = int(x) + } + + return nil +} + +type WhisperFilterArgs struct { + To []byte + From []byte + KeyName string + PoW float64 + Topics []whisperv5.TopicType + AcceptP2P bool +} + +// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a +// JSON message blob into a WhisperFilterArgs structure. +func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { + // Unmarshal the JSON message and sanity check + var obj struct { + To rpc.HexBytes `json:"to"` + From rpc.HexBytes `json:"from"` + KeyName string `json:"keyname"` + PoW float64 `json:"pow"` + Topics []interface{} `json:"topics"` + AcceptP2P bool `json:"acceptP2P"` + } + if err := json.Unmarshal(b, &obj); err != nil { + return err + } + + args.To = obj.To + args.From = obj.From + args.KeyName = obj.KeyName + args.PoW = obj.PoW + args.AcceptP2P = obj.AcceptP2P + + // Construct the topic array + if obj.Topics != nil { + topics := make([]string, len(obj.Topics)) + for i, field := range obj.Topics { + switch value := field.(type) { + case string: + topics[i] = value + case nil: + return fmt.Errorf("topic[%d] is empty", i) + default: + return fmt.Errorf("topic[%d] is not a string", i) + } + } + topicsDecoded := make([]whisperv5.TopicType, len(topics)) + for j, s := range topics { + x := common.FromHex(s) + if x == nil || len(x) != whisperv5.TopicLength { + return fmt.Errorf("topic[%d] is invalid", j) + } + topicsDecoded[j] = whisperv5.BytesToTopic(x) + } + args.Topics = topicsDecoded + } + + return nil +} + +// WhisperMessage is the RPC representation of a whisper message. +type WhisperMessage struct { + Payload string `json:"payload"` + Padding string `json:"padding"` + From string `json:"from"` + To string `json:"to"` + Sent uint32 `json:"sent"` + TTL uint32 `json:"ttl"` + PoW float64 `json:"pow"` + Hash string `json:"hash"` +} + +// NewWhisperMessage converts an internal message into an API version. +func NewWhisperMessage(message *whisperv5.ReceivedMessage) WhisperMessage { + return WhisperMessage{ + Payload: common.ToHex(message.Payload), + Padding: common.ToHex(message.Padding), + From: common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())), + To: common.ToHex(crypto.FromECDSAPub(message.Dst)), + Sent: message.Sent, + TTL: message.TTL, + PoW: message.PoW, + Hash: common.ToHex(message.EnvelopeHash.Bytes()), + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/api.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/api.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/api.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/api.go index bf94aff4b..c2b74c31f 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/api.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/api.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package whisper +package whisperv2 import ( "encoding/json" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/doc.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/doc.go similarity index 98% rename from vendor/github.com/ethereum/go-ethereum/whisper/doc.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/doc.go index cfb0b5117..7252f44b1 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/doc.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/doc.go @@ -29,4 +29,4 @@ Whisper is a pure identity-based messaging system. Whisper provides a low-level or prejudiced by the low-level hardware attributes and characteristics, particularly the notion of singular endpoints. */ -package whisper +package whisperv2 diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/envelope.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/envelope.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope.go index 97d489b96..7110ab457 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/envelope.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope.go @@ -17,7 +17,7 @@ // Contains the Whisper protocol Envelope element. For formal details please see // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#envelopes. -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/filter.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/filter.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter.go index 9f6d6b781..8ce4a54fb 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/filter.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter.go @@ -16,7 +16,7 @@ // Contains the message filter for fine grained subscriptions. -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/main.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/main.go similarity index 100% rename from vendor/github.com/ethereum/go-ethereum/whisper/main.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/main.go diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/message.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/message.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message.go index f05b5d8b5..7ef9d0912 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/message.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message.go @@ -17,7 +17,7 @@ // Contains the Whisper protocol Message element. For formal details please see // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#messages. -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/peer.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/peer.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer.go index ee10e66e7..404ebd513 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package whisper +package whisperv2 import ( "fmt" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/topic.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/topic.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic.go index d37eb25ee..3e2b47bd3 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/topic.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic.go @@ -17,7 +17,7 @@ // Contains the Whisper protocol Topic element. For formal details please see // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#topics. -package whisper +package whisperv2 import "github.com/ethereum/go-ethereum/crypto" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisper.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper.go similarity index 99% rename from vendor/github.com/ethereum/go-ethereum/whisper/whisper.go rename to vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper.go index 91cfc9b6e..8ef584b84 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisper.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package whisper +package whisperv2 import ( "crypto/ecdsa" diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go new file mode 100644 index 000000000..ef3b93d12 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go @@ -0,0 +1,87 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +Package whisper implements the Whisper PoC-1. + +(https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec) + +Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP). +As such it may be likened and compared to both, not dissimilar to the +matter/energy duality (apologies to physicists for the blatant abuse of a +fundamental and beautiful natural principle). + +Whisper is a pure identity-based messaging system. Whisper provides a low-level +(non-application-specific) but easily-accessible API without being based upon +or prejudiced by the low-level hardware attributes and characteristics, +particularly the notion of singular endpoints. +*/ +package whisperv5 + +import ( + "fmt" + "time" +) + +const ( + EnvelopeVersion = uint64(0) + ProtocolVersion = uint64(5) + ProtocolVersionStr = "5.0" + ProtocolName = "shh" + + statusCode = 0 + messagesCode = 1 + p2pCode = 2 + mailRequestCode = 3 + NumberOfMessageCodes = 4 + + paddingMask = byte(3) + signatureFlag = byte(4) + + TopicLength = 4 + signatureLength = 65 + aesKeyLength = 32 + saltLength = 12 + + MaxMessageLength = 0xFFFF // todo: remove this restriction after testing in morden and analizing stats. this should be regulated by MinimumPoW. + MinimumPoW = 10.0 // todo: review + + padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature + padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility + + expirationCycle = time.Second + transmissionCycle = 300 * time.Millisecond + + DefaultTTL = 50 // seconds + SynchAllowance = 10 // seconds +) + +type unknownVersionError uint64 + +func (e unknownVersionError) Error() string { + return fmt.Sprintf("invalid envelope version %d", uint64(e)) +} + +// MailServer represents a mail server, capable of +// archiving the old messages for subsequent delivery +// to the peers. Any implementation must ensure that both +// functions are thread-safe. Also, they must return ASAP. +// DeliverMail should use directMessagesCode for delivery, +// in order to bypass the expiry checks. +type MailServer interface { + Archive(env *Envelope) + DeliverMail(whisperPeer *Peer, data []byte) +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go new file mode 100644 index 000000000..57d454a08 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go @@ -0,0 +1,233 @@ +// 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 . + +// Contains the Whisper protocol Envelope element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#envelopes. + +package whisperv5 + +import ( + "crypto/ecdsa" + "encoding/binary" + "fmt" + "math" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/rlp" +) + +// Envelope represents a clear-text data packet to transmit through the Whisper +// network. Its contents may or may not be encrypted and signed. +type Envelope struct { + Version []byte + Expiry uint32 + TTL uint32 + Topic TopicType + Salt []byte + AESNonce []byte + Data []byte + EnvNonce uint64 + + pow float64 // Message-specific PoW as described in the Whisper specification. + hash common.Hash // Cached hash of the envelope to avoid rehashing every time. + // Don't access hash directly, use Hash() function instead. +} + +// NewEnvelope wraps a Whisper message with expiration and destination data +// included into an envelope for network forwarding. +func NewEnvelope(ttl uint32, topic TopicType, salt []byte, aesNonce []byte, msg *SentMessage) *Envelope { + env := Envelope{ + Version: make([]byte, 1), + Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), + TTL: ttl, + Topic: topic, + Salt: salt, + AESNonce: aesNonce, + Data: msg.Raw, + EnvNonce: 0, + } + + if EnvelopeVersion < 256 { + env.Version[0] = byte(EnvelopeVersion) + } else { + panic("please increase the size of Envelope.Version before releasing this version") + } + + return &env +} + +func (e *Envelope) IsSymmetric() bool { + return e.AESNonce != nil +} + +func (e *Envelope) isAsymmetric() bool { + return !e.IsSymmetric() +} + +func (e *Envelope) Ver() uint64 { + return bytesToIntLittleEndian(e.Version) +} + +// Seal closes the envelope by spending the requested amount of time as a proof +// of work on hashing the data. +func (e *Envelope) Seal(options *MessageParams) { + var target int + if options.PoW == 0 { + // adjust for the duration of Seal() execution only if execution time is predefined unconditionally + e.Expiry += options.WorkTime + } else { + target = e.powToFirstBit(options.PoW) + } + + buf := make([]byte, 64) + h := crypto.Keccak256(e.rlpWithoutNonce()) + copy(buf[:32], h) + + finish, bestBit := time.Now().Add(time.Duration(options.WorkTime)*time.Second).UnixNano(), 0 + for nonce := uint64(0); time.Now().UnixNano() < finish; { + for i := 0; i < 1024; i++ { + binary.BigEndian.PutUint64(buf[56:], nonce) + h = crypto.Keccak256(buf) + firstBit := common.FirstBitSet(common.BigD(h)) + if firstBit > bestBit { + e.EnvNonce, bestBit = nonce, firstBit + if target > 0 && bestBit >= target { + return + } + } + nonce++ + } + } +} + +func (e *Envelope) PoW() float64 { + if e.pow == 0 { + e.calculatePoW(0) + } + return e.pow +} + +func (e *Envelope) calculatePoW(diff uint32) { + buf := make([]byte, 64) + h := crypto.Keccak256(e.rlpWithoutNonce()) + copy(buf[:32], h) + binary.BigEndian.PutUint64(buf[56:], e.EnvNonce) + h = crypto.Keccak256(buf) + firstBit := common.FirstBitSet(common.BigD(h)) + x := math.Pow(2, float64(firstBit)) + x /= float64(len(e.Data)) + x /= float64(e.TTL + diff) + e.pow = x +} + +func (e *Envelope) powToFirstBit(pow float64) int { + x := pow + x *= float64(len(e.Data)) + x *= float64(e.TTL) + bits := math.Log2(x) + bits = math.Ceil(bits) + return int(bits) +} + +// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. +func (e *Envelope) rlpWithoutNonce() []byte { + res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Salt, e.AESNonce, e.Data}) + return res +} + +// Hash returns the SHA3 hash of the envelope, calculating it if not yet done. +func (e *Envelope) Hash() common.Hash { + if (e.hash == common.Hash{}) { + encoded, _ := rlp.EncodeToBytes(e) + e.hash = crypto.Keccak256Hash(encoded) + } + return e.hash +} + +// DecodeRLP decodes an Envelope from an RLP data stream. +func (e *Envelope) DecodeRLP(s *rlp.Stream) error { + raw, err := s.Raw() + if err != nil { + return err + } + // The decoding of Envelope uses the struct fields but also needs + // to compute the hash of the whole RLP-encoded envelope. This + // type has the same structure as Envelope but is not an + // rlp.Decoder (does not implement DecodeRLP function). + // Only public members will be encoded. + type rlpenv Envelope + if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil { + return err + } + e.hash = crypto.Keccak256Hash(raw) + return nil +} + +// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key. +func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) { + message := &ReceivedMessage{Raw: e.Data} + err := message.decryptAsymmetric(key) + switch err { + case nil: + return message, nil + case ecies.ErrInvalidPublicKey: // addressed to somebody else + return nil, err + default: + return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) + } +} + +// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key. +func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) { + msg = &ReceivedMessage{Raw: e.Data} + err = msg.decryptSymmetric(key, e.Salt, e.AESNonce) + if err != nil { + msg = nil + } + return msg, err +} + +// Open tries to decrypt an envelope, and populates the message fields in case of success. +func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { + if e.isAsymmetric() { + msg, _ = e.OpenAsymmetric(watcher.KeyAsym) + if msg != nil { + msg.Dst = &watcher.KeyAsym.PublicKey + } + } else if e.IsSymmetric() { + msg, _ = e.OpenSymmetric(watcher.KeySym) + if msg != nil { + msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) + } + } + + if msg != nil { + ok := msg.Validate() + if !ok { + return nil + } + msg.Topic = e.Topic + msg.PoW = e.PoW() + msg.TTL = e.TTL + msg.Sent = e.Expiry - e.TTL + msg.EnvelopeHash = e.Hash() + msg.EnvelopeVersion = e.Ver() + } + return msg +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go new file mode 100644 index 000000000..5cc7be587 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go @@ -0,0 +1,197 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package whisperv5 + +import ( + "crypto/ecdsa" + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +type Filter struct { + Src *ecdsa.PublicKey // Sender of the message + KeyAsym *ecdsa.PrivateKey // Private Key of recipient + KeySym []byte // Key associated with the Topic + Topics []TopicType // Topics to filter messages with + PoW float64 // Proof of work as described in the Whisper spec + AcceptP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages + SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization + + Messages map[common.Hash]*ReceivedMessage + mutex sync.RWMutex +} + +type Filters struct { + id int + watchers map[int]*Filter + whisper *Whisper + mutex sync.RWMutex +} + +func NewFilters(w *Whisper) *Filters { + return &Filters{ + watchers: make(map[int]*Filter), + whisper: w, + } +} + +func (fs *Filters) Install(watcher *Filter) int { + if watcher.Messages == nil { + watcher.Messages = make(map[common.Hash]*ReceivedMessage) + } + + fs.mutex.Lock() + defer fs.mutex.Unlock() + + fs.watchers[fs.id] = watcher + ret := fs.id + fs.id++ + return ret +} + +func (fs *Filters) Uninstall(id int) { + fs.mutex.Lock() + defer fs.mutex.Unlock() + delete(fs.watchers, id) +} + +func (fs *Filters) Get(i int) *Filter { + fs.mutex.RLock() + defer fs.mutex.RUnlock() + return fs.watchers[i] +} + +func (fs *Filters) NotifyWatchers(env *Envelope, messageCode uint64) { + fs.mutex.RLock() + var msg *ReceivedMessage + for _, watcher := range fs.watchers { + if messageCode == p2pCode && !watcher.AcceptP2P { + continue + } + + match := false + if msg != nil { + match = watcher.MatchMessage(msg) + } else { + match = watcher.MatchEnvelope(env) + if match { + msg = env.Open(watcher) + } + } + + if match && msg != nil { + watcher.Trigger(msg) + } + } + fs.mutex.RUnlock() // we need to unlock before calling addDecryptedMessage + + if msg != nil { + fs.whisper.addDecryptedMessage(msg) + } +} + +func (f *Filter) expectsAsymmetricEncryption() bool { + return f.KeyAsym != nil +} + +func (f *Filter) expectsSymmetricEncryption() bool { + return f.KeySym != nil +} + +func (f *Filter) Trigger(msg *ReceivedMessage) { + f.mutex.Lock() + defer f.mutex.Unlock() + + if _, exist := f.Messages[msg.EnvelopeHash]; !exist { + f.Messages[msg.EnvelopeHash] = msg + } +} + +func (f *Filter) Retrieve() (all []*ReceivedMessage) { + f.mutex.Lock() + defer f.mutex.Unlock() + + all = make([]*ReceivedMessage, 0, len(f.Messages)) + for _, msg := range f.Messages { + all = append(all, msg) + } + f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages + return all +} + +func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { + if f.PoW > 0 && msg.PoW < f.PoW { + return false + } + if f.Src != nil && !isPubKeyEqual(msg.Src, f.Src) { + return false + } + + if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { + // if Dst match, ignore the topic + return isPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) + } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { + // check if that both the key and the topic match + if f.SymKeyHash == msg.SymKeyHash { + for _, t := range f.Topics { + if t == msg.Topic { + return true + } + } + return false + } + } + return false +} + +func (f *Filter) MatchEnvelope(envelope *Envelope) bool { + if f.PoW > 0 && envelope.pow < f.PoW { + return false + } + + encryptionMethodMatch := false + if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() { + encryptionMethodMatch = true + if f.Topics == nil { + // wildcard + return true + } + } else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() { + encryptionMethodMatch = true + } + + if encryptionMethodMatch { + for _, t := range f.Topics { + if t == envelope.Topic { + return true + } + } + } + + return false +} + +func isPubKeyEqual(a, b *ecdsa.PublicKey) bool { + if !ValidatePublicKey(a) { + return false + } else if !ValidatePublicKey(b) { + return false + } + // the Curve is always the same, just compare the points + return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go new file mode 100644 index 000000000..680d1f8a2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go @@ -0,0 +1,378 @@ +// 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 . + +// Contains the Whisper protocol Message element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#messages. +// todo: fix the spec link, and move it to doc.go + +package whisperv5 + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + crand "crypto/rand" + "crypto/sha256" + "errors" + "fmt" + mrand "math/rand" + + "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" + "golang.org/x/crypto/pbkdf2" +) + +// Options specifies the exact way a message should be wrapped into an Envelope. +type MessageParams struct { + TTL uint32 + Src *ecdsa.PrivateKey + Dst *ecdsa.PublicKey + KeySym []byte + Topic TopicType + WorkTime uint32 + PoW float64 + Payload []byte + Padding []byte +} + +// SentMessage represents an end-user data packet to transmit through the +// Whisper protocol. These are wrapped into Envelopes that need not be +// understood by intermediate nodes, just forwarded. +type SentMessage struct { + Raw []byte +} + +// ReceivedMessage represents a data packet to be received through the +// Whisper protocol. +type ReceivedMessage struct { + Raw []byte + + Payload []byte + Padding []byte + Signature []byte + + PoW float64 // Proof of work as described in the Whisper spec + Sent uint32 // Time when the message was posted into the network + TTL uint32 // Maximum time to live allowed for the message + Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) + Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) + Topic TopicType + + SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic + EnvelopeHash common.Hash // Message envelope hash to act as a unique id + EnvelopeVersion uint64 +} + +func isMessageSigned(flags byte) bool { + return (flags & signatureFlag) != 0 +} + +func (msg *ReceivedMessage) isSymmetricEncryption() bool { + return msg.SymKeyHash != common.Hash{} +} + +func (msg *ReceivedMessage) isAsymmetricEncryption() bool { + return msg.Dst != nil +} + +func DeriveOneTimeKey(key []byte, salt []byte, version uint64) ([]byte, error) { + if version == 0 { + derivedKey := pbkdf2.Key(key, salt, 8, aesKeyLength, sha256.New) + return derivedKey, nil + } else { + return nil, unknownVersionError(version) + } +} + +// NewMessage creates and initializes a non-signed, non-encrypted Whisper message. +func NewSentMessage(params *MessageParams) *SentMessage { + msg := SentMessage{} + msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper) + msg.Raw[0] = 0 // set all the flags to zero + msg.appendPadding(params) + msg.Raw = append(msg.Raw, params.Payload...) + return &msg +} + +// appendPadding appends the pseudorandom padding bytes and sets the padding flag. +// The last byte contains the size of padding (thus, its size must not exceed 256). +func (msg *SentMessage) appendPadding(params *MessageParams) { + total := len(params.Payload) + 1 + if params.Src != nil { + total += signatureLength + } + padChunk := padSizeLimitUpper + if total <= padSizeLimitLower { + padChunk = padSizeLimitLower + } + odd := total % padChunk + if odd > 0 { + padSize := padChunk - odd + if padSize > 255 { + // this algorithm is only valid if padSizeLimitUpper <= 256. + // if padSizeLimitUpper will ever change, please fix the algorithm + // (for more information see ReceivedMessage.extractPadding() function). + panic("please fix the padding algorithm before releasing new version") + } + buf := make([]byte, padSize) + randomize(buf[1:]) // change to: err = mrand.Read(buf[1:]) + buf[0] = byte(padSize) + if params.Padding != nil { + copy(buf[1:], params.Padding) + } + msg.Raw = append(msg.Raw, buf...) + msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size + } +} + +// sign calculates and sets the cryptographic signature for the message, +// also setting the sign flag. +func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error { + if isMessageSigned(msg.Raw[0]) { + // this should not happen, but no reason to panic + glog.V(logger.Error).Infof("Trying to sign a message which was already signed") + return nil + } + + msg.Raw[0] |= signatureFlag + hash := crypto.Keccak256(msg.Raw) + signature, err := crypto.Sign(hash, key) + if err != nil { + msg.Raw[0] &= ^signatureFlag // clear the flag + return err + } + msg.Raw = append(msg.Raw, signature...) + return nil +} + +// encryptAsymmetric encrypts a message with a public key. +func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { + if !ValidatePublicKey(key) { + return fmt.Errorf("Invalid public key provided for asymmetric encryption") + } + encrypted, err := crypto.Encrypt(key, msg.Raw) + if err == nil { + msg.Raw = encrypted + } + return err +} + +// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. +// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). +func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte, err error) { + if !validateSymmetricKey(key) { + return nil, nil, errors.New("invalid key provided for symmetric encryption") + } + + salt = make([]byte, saltLength) + _, err = crand.Read(salt) + if err != nil { + return nil, nil, err + } else if !validateSymmetricKey(salt) { + return nil, nil, errors.New("crypto/rand failed to generate salt") + } + + derivedKey, err := DeriveOneTimeKey(key, salt, EnvelopeVersion) + if err != nil { + return nil, nil, err + } + if !validateSymmetricKey(derivedKey) { + return nil, nil, errors.New("failed to derive one-time key") + } + block, err := aes.NewCipher(derivedKey) + if err != nil { + return nil, nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, nil, err + } + + // never use more than 2^32 random nonces with a given key + nonce = make([]byte, aesgcm.NonceSize()) + _, err = crand.Read(nonce) + if err != nil { + return nil, nil, err + } + msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) + return salt, nonce, nil +} + +// Wrap bundles the message into an Envelope to transmit over the network. +// +// pow (Proof Of Work) controls how much time to spend on hashing the message, +// inherently controlling its priority through the network (smaller hash, bigger +// priority). +// +// The user can control the amount of identity, privacy and encryption through +// the options parameter as follows: +// - options.From == nil && options.To == nil: anonymous broadcast +// - options.From != nil && options.To == nil: signed broadcast (known sender) +// - options.From == nil && options.To != nil: encrypted anonymous message +// - options.From != nil && options.To != nil: encrypted signed message +func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { + if options.TTL == 0 { + options.TTL = DefaultTTL + } + if options.Src != nil { + err = msg.sign(options.Src) + if err != nil { + return nil, err + } + } + if len(msg.Raw) > MaxMessageLength { + glog.V(logger.Error).Infof("Message size must not exceed %d bytes", MaxMessageLength) + return nil, errors.New("Oversized message") + } + var salt, nonce []byte + if options.Dst != nil { + err = msg.encryptAsymmetric(options.Dst) + } else if options.KeySym != nil { + salt, nonce, err = msg.encryptSymmetric(options.KeySym) + } else { + err = errors.New("Unable to encrypt the message: neither Dst nor Key") + } + + if err != nil { + return nil, err + } + + envelope = NewEnvelope(options.TTL, options.Topic, salt, nonce, msg) + envelope.Seal(options) + return envelope, nil +} + +// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. +// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). +func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []byte) error { + derivedKey, err := DeriveOneTimeKey(key, salt, msg.EnvelopeVersion) + if err != nil { + return err + } + + block, err := aes.NewCipher(derivedKey) + if err != nil { + return err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return err + } + if len(nonce) != aesgcm.NonceSize() { + info := fmt.Sprintf("Wrong AES nonce size - want: %d, got: %d", len(nonce), aesgcm.NonceSize()) + glog.V(logger.Error).Infof(info) + return errors.New(info) + } + decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil) + if err != nil { + return err + } + msg.Raw = decrypted + return nil +} + +// decryptAsymmetric decrypts an encrypted payload with a private key. +func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { + decrypted, err := crypto.Decrypt(key, msg.Raw) + if err == nil { + msg.Raw = decrypted + } + return err +} + +// Validate checks the validity and extracts the fields in case of success +func (msg *ReceivedMessage) Validate() bool { + end := len(msg.Raw) + if end < 1 { + return false + } + + if isMessageSigned(msg.Raw[0]) { + end -= signatureLength + if end <= 1 { + return false + } + msg.Signature = msg.Raw[end:] + msg.Src = msg.SigToPubKey() + if msg.Src == nil { + return false + } + } + + padSize, ok := msg.extractPadding(end) + if !ok { + return false + } + + msg.Payload = msg.Raw[1+padSize : end] + return true +} + +// extractPadding extracts the padding from raw message. +// although we don't support sending messages with padding size +// exceeding 255 bytes, such messages are perfectly valid, and +// can be successfully decrypted. +func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { + paddingSize := 0 + sz := int(msg.Raw[0] & paddingMask) // number of bytes containing the entire size of padding, could be zero + if sz != 0 { + paddingSize = int(bytesToIntLittleEndian(msg.Raw[1 : 1+sz])) + if paddingSize < sz || paddingSize+1 > end { + return 0, false + } + msg.Padding = msg.Raw[1+sz : 1+paddingSize] + } + return paddingSize, true +} + +// Recover retrieves the public key of the message signer. +func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { + defer func() { recover() }() // in case of invalid signature + + pub, err := crypto.SigToPub(msg.hash(), msg.Signature) + if err != nil { + glog.V(logger.Error).Infof("Could not get public key from signature: %v", err) + return nil + } + return pub +} + +// hash calculates the SHA3 checksum of the message flags, payload and padding. +func (msg *ReceivedMessage) hash() []byte { + if isMessageSigned(msg.Raw[0]) { + sz := len(msg.Raw) - signatureLength + return crypto.Keccak256(msg.Raw[:sz]) + } + return crypto.Keccak256(msg.Raw) +} + +// rand.Rand provides a Read method in Go 1.7 and later, +// but we can't use it yet. +func randomize(b []byte) { + cnt := 0 + val := mrand.Int63() + for n := 0; n < len(b); n++ { + b[n] = byte(val) + val >>= 8 + cnt++ + if cnt >= 7 { + cnt = 0 + val = mrand.Int63() + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go new file mode 100644 index 000000000..fc1afb6d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go @@ -0,0 +1,174 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package whisperv5 + +import ( + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + set "gopkg.in/fatih/set.v0" +) + +// peer represents a whisper protocol peer connection. +type Peer struct { + host *Whisper + peer *p2p.Peer + ws p2p.MsgReadWriter + trusted bool + + known *set.Set // Messages already known by the peer to avoid wasting bandwidth + + quit chan struct{} +} + +// newPeer creates a new whisper peer object, but does not run the handshake itself. +func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { + return &Peer{ + host: host, + peer: remote, + ws: rw, + trusted: false, + known: set.New(), + quit: make(chan struct{}), + } +} + +// start initiates the peer updater, periodically broadcasting the whisper packets +// into the network. +func (p *Peer) start() { + go p.update() + glog.V(logger.Debug).Infof("%v: whisper started", p.peer) +} + +// stop terminates the peer updater, stopping message forwarding to it. +func (p *Peer) stop() { + close(p.quit) + glog.V(logger.Debug).Infof("%v: whisper stopped", p.peer) +} + +// handshake sends the protocol initiation status message to the remote peer and +// verifies the remote status too. +func (p *Peer) handshake() error { + // Send the handshake status message asynchronously + errc := make(chan error, 1) + go func() { + errc <- p2p.Send(p.ws, statusCode, ProtocolVersion) + }() + // Fetch the remote status packet and verify protocol match + packet, err := p.ws.ReadMsg() + if err != nil { + return err + } + if packet.Code != statusCode { + return fmt.Errorf("peer sent %x before status packet", packet.Code) + } + s := rlp.NewStream(packet.Payload, uint64(packet.Size)) + peerVersion, err := s.Uint() + if err != nil { + return fmt.Errorf("bad status message: %v", err) + } + if peerVersion != ProtocolVersion { + return fmt.Errorf("protocol version mismatch %d != %d", peerVersion, ProtocolVersion) + } + // Wait until out own status is consumed too + if err := <-errc; err != nil { + return fmt.Errorf("failed to send status packet: %v", err) + } + return nil +} + +// update executes periodic operations on the peer, including message transmission +// and expiration. +func (p *Peer) update() { + // Start the tickers for the updates + expire := time.NewTicker(expirationCycle) + transmit := time.NewTicker(transmissionCycle) + + // Loop and transmit until termination is requested + for { + select { + case <-expire.C: + p.expire() + + case <-transmit.C: + if err := p.broadcast(); err != nil { + glog.V(logger.Info).Infof("%v: broadcast failed: %v", p.peer, err) + return + } + + case <-p.quit: + return + } + } +} + +// mark marks an envelope known to the peer so that it won't be sent back. +func (peer *Peer) mark(envelope *Envelope) { + peer.known.Add(envelope.Hash()) +} + +// marked checks if an envelope is already known to the remote peer. +func (peer *Peer) marked(envelope *Envelope) bool { + return peer.known.Has(envelope.Hash()) +} + +// expire iterates over all the known envelopes in the host and removes all +// expired (unknown) ones from the known list. +func (peer *Peer) expire() { + // Assemble the list of available envelopes + available := set.NewNonTS() + for _, envelope := range peer.host.Envelopes() { + available.Add(envelope.Hash()) + } + // Cross reference availability with known status + unmark := make(map[common.Hash]struct{}) + peer.known.Each(func(v interface{}) bool { + if !available.Has(v.(common.Hash)) { + unmark[v.(common.Hash)] = struct{}{} + } + return true + }) + // Dump all known but unavailable + for hash, _ := range unmark { + peer.known.Remove(hash) + } +} + +// broadcast iterates over the collection of envelopes and transmits yet unknown +// ones over the network. +func (p *Peer) broadcast() error { + // Fetch the envelopes and collect the unknown ones + envelopes := p.host.Envelopes() + transmit := make([]*Envelope, 0, len(envelopes)) + for _, envelope := range envelopes { + if !p.marked(envelope) { + transmit = append(transmit, envelope) + p.mark(envelope) + } + } + // Transmit the unknown batch (potentially empty) + if err := p2p.Send(p.ws, messagesCode, transmit); err != nil { + return err + } + glog.V(logger.Detail).Infoln(p.peer, "broadcasted", len(transmit), "message(s)") + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic.go new file mode 100644 index 000000000..c29c344be --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic.go @@ -0,0 +1,70 @@ +// 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 . + +// Contains the Whisper protocol Topic element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#topics. + +package whisperv5 + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// Topic represents a cryptographically secure, probabilistic partial +// classifications of a message, determined as the first (left) 4 bytes of the +// SHA3 hash of some arbitrary data given by the original author of the message. +type TopicType [TopicLength]byte + +func BytesToTopic(b []byte) (t TopicType) { + sz := TopicLength + if x := len(b); x < TopicLength { + sz = x + } + for i := 0; i < sz; i++ { + t[i] = b[i] + } + return t +} + +// String converts a topic byte array to a string representation. +func (topic *TopicType) String() string { + return string(common.ToHex(topic[:])) +} + +// UnmarshalJSON parses a hex representation to a topic. +func (t *TopicType) 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 + if len(input) != TopicLength*2 { + return fmt.Errorf("unmarshalJSON failed: topic must be exactly %d bytes", TopicLength) + } + b := common.FromHex(string(input)) + if b == nil { + return fmt.Errorf("unmarshalJSON failed: wrong topic format") + } + *t = BytesToTopic(b) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go new file mode 100644 index 000000000..836810824 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go @@ -0,0 +1,585 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package whisperv5 + +import ( + "bytes" + "crypto/ecdsa" + crand "crypto/rand" + "crypto/sha256" + "fmt" + "sync" + "time" + + "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" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/pbkdf2" + set "gopkg.in/fatih/set.v0" +) + +// Whisper represents a dark communication interface through the Ethereum +// network, using its very own P2P communication layer. +type Whisper struct { + protocol p2p.Protocol + filters *Filters + + privateKeys map[string]*ecdsa.PrivateKey + symKeys map[string][]byte + keyMu sync.RWMutex + + envelopes map[common.Hash]*Envelope // Pool of messages currently tracked by this node + messages map[common.Hash]*ReceivedMessage // Pool of successfully decrypted messages, which are not expired yet + expirations map[uint32]*set.SetNonTS // Message expiration pool + poolMu sync.RWMutex // Mutex to sync the message and expiration pools + + peers map[*Peer]struct{} // Set of currently active peers + peerMu sync.RWMutex // Mutex to sync the active peer set + + mailServer MailServer + + quit chan struct{} + test bool +} + +// New creates a Whisper client ready to communicate through the Ethereum P2P network. +// Param s should be passed if you want to implement mail server, otherwise nil. +func NewWhisper(server MailServer) *Whisper { + whisper := &Whisper{ + privateKeys: make(map[string]*ecdsa.PrivateKey), + symKeys: make(map[string][]byte), + envelopes: make(map[common.Hash]*Envelope), + messages: make(map[common.Hash]*ReceivedMessage), + expirations: make(map[uint32]*set.SetNonTS), + peers: make(map[*Peer]struct{}), + mailServer: server, + quit: make(chan struct{}), + } + whisper.filters = NewFilters(whisper) + + // p2p whisper sub protocol handler + whisper.protocol = p2p.Protocol{ + Name: ProtocolName, + Version: uint(ProtocolVersion), + Length: NumberOfMessageCodes, + Run: whisper.HandlePeer, + } + + return whisper +} + +// Protocols returns the whisper sub-protocols ran by this particular client. +func (w *Whisper) Protocols() []p2p.Protocol { + return []p2p.Protocol{w.protocol} +} + +// Version returns the whisper sub-protocols version number. +func (w *Whisper) Version() uint { + return w.protocol.Version +} + +func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { + w.peerMu.Lock() + defer w.peerMu.Unlock() + for p, _ := range w.peers { + id := p.peer.ID() + if bytes.Equal(peerID, id[:]) { + return p, nil + } + } + return nil, fmt.Errorf("Could not find peer with ID: %x", peerID) +} + +// MarkPeerTrusted marks specific peer trusted, which will allow it +// to send historic (expired) messages. +func (w *Whisper) MarkPeerTrusted(peerID []byte) error { + p, err := w.getPeer(peerID) + if err != nil { + return err + } + p.trusted = true + return nil +} + +func (w *Whisper) RequestHistoricMessages(peerID []byte, data []byte) error { + p, err := w.getPeer(peerID) + if err != nil { + return err + } + p.trusted = true + return p2p.Send(p.ws, mailRequestCode, data) +} + +func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { + p, err := w.getPeer(peerID) + if err != nil { + return err + } + return p2p.Send(p.ws, p2pCode, envelope) +} + +// NewIdentity generates a new cryptographic identity for the client, and injects +// it into the known identities for message decryption. +func (w *Whisper) NewIdentity() *ecdsa.PrivateKey { + key, err := crypto.GenerateKey() + if err != nil || !validatePrivateKey(key) { + key, err = crypto.GenerateKey() // retry once + } + if err != nil { + panic(err) + } + if !validatePrivateKey(key) { + panic("Failed to generate valid key") + } + w.keyMu.Lock() + defer w.keyMu.Unlock() + w.privateKeys[common.ToHex(crypto.FromECDSAPub(&key.PublicKey))] = key + return key +} + +// DeleteIdentity deletes the specified key if it exists. +func (w *Whisper) DeleteIdentity(key string) { + w.keyMu.Lock() + defer w.keyMu.Unlock() + delete(w.privateKeys, key) +} + +// HasIdentity checks if the the whisper node is configured with the private key +// of the specified public pair. +func (w *Whisper) HasIdentity(pubKey string) bool { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.privateKeys[pubKey] != nil +} + +// GetIdentity retrieves the private key of the specified public identity. +func (w *Whisper) GetIdentity(pubKey string) *ecdsa.PrivateKey { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.privateKeys[pubKey] +} + +func (w *Whisper) GenerateSymKey(name string) error { + buf := make([]byte, aesKeyLength*2) + _, err := crand.Read(buf) // todo: check how safe is this function + if err != nil { + return err + } else if !validateSymmetricKey(buf) { + return fmt.Errorf("crypto/rand failed to generate random data") + } + + key := buf[:aesKeyLength] + salt := buf[aesKeyLength:] + derived, err := DeriveOneTimeKey(key, salt, EnvelopeVersion) + if err != nil { + return err + } else if !validateSymmetricKey(derived) { + return fmt.Errorf("failed to derive valid key") + } + + w.keyMu.Lock() + defer w.keyMu.Unlock() + + if w.symKeys[name] != nil { + return fmt.Errorf("Key with name [%s] already exists", name) + } + w.symKeys[name] = derived + return nil +} + +func (w *Whisper) AddSymKey(name string, key []byte) error { + if w.HasSymKey(name) { + return fmt.Errorf("Key with name [%s] already exists", name) + } + + derived, err := deriveKeyMaterial(key, EnvelopeVersion) + if err != nil { + return err + } + + w.keyMu.Lock() + defer w.keyMu.Unlock() + + // double check is necessary, because deriveKeyMaterial() is slow + if w.symKeys[name] != nil { + return fmt.Errorf("Key with name [%s] already exists", name) + } + w.symKeys[name] = derived + return nil +} + +func (w *Whisper) HasSymKey(name string) bool { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.symKeys[name] != nil +} + +func (w *Whisper) DeleteSymKey(name string) { + w.keyMu.Lock() + defer w.keyMu.Unlock() + delete(w.symKeys, name) +} + +func (w *Whisper) GetSymKey(name string) []byte { + w.keyMu.RLock() + defer w.keyMu.RUnlock() + return w.symKeys[name] +} + +// Watch installs a new message handler to run in case a matching packet arrives +// from the whisper network. +func (w *Whisper) Watch(f *Filter) int { + return w.filters.Install(f) +} + +func (w *Whisper) GetFilter(id int) *Filter { + return w.filters.Get(id) +} + +// Unwatch removes an installed message handler. +func (w *Whisper) Unwatch(id int) { + w.filters.Uninstall(id) +} + +// Send injects a message into the whisper send queue, to be distributed in the +// network in the coming cycles. +func (w *Whisper) Send(envelope *Envelope) error { + return w.add(envelope) +} + +// Start implements node.Service, starting the background data propagation thread +// of the Whisper protocol. +func (w *Whisper) Start(*p2p.Server) error { + glog.V(logger.Info).Infoln("Whisper started") + go w.update() + return nil +} + +// Stop implements node.Service, stopping the background data propagation thread +// of the Whisper protocol. +func (w *Whisper) Stop() error { + close(w.quit) + glog.V(logger.Info).Infoln("Whisper stopped") + return nil +} + +// handlePeer is called by the underlying P2P layer when the whisper sub-protocol +// connection is negotiated. +func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { + // Create the new peer and start tracking it + whisperPeer := newPeer(wh, peer, rw) + + wh.peerMu.Lock() + wh.peers[whisperPeer] = struct{}{} + wh.peerMu.Unlock() + + defer func() { + wh.peerMu.Lock() + delete(wh.peers, whisperPeer) + wh.peerMu.Unlock() + }() + + // Run the peer handshake and state updates + if err := whisperPeer.handshake(); err != nil { + return err + } + whisperPeer.start() + defer whisperPeer.stop() + + return wh.runMessageLoop(whisperPeer, rw) +} + +// runMessageLoop reads and processes inbound messages directly to merge into client-global state. +func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { + for { + // fetch the next packet + packet, err := rw.ReadMsg() + if err != nil { + return err + } + + switch packet.Code { + case statusCode: + // this should not happen, but no need to panic; just ignore this message. + glog.V(logger.Warn).Infof("%v: unxepected status message received", p.peer) + case messagesCode: + // decode the contained envelopes + var envelopes []*Envelope + if err := packet.Decode(&envelopes); err != nil { + glog.V(logger.Warn).Infof("%v: failed to decode envelope: [%v], peer will be disconnected", p.peer, err) + return fmt.Errorf("garbage received") + } + // inject all envelopes into the internal pool + for _, envelope := range envelopes { + if err := wh.add(envelope); err != nil { + glog.V(logger.Warn).Infof("%v: bad envelope received: [%v], peer will be disconnected", p.peer, err) + return fmt.Errorf("invalid envelope") + } + p.mark(envelope) + if wh.mailServer != nil { + wh.mailServer.Archive(envelope) + } + } + case p2pCode: + // peer-to-peer message, sent directly to peer bypassing PoW checks, etc. + // this message is not supposed to be forwarded to other peers, and + // therefore might not satisfy the PoW, expiry and other requirements. + // these messages are only accepted from the trusted peer. + if p.trusted { + var envelopes []*Envelope + if err := packet.Decode(&envelopes); err != nil { + glog.V(logger.Warn).Infof("%v: failed to decode direct message: [%v], peer will be disconnected", p.peer, err) + return fmt.Errorf("garbage received (directMessage)") + } + for _, envelope := range envelopes { + wh.postEvent(envelope, p2pCode) + } + } + case mailRequestCode: + // Must be processed if mail server is implemented. Otherwise ignore. + if wh.mailServer != nil { + s := rlp.NewStream(packet.Payload, uint64(packet.Size)) + data, err := s.Bytes() + if err == nil { + wh.mailServer.DeliverMail(p, data) + } else { + glog.V(logger.Error).Infof("%v: bad requestHistoricMessages received: [%v]", p.peer, err) + } + } + default: + // New message types might be implemented in the future versions of Whisper. + // For forward compatibility, just ignore. + } + + packet.Discard() + } +} + +// add inserts a new envelope into the message pool to be distributed within the +// whisper network. It also inserts the envelope into the expiration pool at the +// appropriate time-stamp. In case of error, connection should be dropped. +func (wh *Whisper) add(envelope *Envelope) error { + now := uint32(time.Now().Unix()) + sent := envelope.Expiry - envelope.TTL + + if sent > now { + if sent-SynchAllowance > now { + return fmt.Errorf("message created in the future") + } else { + // recalculate PoW, adjusted for the time difference, plus one second for latency + envelope.calculatePoW(sent - now + 1) + } + } + + if envelope.Expiry < now { + if envelope.Expiry+SynchAllowance*2 < now { + return fmt.Errorf("very old message") + } else { + return nil // drop envelope without error + } + } + + if len(envelope.Data) > MaxMessageLength { + return fmt.Errorf("huge messages are not allowed") + } + + if len(envelope.Version) > 4 { + return fmt.Errorf("oversized Version") + } + + if len(envelope.AESNonce) > 12 { + // the standard AES GSM nonce size is 12, + // but const gcmStandardNonceSize cannot be accessed directly + return fmt.Errorf("oversized AESNonce") + } + + if len(envelope.Salt) > saltLength { + return fmt.Errorf("oversized Salt") + } + + if envelope.PoW() < MinimumPoW && !wh.test { + glog.V(logger.Debug).Infof("envelope with low PoW dropped: %f", envelope.PoW()) + return nil // drop envelope without error + } + + hash := envelope.Hash() + + wh.poolMu.Lock() + _, alreadyCached := wh.envelopes[hash] + if !alreadyCached { + wh.envelopes[hash] = envelope + if wh.expirations[envelope.Expiry] == nil { + wh.expirations[envelope.Expiry] = set.NewNonTS() + } + if !wh.expirations[envelope.Expiry].Has(hash) { + wh.expirations[envelope.Expiry].Add(hash) + } + } + wh.poolMu.Unlock() + + if alreadyCached { + glog.V(logger.Detail).Infof("whisper envelope already cached: %x\n", envelope) + } else { + wh.postEvent(envelope, messagesCode) // notify the local node about the new message + glog.V(logger.Detail).Infof("cached whisper envelope %v\n", envelope) + } + return nil +} + +// postEvent delivers the message to the watchers. +func (w *Whisper) postEvent(envelope *Envelope, messageCode uint64) { + // if the version of incoming message is higher than + // currently supported version, we can not decrypt it, + // and therefore just ignore this message + if envelope.Ver() <= EnvelopeVersion { + // todo: review if you need an additional thread here + go w.filters.NotifyWatchers(envelope, messageCode) + } +} + +// update loops until the lifetime of the whisper node, updating its internal +// state by expiring stale messages from the pool. +func (w *Whisper) update() { + // Start a ticker to check for expirations + expire := time.NewTicker(expirationCycle) + + // Repeat updates until termination is requested + for { + select { + case <-expire.C: + w.expire() + + case <-w.quit: + return + } + } +} + +// expire iterates over all the expiration timestamps, removing all stale +// messages from the pools. +func (w *Whisper) expire() { + w.poolMu.Lock() + defer w.poolMu.Unlock() + + now := uint32(time.Now().Unix()) + for then, hashSet := range w.expirations { + // Short circuit if a future time + if then > now { + continue + } + // Dump all expired messages and remove timestamp + hashSet.Each(func(v interface{}) bool { + delete(w.envelopes, v.(common.Hash)) + delete(w.messages, v.(common.Hash)) + return true + }) + w.expirations[then].Clear() + } +} + +// envelopes retrieves all the messages currently pooled by the node. +func (w *Whisper) Envelopes() []*Envelope { + w.poolMu.RLock() + defer w.poolMu.RUnlock() + + all := make([]*Envelope, 0, len(w.envelopes)) + for _, envelope := range w.envelopes { + all = append(all, envelope) + } + return all +} + +// Messages retrieves all the decrypted messages matching a filter id. +func (w *Whisper) Messages(id int) []*ReceivedMessage { + result := make([]*ReceivedMessage, 0) + w.poolMu.RLock() + defer w.poolMu.RUnlock() + + if filter := w.filters.Get(id); filter != nil { + for _, msg := range w.messages { + if filter.MatchMessage(msg) { + result = append(result, msg) + } + } + } + return result +} + +func (w *Whisper) addDecryptedMessage(msg *ReceivedMessage) { + w.poolMu.Lock() + defer w.poolMu.Unlock() + + w.messages[msg.EnvelopeHash] = msg +} + +func ValidatePublicKey(k *ecdsa.PublicKey) bool { + return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 +} + +func validatePrivateKey(k *ecdsa.PrivateKey) bool { + if k == nil || k.D == nil || k.D.Sign() == 0 { + return false + } + return ValidatePublicKey(&k.PublicKey) +} + +// validateSymmetricKey returns false if the key contains all zeros +func validateSymmetricKey(k []byte) bool { + return len(k) > 0 && !containsOnlyZeros(k) +} + +func containsOnlyZeros(data []byte) bool { + for _, b := range data { + if b != 0 { + return false + } + } + return true +} + +func bytesToIntLittleEndian(b []byte) (res uint64) { + mul := uint64(1) + for i := 0; i < len(b); i++ { + res += uint64(b[i]) * mul + mul *= 256 + } + return res +} + +func BytesToIntBigEndian(b []byte) (res uint64) { + for i := 0; i < len(b); i++ { + res *= 256 + res += uint64(b[i]) + } + return res +} + +// DeriveSymmetricKey derives symmetric key material from the key or password. +// pbkdf2 is used for security, in case people use password instead of randomly generated keys. +func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) { + if version == 0 { + // kdf should run no less than 0.1 seconds on average compute, + // because it's a once in a session experience + derivedKey := pbkdf2.Key(key, nil, 65356, aesKeyLength, sha256.New) + return derivedKey, nil + } else { + return nil, unknownVersionError(version) + } +} diff --git a/vendor/github.com/rcrowley/go-metrics/README.md b/vendor/github.com/rcrowley/go-metrics/README.md index 66ba9cab5..2d1a6dcfa 100644 --- a/vendor/github.com/rcrowley/go-metrics/README.md +++ b/vendor/github.com/rcrowley/go-metrics/README.md @@ -21,6 +21,9 @@ g := metrics.NewGauge() metrics.Register("bar", g) g.Update(47) +r := NewRegistry() +g := metrics.NewRegisteredFunctionalGauge("cache-evictions", r, func() int64 { return cache.getEvictionsCount() }) + s := metrics.NewExpDecaySample(1028, 0.015) // or metrics.NewUniformSample(1028) h := metrics.NewHistogram(s) metrics.Register("baz", h) @@ -36,6 +39,15 @@ t.Time(func() {}) t.Update(47) ``` +Register() is not threadsafe. For threadsafe metric registration use +GetOrRegister: + +``` +t := metrics.GetOrRegisterTimer("account.create.latency", nil) +t.Time(func() {}) +t.Update(47) +``` + Periodically log every metric in human-readable form to standard error: ```go @@ -67,7 +79,7 @@ issues [#121](https://github.com/rcrowley/go-metrics/issues/121) and [#124](https://github.com/rcrowley/go-metrics/issues/124) for progress and details. ```go -import "github.com/rcrowley/go-metrics/influxdb" +import "github.com/vrischmann/go-metrics-influxdb" go influxdb.Influxdb(metrics.DefaultRegistry, 10e9, &influxdb.Config{ Host: "127.0.0.1:8086", @@ -137,3 +149,5 @@ Clients are available for the following destinations: * Librato - [https://github.com/mihasya/go-metrics-librato](https://github.com/mihasya/go-metrics-librato) * Graphite - [https://github.com/cyberdelia/go-metrics-graphite](https://github.com/cyberdelia/go-metrics-graphite) * InfluxDB - [https://github.com/vrischmann/go-metrics-influxdb](https://github.com/vrischmann/go-metrics-influxdb) +* Ganglia - [https://github.com/appscode/metlia](https://github.com/appscode/metlia) +* Prometheus - [https://github.com/deathowl/go-metrics-prometheus](https://github.com/deathowl/go-metrics-prometheus) diff --git a/vendor/github.com/rcrowley/go-metrics/cmd/metrics-bench/metrics-bench.go b/vendor/github.com/rcrowley/go-metrics/cmd/metrics-bench/metrics-bench.go new file mode 100644 index 000000000..dddaf4b12 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/cmd/metrics-bench/metrics-bench.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "github.com/rcrowley/go-metrics" + "time" +) + +func main() { + r := metrics.NewRegistry() + for i := 0; i < 10000; i++ { + r.Register(fmt.Sprintf("counter-%d", i), metrics.NewCounter()) + r.Register(fmt.Sprintf("gauge-%d", i), metrics.NewGauge()) + r.Register(fmt.Sprintf("gaugefloat64-%d", i), metrics.NewGaugeFloat64()) + r.Register(fmt.Sprintf("histogram-uniform-%d", i), metrics.NewHistogram(metrics.NewUniformSample(1028))) + r.Register(fmt.Sprintf("histogram-exp-%d", i), metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015))) + r.Register(fmt.Sprintf("meter-%d", i), metrics.NewMeter()) + } + time.Sleep(600e9) +} diff --git a/vendor/github.com/rcrowley/go-metrics/cmd/metrics-example/metrics-example.go b/vendor/github.com/rcrowley/go-metrics/cmd/metrics-example/metrics-example.go new file mode 100644 index 000000000..66f42c046 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/cmd/metrics-example/metrics-example.go @@ -0,0 +1,154 @@ +package main + +import ( + "errors" + "github.com/rcrowley/go-metrics" + // "github.com/rcrowley/go-metrics/stathat" + "log" + "math/rand" + "os" + // "syslog" + "time" +) + +const fanout = 10 + +func main() { + + r := metrics.NewRegistry() + + c := metrics.NewCounter() + r.Register("foo", c) + for i := 0; i < fanout; i++ { + go func() { + for { + c.Dec(19) + time.Sleep(300e6) + } + }() + go func() { + for { + c.Inc(47) + time.Sleep(400e6) + } + }() + } + + g := metrics.NewGauge() + r.Register("bar", g) + for i := 0; i < fanout; i++ { + go func() { + for { + g.Update(19) + time.Sleep(300e6) + } + }() + go func() { + for { + g.Update(47) + time.Sleep(400e6) + } + }() + } + + gf := metrics.NewGaugeFloat64() + r.Register("barfloat64", gf) + for i := 0; i < fanout; i++ { + go func() { + for { + g.Update(19.0) + time.Sleep(300e6) + } + }() + go func() { + for { + g.Update(47.0) + time.Sleep(400e6) + } + }() + } + + hc := metrics.NewHealthcheck(func(h metrics.Healthcheck) { + if 0 < rand.Intn(2) { + h.Healthy() + } else { + h.Unhealthy(errors.New("baz")) + } + }) + r.Register("baz", hc) + + s := metrics.NewExpDecaySample(1028, 0.015) + //s := metrics.NewUniformSample(1028) + h := metrics.NewHistogram(s) + r.Register("bang", h) + for i := 0; i < fanout; i++ { + go func() { + for { + h.Update(19) + time.Sleep(300e6) + } + }() + go func() { + for { + h.Update(47) + time.Sleep(400e6) + } + }() + } + + m := metrics.NewMeter() + r.Register("quux", m) + for i := 0; i < fanout; i++ { + go func() { + for { + m.Mark(19) + time.Sleep(300e6) + } + }() + go func() { + for { + m.Mark(47) + time.Sleep(400e6) + } + }() + } + + t := metrics.NewTimer() + r.Register("hooah", t) + for i := 0; i < fanout; i++ { + go func() { + for { + t.Time(func() { time.Sleep(300e6) }) + } + }() + go func() { + for { + t.Time(func() { time.Sleep(400e6) }) + } + }() + } + + metrics.RegisterDebugGCStats(r) + go metrics.CaptureDebugGCStats(r, 5e9) + + metrics.RegisterRuntimeMemStats(r) + go metrics.CaptureRuntimeMemStats(r, 5e9) + + metrics.Log(r, 60e9, log.New(os.Stderr, "metrics: ", log.Lmicroseconds)) + + /* + w, err := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics") + if nil != err { log.Fatalln(err) } + metrics.Syslog(r, 60e9, w) + */ + + /* + addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003") + metrics.Graphite(r, 10e9, "metrics", addr) + */ + + /* + stathat.Stathat(r, 10e9, "example@example.com") + */ + +} diff --git a/vendor/github.com/rcrowley/go-metrics/cmd/never-read/never-read.go b/vendor/github.com/rcrowley/go-metrics/cmd/never-read/never-read.go new file mode 100644 index 000000000..dc175b778 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/cmd/never-read/never-read.go @@ -0,0 +1,22 @@ +package main + +import ( + "log" + "net" +) + +func main() { + addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003") + l, err := net.ListenTCP("tcp", addr) + if nil != err { + log.Fatalln(err) + } + log.Println("listening", l.Addr()) + for { + c, err := l.AcceptTCP() + if nil != err { + log.Fatalln(err) + } + log.Println("accepted", c.RemoteAddr()) + } +} diff --git a/vendor/github.com/rcrowley/go-metrics/exp/exp.go b/vendor/github.com/rcrowley/go-metrics/exp/exp.go new file mode 100644 index 000000000..11dd3f898 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/exp/exp.go @@ -0,0 +1,156 @@ +// Hook go-metrics into expvar +// on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler +package exp + +import ( + "expvar" + "fmt" + "net/http" + "sync" + + "github.com/rcrowley/go-metrics" +) + +type exp struct { + expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely + registry metrics.Registry +} + +func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) { + // load our variables into expvar + exp.syncToExpvar() + + // now just run the official expvar handler code (which is not publicly callable, so pasted inline) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + fmt.Fprintf(w, "{\n") + first := true + expvar.Do(func(kv expvar.KeyValue) { + if !first { + fmt.Fprintf(w, ",\n") + } + first = false + fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) + }) + fmt.Fprintf(w, "\n}\n") +} + +// Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars" +func Exp(r metrics.Registry) { + h := ExpHandler(r) + // this would cause a panic: + // panic: http: multiple registrations for /debug/vars + // http.HandleFunc("/debug/vars", e.expHandler) + // haven't found an elegant way, so just use a different endpoint + http.Handle("/debug/metrics", h) +} + +// ExpHandler will return an expvar powered metrics handler. +func ExpHandler(r metrics.Registry) http.Handler { + e := exp{sync.Mutex{}, r} + return http.HandlerFunc(e.expHandler) +} + +func (exp *exp) getInt(name string) *expvar.Int { + var v *expvar.Int + exp.expvarLock.Lock() + p := expvar.Get(name) + if p != nil { + v = p.(*expvar.Int) + } else { + v = new(expvar.Int) + expvar.Publish(name, v) + } + exp.expvarLock.Unlock() + return v +} + +func (exp *exp) getFloat(name string) *expvar.Float { + var v *expvar.Float + exp.expvarLock.Lock() + p := expvar.Get(name) + if p != nil { + v = p.(*expvar.Float) + } else { + v = new(expvar.Float) + expvar.Publish(name, v) + } + exp.expvarLock.Unlock() + return v +} + +func (exp *exp) publishCounter(name string, metric metrics.Counter) { + v := exp.getInt(name) + v.Set(metric.Count()) +} + +func (exp *exp) publishGauge(name string, metric metrics.Gauge) { + v := exp.getInt(name) + v.Set(metric.Value()) +} +func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) { + exp.getFloat(name).Set(metric.Value()) +} + +func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + exp.getInt(name + ".count").Set(h.Count()) + exp.getFloat(name + ".min").Set(float64(h.Min())) + exp.getFloat(name + ".max").Set(float64(h.Max())) + exp.getFloat(name + ".mean").Set(float64(h.Mean())) + exp.getFloat(name + ".std-dev").Set(float64(h.StdDev())) + exp.getFloat(name + ".50-percentile").Set(float64(ps[0])) + exp.getFloat(name + ".75-percentile").Set(float64(ps[1])) + exp.getFloat(name + ".95-percentile").Set(float64(ps[2])) + exp.getFloat(name + ".99-percentile").Set(float64(ps[3])) + exp.getFloat(name + ".999-percentile").Set(float64(ps[4])) +} + +func (exp *exp) publishMeter(name string, metric metrics.Meter) { + m := metric.Snapshot() + exp.getInt(name + ".count").Set(m.Count()) + exp.getFloat(name + ".one-minute").Set(float64(m.Rate1())) + exp.getFloat(name + ".five-minute").Set(float64(m.Rate5())) + exp.getFloat(name + ".fifteen-minute").Set(float64((m.Rate15()))) + exp.getFloat(name + ".mean").Set(float64(m.RateMean())) +} + +func (exp *exp) publishTimer(name string, metric metrics.Timer) { + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + exp.getInt(name + ".count").Set(t.Count()) + exp.getFloat(name + ".min").Set(float64(t.Min())) + exp.getFloat(name + ".max").Set(float64(t.Max())) + exp.getFloat(name + ".mean").Set(float64(t.Mean())) + exp.getFloat(name + ".std-dev").Set(float64(t.StdDev())) + exp.getFloat(name + ".50-percentile").Set(float64(ps[0])) + exp.getFloat(name + ".75-percentile").Set(float64(ps[1])) + exp.getFloat(name + ".95-percentile").Set(float64(ps[2])) + exp.getFloat(name + ".99-percentile").Set(float64(ps[3])) + exp.getFloat(name + ".999-percentile").Set(float64(ps[4])) + exp.getFloat(name + ".one-minute").Set(float64(t.Rate1())) + exp.getFloat(name + ".five-minute").Set(float64(t.Rate5())) + exp.getFloat(name + ".fifteen-minute").Set(float64((t.Rate15()))) + exp.getFloat(name + ".mean-rate").Set(float64(t.RateMean())) +} + +func (exp *exp) syncToExpvar() { + exp.registry.Each(func(name string, i interface{}) { + switch i.(type) { + case metrics.Counter: + exp.publishCounter(name, i.(metrics.Counter)) + case metrics.Gauge: + exp.publishGauge(name, i.(metrics.Gauge)) + case metrics.GaugeFloat64: + exp.publishGaugeFloat64(name, i.(metrics.GaugeFloat64)) + case metrics.Histogram: + exp.publishHistogram(name, i.(metrics.Histogram)) + case metrics.Meter: + exp.publishMeter(name, i.(metrics.Meter)) + case metrics.Timer: + exp.publishTimer(name, i.(metrics.Timer)) + default: + panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) + } + }) +} diff --git a/vendor/github.com/rcrowley/go-metrics/gauge.go b/vendor/github.com/rcrowley/go-metrics/gauge.go index 807638a31..d618c4553 100644 --- a/vendor/github.com/rcrowley/go-metrics/gauge.go +++ b/vendor/github.com/rcrowley/go-metrics/gauge.go @@ -36,6 +36,25 @@ func NewRegisteredGauge(name string, r Registry) Gauge { return c } +// NewFunctionalGauge constructs a new FunctionalGauge. +func NewFunctionalGauge(f func() int64) Gauge { + if UseNilMetrics { + return NilGauge{} + } + return &FunctionalGauge{value: f} +} + + +// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. +func NewRegisteredFunctionalGauge(name string, r Registry, f func() int64) Gauge { + c := NewFunctionalGauge(f) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + // GaugeSnapshot is a read-only copy of another Gauge. type GaugeSnapshot int64 @@ -82,3 +101,20 @@ func (g *StandardGauge) Update(v int64) { func (g *StandardGauge) Value() int64 { return atomic.LoadInt64(&g.value) } +// FunctionalGauge returns value from given function +type FunctionalGauge struct { + value func() int64 +} + +// Value returns the gauge's current value. +func (g FunctionalGauge) Value() int64 { + return g.value() +} + +// Snapshot returns the snapshot. +func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) } + +// Update panics. +func (FunctionalGauge) Update(int64) { + panic("Update called on a FunctionalGauge") +} \ No newline at end of file diff --git a/vendor/github.com/rcrowley/go-metrics/gauge_float64.go b/vendor/github.com/rcrowley/go-metrics/gauge_float64.go index 47c3566c2..6f93920b2 100644 --- a/vendor/github.com/rcrowley/go-metrics/gauge_float64.go +++ b/vendor/github.com/rcrowley/go-metrics/gauge_float64.go @@ -38,6 +38,24 @@ func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { return c } +// NewFunctionalGauge constructs a new FunctionalGauge. +func NewFunctionalGaugeFloat64(f func() float64) GaugeFloat64 { + if UseNilMetrics { + return NilGaugeFloat64{} + } + return &FunctionalGaugeFloat64{value: f} +} + +// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. +func NewRegisteredFunctionalGaugeFloat64(name string, r Registry, f func() float64) GaugeFloat64 { + c := NewFunctionalGaugeFloat64(f) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + // GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. type GaugeFloat64Snapshot float64 @@ -89,3 +107,21 @@ func (g *StandardGaugeFloat64) Value() float64 { defer g.mutex.Unlock() return g.value } + +// FunctionalGaugeFloat64 returns value from given function +type FunctionalGaugeFloat64 struct { + value func() float64 +} + +// Value returns the gauge's current value. +func (g FunctionalGaugeFloat64) Value() float64 { + return g.value() +} + +// Snapshot returns the snapshot. +func (g FunctionalGaugeFloat64) Snapshot() GaugeFloat64 { return GaugeFloat64Snapshot(g.Value()) } + +// Update panics. +func (FunctionalGaugeFloat64) Update(float64) { + panic("Update called on a FunctionalGaugeFloat64") +} diff --git a/vendor/github.com/rcrowley/go-metrics/json.go b/vendor/github.com/rcrowley/go-metrics/json.go index 2676aeea5..2fdcbcfbf 100644 --- a/vendor/github.com/rcrowley/go-metrics/json.go +++ b/vendor/github.com/rcrowley/go-metrics/json.go @@ -81,3 +81,7 @@ func WriteJSON(r Registry, d time.Duration, w io.Writer) { func WriteJSONOnce(r Registry, w io.Writer) { json.NewEncoder(w).Encode(r) } + +func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) { + return json.Marshal(p.underlying) +} diff --git a/vendor/github.com/rcrowley/go-metrics/librato/client.go b/vendor/github.com/rcrowley/go-metrics/librato/client.go new file mode 100644 index 000000000..8c0c850e3 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/librato/client.go @@ -0,0 +1,102 @@ +package librato + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" +) + +const Operations = "operations" +const OperationsShort = "ops" + +type LibratoClient struct { + Email, Token string +} + +// property strings +const ( + // display attributes + Color = "color" + DisplayMax = "display_max" + DisplayMin = "display_min" + DisplayUnitsLong = "display_units_long" + DisplayUnitsShort = "display_units_short" + DisplayStacked = "display_stacked" + DisplayTransform = "display_transform" + // special gauge display attributes + SummarizeFunction = "summarize_function" + Aggregate = "aggregate" + + // metric keys + Name = "name" + Period = "period" + Description = "description" + DisplayName = "display_name" + Attributes = "attributes" + + // measurement keys + MeasureTime = "measure_time" + Source = "source" + Value = "value" + + // special gauge keys + Count = "count" + Sum = "sum" + Max = "max" + Min = "min" + SumSquares = "sum_squares" + + // batch keys + Counters = "counters" + Gauges = "gauges" + + MetricsPostUrl = "https://metrics-api.librato.com/v1/metrics" +) + +type Measurement map[string]interface{} +type Metric map[string]interface{} + +type Batch struct { + Gauges []Measurement `json:"gauges,omitempty"` + Counters []Measurement `json:"counters,omitempty"` + MeasureTime int64 `json:"measure_time"` + Source string `json:"source"` +} + +func (self *LibratoClient) PostMetrics(batch Batch) (err error) { + var ( + js []byte + req *http.Request + resp *http.Response + ) + + if len(batch.Counters) == 0 && len(batch.Gauges) == 0 { + return nil + } + + if js, err = json.Marshal(batch); err != nil { + return + } + + if req, err = http.NewRequest("POST", MetricsPostUrl, bytes.NewBuffer(js)); err != nil { + return + } + + req.Header.Set("Content-Type", "application/json") + req.SetBasicAuth(self.Email, self.Token) + + if resp, err = http.DefaultClient.Do(req); err != nil { + return + } + + if resp.StatusCode != http.StatusOK { + var body []byte + if body, err = ioutil.ReadAll(resp.Body); err != nil { + body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err)) + } + err = fmt.Errorf("Unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body)) + } + return +} diff --git a/vendor/github.com/rcrowley/go-metrics/librato/librato.go b/vendor/github.com/rcrowley/go-metrics/librato/librato.go new file mode 100644 index 000000000..d7c057468 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/librato/librato.go @@ -0,0 +1,235 @@ +package librato + +import ( + "fmt" + "log" + "math" + "regexp" + "time" + + "github.com/rcrowley/go-metrics" +) + +// a regexp for extracting the unit from time.Duration.String +var unitRegexp = regexp.MustCompile("[^\\d]+$") + +// a helper that turns a time.Duration into librato display attributes for timer metrics +func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) { + attrs = make(map[string]interface{}) + attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d)) + attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String()))) + return +} + +type Reporter struct { + Email, Token string + Namespace string + Source string + Interval time.Duration + Registry metrics.Registry + Percentiles []float64 // percentiles to report on histogram metrics + TimerAttributes map[string]interface{} // units in which timers will be displayed + intervalSec int64 +} + +func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter { + return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)} +} + +func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) { + NewReporter(r, d, e, t, s, p, u).Run() +} + +func (self *Reporter) Run() { + log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015") + ticker := time.Tick(self.Interval) + metricsApi := &LibratoClient{self.Email, self.Token} + for now := range ticker { + var metrics Batch + var err error + if metrics, err = self.BuildRequest(now, self.Registry); err != nil { + log.Printf("ERROR constructing librato request body %s", err) + continue + } + if err := metricsApi.PostMetrics(metrics); err != nil { + log.Printf("ERROR sending metrics to librato %s", err) + continue + } + } +} + +// calculate sum of squares from data provided by metrics.Histogram +// see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods +func sumSquares(s metrics.Sample) float64 { + count := float64(s.Count()) + sumSquared := math.Pow(count*s.Mean(), 2) + sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count + if math.IsNaN(sumSquares) { + return 0.0 + } + return sumSquares +} +func sumSquaresTimer(t metrics.Timer) float64 { + count := float64(t.Count()) + sumSquared := math.Pow(count*t.Mean(), 2) + sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count + if math.IsNaN(sumSquares) { + return 0.0 + } + return sumSquares +} + +func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { + snapshot = Batch{ + // coerce timestamps to a stepping fn so that they line up in Librato graphs + MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec, + Source: self.Source, + } + snapshot.Gauges = make([]Measurement, 0) + snapshot.Counters = make([]Measurement, 0) + histogramGaugeCount := 1 + len(self.Percentiles) + r.Each(func(name string, metric interface{}) { + if self.Namespace != "" { + name = fmt.Sprintf("%s.%s", self.Namespace, name) + } + measurement := Measurement{} + measurement[Period] = self.Interval.Seconds() + switch m := metric.(type) { + case metrics.Counter: + if m.Count() > 0 { + measurement[Name] = fmt.Sprintf("%s.%s", name, "count") + measurement[Value] = float64(m.Count()) + measurement[Attributes] = map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + } + snapshot.Counters = append(snapshot.Counters, measurement) + } + case metrics.Gauge: + measurement[Name] = name + measurement[Value] = float64(m.Value()) + snapshot.Gauges = append(snapshot.Gauges, measurement) + case metrics.GaugeFloat64: + measurement[Name] = name + measurement[Value] = float64(m.Value()) + snapshot.Gauges = append(snapshot.Gauges, measurement) + case metrics.Histogram: + if m.Count() > 0 { + gauges := make([]Measurement, histogramGaugeCount, histogramGaugeCount) + s := m.Sample() + measurement[Name] = fmt.Sprintf("%s.%s", name, "hist") + measurement[Count] = uint64(s.Count()) + measurement[Max] = float64(s.Max()) + measurement[Min] = float64(s.Min()) + measurement[Sum] = float64(s.Sum()) + measurement[SumSquares] = sumSquares(s) + gauges[0] = measurement + for i, p := range self.Percentiles { + gauges[i+1] = Measurement{ + Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), + Value: s.Percentile(p), + Period: measurement[Period], + } + } + snapshot.Gauges = append(snapshot.Gauges, gauges...) + } + case metrics.Meter: + measurement[Name] = name + measurement[Value] = float64(m.Count()) + snapshot.Counters = append(snapshot.Counters, measurement) + snapshot.Gauges = append(snapshot.Gauges, + Measurement{ + Name: fmt.Sprintf("%s.%s", name, "1min"), + Value: m.Rate1(), + Period: int64(self.Interval.Seconds()), + Attributes: map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + }, + }, + Measurement{ + Name: fmt.Sprintf("%s.%s", name, "5min"), + Value: m.Rate5(), + Period: int64(self.Interval.Seconds()), + Attributes: map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + }, + }, + Measurement{ + Name: fmt.Sprintf("%s.%s", name, "15min"), + Value: m.Rate15(), + Period: int64(self.Interval.Seconds()), + Attributes: map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + }, + }, + ) + case metrics.Timer: + measurement[Name] = name + measurement[Value] = float64(m.Count()) + snapshot.Counters = append(snapshot.Counters, measurement) + if m.Count() > 0 { + libratoName := fmt.Sprintf("%s.%s", name, "timer.mean") + gauges := make([]Measurement, histogramGaugeCount, histogramGaugeCount) + gauges[0] = Measurement{ + Name: libratoName, + Count: uint64(m.Count()), + Sum: m.Mean() * float64(m.Count()), + Max: float64(m.Max()), + Min: float64(m.Min()), + SumSquares: sumSquaresTimer(m), + Period: int64(self.Interval.Seconds()), + Attributes: self.TimerAttributes, + } + for i, p := range self.Percentiles { + gauges[i+1] = Measurement{ + Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), + Value: m.Percentile(p), + Period: int64(self.Interval.Seconds()), + Attributes: self.TimerAttributes, + } + } + snapshot.Gauges = append(snapshot.Gauges, gauges...) + snapshot.Gauges = append(snapshot.Gauges, + Measurement{ + Name: fmt.Sprintf("%s.%s", name, "rate.1min"), + Value: m.Rate1(), + Period: int64(self.Interval.Seconds()), + Attributes: map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + }, + }, + Measurement{ + Name: fmt.Sprintf("%s.%s", name, "rate.5min"), + Value: m.Rate5(), + Period: int64(self.Interval.Seconds()), + Attributes: map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + }, + }, + Measurement{ + Name: fmt.Sprintf("%s.%s", name, "rate.15min"), + Value: m.Rate15(), + Period: int64(self.Interval.Seconds()), + Attributes: map[string]interface{}{ + DisplayUnitsLong: Operations, + DisplayUnitsShort: OperationsShort, + DisplayMin: "0", + }, + }, + ) + } + } + }) + return +} diff --git a/vendor/github.com/rcrowley/go-metrics/log.go b/vendor/github.com/rcrowley/go-metrics/log.go index f074eb03d..f8074c045 100644 --- a/vendor/github.com/rcrowley/go-metrics/log.go +++ b/vendor/github.com/rcrowley/go-metrics/log.go @@ -1,17 +1,20 @@ package metrics import ( - "log" "time" ) -func Log(r Registry, freq time.Duration, l *log.Logger) { +type Logger interface { + Printf(format string, v ...interface{}) +} + +func Log(r Registry, freq time.Duration, l Logger) { LogScaled(r, freq, time.Nanosecond, l) } // Output each metric in the given registry periodically using the given // logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos. -func LogScaled(r Registry, freq time.Duration, scale time.Duration, l *log.Logger) { +func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { du := float64(scale) duSuffix := scale.String()[1:] diff --git a/vendor/github.com/rcrowley/go-metrics/registry.go b/vendor/github.com/rcrowley/go-metrics/registry.go index e1f68a5dc..9086dcbdd 100644 --- a/vendor/github.com/rcrowley/go-metrics/registry.go +++ b/vendor/github.com/rcrowley/go-metrics/registry.go @@ -3,6 +3,7 @@ package metrics import ( "fmt" "reflect" + "strings" "sync" ) @@ -166,12 +167,34 @@ func NewPrefixedChildRegistry(parent Registry, prefix string) Registry { // Call the given function for each registered metric. func (r *PrefixedRegistry) Each(fn func(string, interface{})) { - r.underlying.Each(fn) + wrappedFn := func (prefix string) func(string, interface{}) { + return func(name string, iface interface{}) { + if strings.HasPrefix(name,prefix) { + fn(name, iface) + } else { + return + } + } + } + + baseRegistry, prefix := findPrefix(r, "") + baseRegistry.Each(wrappedFn(prefix)) +} + +func findPrefix(registry Registry, prefix string) (Registry, string) { + switch r := registry.(type) { + case *PrefixedRegistry: + return findPrefix(r.underlying, r.prefix + prefix) + case *StandardRegistry: + return r, prefix + } + return nil, "" } // Get the metric by the given name or nil if none is registered. func (r *PrefixedRegistry) Get(name string) interface{} { - return r.underlying.Get(name) + realName := r.prefix + name + return r.underlying.Get(realName) } // Gets an existing metric or registers the given one. diff --git a/vendor/github.com/rcrowley/go-metrics/stathat/stathat.go b/vendor/github.com/rcrowley/go-metrics/stathat/stathat.go new file mode 100644 index 000000000..0afcb4848 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/stathat/stathat.go @@ -0,0 +1,69 @@ +// Metrics output to StatHat. +package stathat + +import ( + "github.com/rcrowley/go-metrics" + "github.com/stathat/go" + "log" + "time" +) + +func Stathat(r metrics.Registry, d time.Duration, userkey string) { + for { + if err := sh(r, userkey); nil != err { + log.Println(err) + } + time.Sleep(d) + } +} + +func sh(r metrics.Registry, userkey string) error { + r.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case metrics.Counter: + stathat.PostEZCount(name, userkey, int(metric.Count())) + case metrics.Gauge: + stathat.PostEZValue(name, userkey, float64(metric.Value())) + case metrics.GaugeFloat64: + stathat.PostEZValue(name, userkey, float64(metric.Value())) + case metrics.Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + stathat.PostEZCount(name+".count", userkey, int(h.Count())) + stathat.PostEZValue(name+".min", userkey, float64(h.Min())) + stathat.PostEZValue(name+".max", userkey, float64(h.Max())) + stathat.PostEZValue(name+".mean", userkey, float64(h.Mean())) + stathat.PostEZValue(name+".std-dev", userkey, float64(h.StdDev())) + stathat.PostEZValue(name+".50-percentile", userkey, float64(ps[0])) + stathat.PostEZValue(name+".75-percentile", userkey, float64(ps[1])) + stathat.PostEZValue(name+".95-percentile", userkey, float64(ps[2])) + stathat.PostEZValue(name+".99-percentile", userkey, float64(ps[3])) + stathat.PostEZValue(name+".999-percentile", userkey, float64(ps[4])) + case metrics.Meter: + m := metric.Snapshot() + stathat.PostEZCount(name+".count", userkey, int(m.Count())) + stathat.PostEZValue(name+".one-minute", userkey, float64(m.Rate1())) + stathat.PostEZValue(name+".five-minute", userkey, float64(m.Rate5())) + stathat.PostEZValue(name+".fifteen-minute", userkey, float64(m.Rate15())) + stathat.PostEZValue(name+".mean", userkey, float64(m.RateMean())) + case metrics.Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + stathat.PostEZCount(name+".count", userkey, int(t.Count())) + stathat.PostEZValue(name+".min", userkey, float64(t.Min())) + stathat.PostEZValue(name+".max", userkey, float64(t.Max())) + stathat.PostEZValue(name+".mean", userkey, float64(t.Mean())) + stathat.PostEZValue(name+".std-dev", userkey, float64(t.StdDev())) + stathat.PostEZValue(name+".50-percentile", userkey, float64(ps[0])) + stathat.PostEZValue(name+".75-percentile", userkey, float64(ps[1])) + stathat.PostEZValue(name+".95-percentile", userkey, float64(ps[2])) + stathat.PostEZValue(name+".99-percentile", userkey, float64(ps[3])) + stathat.PostEZValue(name+".999-percentile", userkey, float64(ps[4])) + stathat.PostEZValue(name+".one-minute", userkey, float64(t.Rate1())) + stathat.PostEZValue(name+".five-minute", userkey, float64(t.Rate5())) + stathat.PostEZValue(name+".fifteen-minute", userkey, float64(t.Rate15())) + stathat.PostEZValue(name+".mean-rate", userkey, float64(t.RateMean())) + } + }) + return nil +}