Merge branch 'release/1.0.0'
This commit is contained in:
commit
90983ce0ad
35
Makefile
35
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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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}
|
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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}
|
|
@ -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))) {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// +build darwin,cgo
|
||||
|
||||
package geth
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
extern bool StatusServiceSignalEvent( const char *jsonEvent );
|
||||
*/
|
||||
import "C"
|
107
geth/node.go
107
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -5,10 +5,43 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <objc/objc.h>
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/message.h>
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
251
geth/txqueue.go
251
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
},
|
||||
})
|
||||
|
|
134
jail/jail.go
134
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}}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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}
|
||||
};
|
|
@ -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}
|
||||
};
|
|
@ -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}
|
||||
};
|
|
@ -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}
|
||||
};
|
|
@ -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
|
|
@ -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())
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.6
|
||||
- tip
|
|
@ -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.
|
|
@ -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.
|
|
@ -0,0 +1,4 @@
|
|||
/*
|
||||
Package structhash creates hash strings from arbitrary go data structures.
|
||||
*/
|
||||
package structhash
|
|
@ -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()
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
|
@ -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.
|
||||
|
|
@ -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.
|
|
@ -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)
|
||||
}
|
||||
```
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
```
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
```
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
```
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package backends
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// This nil assignment ensures compile time that nilBackend implements bind.ContractBackend.
|
||||
var _ bind.ContractBackend = (*nilBackend)(nil)
|
||||
|
||||
// nilBackend implements bind.ContractBackend, but panics on any method call.
|
||||
// Its sole purpose is to support the binding tests to construct the generated
|
||||
// wrappers without calling any methods on them.
|
||||
type nilBackend struct{}
|
||||
|
||||
func (*nilBackend) ContractCall(context.Context, common.Address, []byte, bool) ([]byte, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (*nilBackend) EstimateGasLimit(context.Context, common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (*nilBackend) HasCode(context.Context, common.Address, bool) (bool, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (*nilBackend) SuggestGasPrice(context.Context) (*big.Int, error) { panic("not implemented") }
|
||||
func (*nilBackend) PendingAccountNonce(context.Context, common.Address) (uint64, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (*nilBackend) SendTransaction(context.Context, *types.Transaction) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// NewNilBackend creates a new binding backend that can be used for instantiation
|
||||
// but will panic on any invocation. Its sole purpose is to help testing.
|
||||
func NewNilBackend() bind.ContractBackend {
|
||||
return new(nilBackend)
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package backends
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"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))
|
||||
}
|
270
vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go
generated
vendored
270
vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go
generated
vendored
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}}
|
||||
`
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:<IP>)")
|
||||
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 {}
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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 <file name>")
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
60006102ff5360003560001a60008114156103395760013560405260216040516020025990590160009052606052604051602002816060513760405160200281019050506002604051121561005957604051602002606051f35b604051602002599059016000905260a052600060c052604051602002599059016000905260e0526000610100526001610120525b604051610120511215610109576060515161012051602002606051015112156100d8576101205160200260605101516101005160200260e051015260016101005101610100526100f9565b61012051602002606051015160c05160200260a0510152600160c0510160c0525b600161012051016101205261008d565b60216020599059016000905260c051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260a0518260005b8381101561016657808301518186015260208101905061014b565b50505050825160200281019050604059905901600090526102405281610240515283602061024051015261024051905090509050905060c05160200280599059016000905281816020850151855160003060195a03f1508090509050905060a05260216020599059016000905261010051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260e0518260005b8381101561022557808301518186015260208101905061020a565b50505050825160200281019050604059905901600090526102c052816102c051528360206102c05101526102c05190509050905090506101005160200280599059016000905281816020850151855160003060195a03f1508090509050905060e05260405160200259905901600090526102e0526000610120525b610100516101205112156102d7576101205160200260e0510151610120516020026102e051015260016101205101610120526102a0565b60605151610100516020026102e05101526000610120525b60c05161012051121561032d576101205160200260a05101516101205160016101005101016020026102e051015260016101205101610120526102ef565b6040516020026102e051f35b50
|
File diff suppressed because one or more lines are too long
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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: "<keyFile>",
|
||||
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 <passwordfile> account new
|
||||
geth --password <passwordfile> account new
|
||||
|
||||
Note, this is meant to be used for testing only, it is a bad idea to save your
|
||||
password to file or expose in any other way.
|
||||
`,
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: accountUpdate,
|
||||
Name: "update",
|
||||
Usage: "update an existing account",
|
||||
Action: accountUpdate,
|
||||
Name: "update",
|
||||
Usage: "Update an existing account",
|
||||
ArgsUsage: "<address>",
|
||||
Description: `
|
||||
|
||||
ethereum account update <address>
|
||||
geth account update <address>
|
||||
|
||||
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 <passwordfile> account update <address>
|
||||
geth --password <passwordfile> account update <address>
|
||||
|
||||
Since only one password can be given, only format update can be performed,
|
||||
changing your password is only possible interactively.
|
||||
`,
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: accountImport,
|
||||
Name: "import",
|
||||
Usage: "import a private key into a new account",
|
||||
Action: accountImport,
|
||||
Name: "import",
|
||||
Usage: "Import a private key into a new account",
|
||||
ArgsUsage: "<keyFile>",
|
||||
Description: `
|
||||
|
||||
ethereum account import <keyfile>
|
||||
geth account import <keyfile>
|
||||
|
||||
Imports an unencrypted private key from <keyfile> 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 <passwordfile> account import <keyfile>
|
||||
geth --password <passwordfile> account import <keyfile>
|
||||
|
||||
Note:
|
||||
As you can directly copy your encrypted accounts to another ethereum instance,
|
||||
this import mechanism is not needed when you transfer an account between
|
||||
nodes.
|
||||
`,
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func accountList(ctx *cli.Context) error {
|
||||
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 {
|
||||
|
|
|
@ -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: "<filename>",
|
||||
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: "<filename> [<blockNumFirst> <blockNumLast>]",
|
||||
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: "[<blockHash> | <blockNum>]...",
|
||||
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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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."}}}}}
|
|
@ -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: "<blockNum> <outputDir>",
|
||||
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: "<genesisPath>",
|
||||
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 <gpu number>`)
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
`)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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 ""
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package natspec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
type abi2method map[[8]byte]*method
|
||||
|
||||
type NatSpec struct {
|
||||
jsvm *otto.Otto
|
||||
abiDocJson []byte
|
||||
userDoc userDoc
|
||||
tx, data string
|
||||
}
|
||||
|
||||
// main entry point for to get natspec notice for a transaction
|
||||
// the implementation is frontend friendly in that it always gives back
|
||||
// a notice that is safe to display
|
||||
// :FIXME: the second return value is an error, which can be used to fine-tune bahaviour
|
||||
func GetNotice(xeth *xeth.XEth, tx string, http *httpclient.HTTPClient) (notice string) {
|
||||
ns, err := New(xeth, tx, http)
|
||||
if err != nil {
|
||||
if ns == nil {
|
||||
return getFallbackNotice(fmt.Sprintf("no NatSpec info found for contract: %v", err), tx)
|
||||
} else {
|
||||
return getFallbackNotice(fmt.Sprintf("invalid NatSpec info: %v", err), tx)
|
||||
}
|
||||
}
|
||||
|
||||
notice, err = ns.Notice()
|
||||
if err != nil {
|
||||
return getFallbackNotice(fmt.Sprintf("NatSpec notice error: %v", err), tx)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getFallbackNotice(comment, tx string) string {
|
||||
return fmt.Sprintf("About to submit transaction (%s): %s", comment, tx)
|
||||
}
|
||||
|
||||
type transaction struct {
|
||||
To string `json:"to"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type jsonTx struct {
|
||||
Params []transaction `json:"params"`
|
||||
}
|
||||
|
||||
type contractInfo struct {
|
||||
Source string `json:"source"`
|
||||
Language string `json:"language"`
|
||||
Version string `json:"compilerVersion"`
|
||||
AbiDefinition json.RawMessage `json:"abiDefinition"`
|
||||
UserDoc userDoc `json:"userDoc"`
|
||||
DeveloperDoc json.RawMessage `json:"developerDoc"`
|
||||
}
|
||||
|
||||
func New(xeth *xeth.XEth, jsontx string, http *httpclient.HTTPClient) (self *NatSpec, err error) {
|
||||
|
||||
// extract contract address from tx
|
||||
var tx jsonTx
|
||||
err = json.Unmarshal([]byte(jsontx), &tx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t := tx.Params[0]
|
||||
contractAddress := t.To
|
||||
|
||||
content, err := FetchDocsForContract(contractAddress, xeth, http)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
self, err = NewWithDocs(content, jsontx, t.Data)
|
||||
return
|
||||
}
|
||||
|
||||
// also called by admin.contractInfo.get
|
||||
func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, client *httpclient.HTTPClient) (content []byte, err error) {
|
||||
// retrieve contract hash from state
|
||||
codehex := xeth.CodeAt(contractAddress)
|
||||
codeb := xeth.CodeAtBytes(contractAddress)
|
||||
|
||||
if codehex == "0x" {
|
||||
err = fmt.Errorf("contract (%v) not found", contractAddress)
|
||||
return
|
||||
}
|
||||
codehash := common.BytesToHash(crypto.Keccak256(codeb))
|
||||
// set up nameresolver with natspecreg + urlhint contract addresses
|
||||
reg := registrar.New(xeth)
|
||||
|
||||
// resolve host via HashReg/UrlHint Resolver
|
||||
hash, err := reg.HashToHash(codehash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if client.HasScheme("bzz") {
|
||||
content, err = client.Get("bzz://"+hash.Hex()[2:], "")
|
||||
if err == nil { // non-fatal
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
//falling back to urlhint
|
||||
}
|
||||
|
||||
uri, err := reg.HashToUrl(hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get content via http client and authenticate content using hash
|
||||
content, err = client.GetAuthContent(uri, hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NewWithDocs(infoDoc []byte, tx string, data string) (self *NatSpec, err error) {
|
||||
|
||||
var contract contractInfo
|
||||
err = json.Unmarshal(infoDoc, &contract)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
self = &NatSpec{
|
||||
jsvm: otto.New(),
|
||||
abiDocJson: []byte(contract.AbiDefinition),
|
||||
userDoc: contract.UserDoc,
|
||||
tx: tx,
|
||||
data: data,
|
||||
}
|
||||
|
||||
// load and require natspec js (but it is meant to be protected environment)
|
||||
_, err = self.jsvm.Run(natspecJS)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = self.jsvm.Run("var natspec = require('natspec');")
|
||||
return
|
||||
}
|
||||
|
||||
// type abiDoc []method
|
||||
|
||||
// type method struct {
|
||||
// Name string `json:name`
|
||||
// Inputs []input `json:inputs`
|
||||
// abiKey [8]byte
|
||||
// }
|
||||
|
||||
// type input struct {
|
||||
// Name string `json:name`
|
||||
// Type string `json:type`
|
||||
// }
|
||||
|
||||
// json skeleton for abi doc (contract method definitions)
|
||||
type method struct {
|
||||
Notice string `json:notice`
|
||||
name string
|
||||
}
|
||||
|
||||
type userDoc struct {
|
||||
Methods map[string]*method `json:methods`
|
||||
}
|
||||
|
||||
func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) {
|
||||
for signature, m := range self.userDoc.Methods {
|
||||
name := strings.Split(signature, "(")[0]
|
||||
hash := []byte(common.Bytes2Hex(crypto.Keccak256([]byte(signature))))
|
||||
var key [8]byte
|
||||
copy(key[:], hash[:8])
|
||||
if bytes.Equal(key[:], abiKey[:]) {
|
||||
meth = m
|
||||
meth.name = name
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *NatSpec) Notice() (notice string, err error) {
|
||||
var abiKey [8]byte
|
||||
if len(self.data) < 10 {
|
||||
err = fmt.Errorf("Invalid transaction data")
|
||||
return
|
||||
}
|
||||
copy(abiKey[:], self.data[2:10])
|
||||
meth := self.makeAbi2method(abiKey)
|
||||
|
||||
if meth == nil {
|
||||
err = fmt.Errorf("abi key does not match any method")
|
||||
return
|
||||
}
|
||||
notice, err = self.noticeForMethod(self.tx, meth.name, meth.Notice)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) {
|
||||
|
||||
if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil {
|
||||
return "", fmt.Errorf("natspec.js error setting transaction: %v", err)
|
||||
}
|
||||
|
||||
if _, err = self.jsvm.Run("var abi = " + string(self.abiDocJson) + ";"); err != nil {
|
||||
return "", fmt.Errorf("natspec.js error setting abi: %v", err)
|
||||
}
|
||||
|
||||
if _, err = self.jsvm.Run("var method = '" + name + "';"); err != nil {
|
||||
return "", fmt.Errorf("natspec.js error setting method: %v", err)
|
||||
}
|
||||
|
||||
if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil {
|
||||
return "", fmt.Errorf("natspec.js error setting expression: %v", err)
|
||||
}
|
||||
|
||||
self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};")
|
||||
value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("natspec.js error evaluating expression: %v", err)
|
||||
}
|
||||
evalError := "Natspec evaluation failed, wrong input params"
|
||||
if value.String() == evalError {
|
||||
return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression)
|
||||
}
|
||||
if len(value.String()) == 0 {
|
||||
return "", fmt.Errorf("natspec.js error evaluating expression")
|
||||
}
|
||||
|
||||
return value.String(), nil
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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 ""
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ func newTerminalPrompter() *terminalPrompter {
|
|||
}
|
||||
p.SetCtrlCAborts(true)
|
||||
p.SetTabCompletionStyle(liner.TabPrints)
|
||||
|
||||
p.SetMultiLineMode(true)
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
68
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go
generated
vendored
Normal file
68
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package 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)
|
||||
}
|
642
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go
generated
vendored
Normal file
642
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go
generated
vendored
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 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
|
||||
}
|
541
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go
generated
vendored
Normal file
541
vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go
generated
vendored
Normal file
|
@ -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...)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue