mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
migrate natspec,registar,compiler,httpclient
* integrate with RPC v2 * relocate packages natspec/registrar/compiler under eth * http client under node, implement URLSchemes registration on node with tests * backport natspec to RPC v1
This commit is contained in:
parent
4392570b83
commit
aab01f8067
29 changed files with 399 additions and 297 deletions
|
|
@ -18,6 +18,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -30,9 +31,9 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/natspec"
|
|
||||||
"github.com/ethereum/go-ethereum/common/registrar"
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/natspec"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/registrar"
|
||||||
re "github.com/ethereum/go-ethereum/jsre"
|
re "github.com/ethereum/go-ethereum/jsre"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -358,15 +359,23 @@ func (self *jsre) AskPassword() (string, bool) {
|
||||||
return pass, true
|
return pass, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *jsre) ConfirmTransaction(tx string) bool {
|
func (self *jsre) ConfirmTransaction(txs string) bool {
|
||||||
// Retrieve the Ethereum instance from the node
|
// Retrieve the Ethereum instance from the node
|
||||||
var ethereum *eth.Ethereum
|
var ethereum *eth.Ethereum
|
||||||
if err := self.stack.Service(ðereum); err != nil {
|
if err := self.stack.Service(ðereum); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// If natspec is enabled, ask for permission
|
// If natspec is enabled, ask for permission
|
||||||
if ethereum.NatSpec {
|
if ethereum.NatSpecEnabled {
|
||||||
notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient())
|
var tx natspec.SendTxArgs
|
||||||
|
err := json.Unmarshal([]byte(txs), &tx)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
notice, err := ethereum.NatSpec().GetNatSpec(&tx)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
fmt.Println(notice)
|
fmt.Println(notice)
|
||||||
answer, _ := self.Prompt("Confirm Transaction [y/n]")
|
answer, _ := self.Prompt("Confirm Transaction [y/n]")
|
||||||
return strings.HasPrefix(strings.Trim(answer, " "), "y")
|
return strings.HasPrefix(strings.Trim(answer, " "), "y")
|
||||||
|
|
|
||||||
|
|
@ -604,6 +604,7 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node.
|
||||||
BootstrapNodes: MakeBootstrapNodes(ctx),
|
BootstrapNodes: MakeBootstrapNodes(ctx),
|
||||||
ListenAddr: MakeListenAddress(ctx),
|
ListenAddr: MakeListenAddress(ctx),
|
||||||
NAT: MakeNAT(ctx),
|
NAT: MakeNAT(ctx),
|
||||||
|
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
||||||
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
||||||
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
|
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
|
||||||
}
|
}
|
||||||
|
|
@ -620,8 +621,7 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node.
|
||||||
Etherbase: MakeEtherbase(accman, ctx),
|
Etherbase: MakeEtherbase(accman, ctx),
|
||||||
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
|
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
|
||||||
ExtraData: MakeMinerExtra(extra, ctx),
|
ExtraData: MakeMinerExtra(extra, ctx),
|
||||||
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
|
NatSpecEnabled: ctx.GlobalBool(NatspecEnabledFlag.Name),
|
||||||
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
|
||||||
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
|
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
|
||||||
GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)),
|
GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)),
|
||||||
GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)),
|
GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)),
|
||||||
|
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package httpclient
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HTTPClient struct {
|
|
||||||
*http.Transport
|
|
||||||
DocRoot string
|
|
||||||
schemes []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(docRoot string) (self *HTTPClient) {
|
|
||||||
self = &HTTPClient{
|
|
||||||
Transport: &http.Transport{},
|
|
||||||
DocRoot: docRoot,
|
|
||||||
schemes: []string{"file"},
|
|
||||||
}
|
|
||||||
self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.
|
|
||||||
|
|
||||||
// A Client is higher-level than a RoundTripper (such as Transport) and additionally handles HTTP details such as cookies and redirects.
|
|
||||||
|
|
||||||
func (self *HTTPClient) Client() *http.Client {
|
|
||||||
return &http.Client{
|
|
||||||
Transport: self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HTTPClient) RegisterScheme(scheme string, rt http.RoundTripper) {
|
|
||||||
self.schemes = append(self.schemes, scheme)
|
|
||||||
self.RegisterProtocol(scheme, rt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HTTPClient) HasScheme(scheme string) bool {
|
|
||||||
for _, s := range self.schemes {
|
|
||||||
if s == scheme {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HTTPClient) GetAuthContent(uri string, hash common.Hash) ([]byte, error) {
|
|
||||||
// retrieve content
|
|
||||||
content, err := self.Get(uri, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check hash to authenticate content
|
|
||||||
chash := crypto.Sha3Hash(content)
|
|
||||||
if chash != hash {
|
|
||||||
return nil, fmt.Errorf("content hash mismatch %x != %x (exp)", hash[:], chash[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
return content, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get(uri, path) downloads the document at uri, if path is non-empty it
|
|
||||||
// is interpreted as a filepath to which the contents are saved
|
|
||||||
func (self *HTTPClient) Get(uri, path string) ([]byte, error) {
|
|
||||||
// retrieve content
|
|
||||||
resp, err := self.Client().Get(uri)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if resp != nil {
|
|
||||||
resp.Body.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var content []byte
|
|
||||||
content, err = ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode/100 != 2 {
|
|
||||||
return content, fmt.Errorf("HTTP error: %s", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
if path != "" {
|
|
||||||
var abspath string
|
|
||||||
abspath, err = filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(abspath, content, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return content, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
15
eth/api.go
15
eth/api.go
|
|
@ -32,12 +32,13 @@ import (
|
||||||
"github.com/ethereum/ethash"
|
"github.com/ethereum/ethash"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/compiler"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/natspec"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
|
@ -882,19 +883,9 @@ func (s *PublicTransactionPoolAPI) sign(address common.Address, tx *types.Transa
|
||||||
return tx.WithSignature(signature)
|
return tx.WithSignature(signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendTxArgs struct {
|
|
||||||
From common.Address `json:"from"`
|
|
||||||
To common.Address `json:"to"`
|
|
||||||
Gas *rpc.HexNumber `json:"gas"`
|
|
||||||
GasPrice *rpc.HexNumber `json:"gasPrice"`
|
|
||||||
Value *rpc.HexNumber `json:"value"`
|
|
||||||
Data string `json:"data"`
|
|
||||||
Nonce *rpc.HexNumber `json:"nonce"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendTransaction will create a transaction for the given transaction argument, sign it and submit it to the
|
// SendTransaction will create a transaction for the given transaction argument, sign it and submit it to the
|
||||||
// transaction pool.
|
// transaction pool.
|
||||||
func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) {
|
func (s *PublicTransactionPoolAPI) SendTransaction(args *natspec.SendTxArgs) (common.Hash, error) {
|
||||||
if args.Gas == nil {
|
if args.Gas == nil {
|
||||||
args.Gas = rpc.NewHexNumber(defaultGas)
|
args.Gas = rpc.NewHexNumber(defaultGas)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,12 @@ import (
|
||||||
"github.com/ethereum/ethash"
|
"github.com/ethereum/ethash"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
|
||||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/compiler"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/natspec"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
|
@ -70,7 +70,6 @@ type Config struct {
|
||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
|
|
||||||
NatSpec bool
|
NatSpec bool
|
||||||
DocRoot string
|
|
||||||
AutoDAG bool
|
AutoDAG bool
|
||||||
PowTest bool
|
PowTest bool
|
||||||
ExtraData []byte
|
ExtraData []byte
|
||||||
|
|
@ -80,6 +79,7 @@ type Config struct {
|
||||||
GasPrice *big.Int
|
GasPrice *big.Int
|
||||||
MinerThreads int
|
MinerThreads int
|
||||||
SolcPath string
|
SolcPath string
|
||||||
|
NatSpecEnabled bool
|
||||||
|
|
||||||
GpoMinGasPrice *big.Int
|
GpoMinGasPrice *big.Int
|
||||||
GpoMaxGasPrice *big.Int
|
GpoMaxGasPrice *big.Int
|
||||||
|
|
@ -108,6 +108,8 @@ type Ethereum struct {
|
||||||
protocolManager *ProtocolManager
|
protocolManager *ProtocolManager
|
||||||
SolcPath string
|
SolcPath string
|
||||||
solc *compiler.Solidity
|
solc *compiler.Solidity
|
||||||
|
natSpec *natspec.NatSpec
|
||||||
|
NatSpecEnabled bool
|
||||||
|
|
||||||
GpoMinGasPrice *big.Int
|
GpoMinGasPrice *big.Int
|
||||||
GpoMaxGasPrice *big.Int
|
GpoMaxGasPrice *big.Int
|
||||||
|
|
@ -116,14 +118,12 @@ type Ethereum struct {
|
||||||
GpobaseStepUp int
|
GpobaseStepUp int
|
||||||
GpobaseCorrectionFactor int
|
GpobaseCorrectionFactor int
|
||||||
|
|
||||||
httpclient *httpclient.HTTPClient
|
|
||||||
|
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
|
http *node.HTTPClient
|
||||||
miner *miner.Miner
|
miner *miner.Miner
|
||||||
|
|
||||||
Mining bool
|
Mining bool
|
||||||
MinerThreads int
|
MinerThreads int
|
||||||
NatSpec bool
|
|
||||||
AutoDAG bool
|
AutoDAG bool
|
||||||
PowTest bool
|
PowTest bool
|
||||||
autodagquit chan bool
|
autodagquit chan bool
|
||||||
|
|
@ -197,9 +197,9 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
accountManager: config.AccountManager,
|
accountManager: config.AccountManager,
|
||||||
etherbase: config.Etherbase,
|
etherbase: config.Etherbase,
|
||||||
netVersionId: config.NetworkId,
|
netVersionId: config.NetworkId,
|
||||||
NatSpec: config.NatSpec,
|
|
||||||
MinerThreads: config.MinerThreads,
|
MinerThreads: config.MinerThreads,
|
||||||
SolcPath: config.SolcPath,
|
SolcPath: config.SolcPath,
|
||||||
|
NatSpecEnabled: config.NatSpecEnabled,
|
||||||
AutoDAG: config.AutoDAG,
|
AutoDAG: config.AutoDAG,
|
||||||
PowTest: config.PowTest,
|
PowTest: config.PowTest,
|
||||||
GpoMinGasPrice: config.GpoMinGasPrice,
|
GpoMinGasPrice: config.GpoMinGasPrice,
|
||||||
|
|
@ -208,9 +208,12 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
GpobaseStepDown: config.GpobaseStepDown,
|
GpobaseStepDown: config.GpobaseStepDown,
|
||||||
GpobaseStepUp: config.GpobaseStepUp,
|
GpobaseStepUp: config.GpobaseStepUp,
|
||||||
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
|
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
|
||||||
httpclient: httpclient.New(config.DocRoot),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backend := NewPublicBlockChainAPI(eth.BlockChain(), eth.ChainDb(), eth.EventMux(), eth.AccountManager())
|
||||||
|
// eth.natSpec = natspec.New(backend, ctx.HTTP, registrar.New(backend))
|
||||||
|
eth.natSpec = natspec.New(backend, ctx.HTTP, nil)
|
||||||
|
|
||||||
if config.PowTest {
|
if config.PowTest {
|
||||||
glog.V(logger.Info).Infof("ethash used in test mode")
|
glog.V(logger.Info).Infof("ethash used in test mode")
|
||||||
eth.pow, err = ethash.NewForTesting()
|
eth.pow, err = ethash.NewForTesting()
|
||||||
|
|
@ -308,6 +311,10 @@ func (s *Ethereum) APIs() []rpc.API {
|
||||||
Namespace: "debug",
|
Namespace: "debug",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: NewPrivateDebugAPI(s),
|
Service: NewPrivateDebugAPI(s),
|
||||||
|
}, {
|
||||||
|
Namespace: "eth",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: s.natSpec,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +335,8 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Ethereum) URLSchemes() []node.URLScheme { return nil }
|
||||||
|
|
||||||
// set in js console via admin interface or wrapper from cli flags
|
// set in js console via admin interface or wrapper from cli flags
|
||||||
func (self *Ethereum) SetEtherbase(etherbase common.Address) {
|
func (self *Ethereum) SetEtherbase(etherbase common.Address) {
|
||||||
self.etherbase = etherbase
|
self.etherbase = etherbase
|
||||||
|
|
@ -342,6 +351,7 @@ func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager
|
||||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||||
|
func (s *Ethereum) NatSpec() *natspec.NatSpec { return s.natSpec }
|
||||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||||
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
|
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
|
||||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||||
|
|
@ -450,10 +460,10 @@ func (self *Ethereum) StopAutoDAG() {
|
||||||
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
|
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPClient returns the light http client used for fetching offchain docs
|
// HTTP returns the light http client used for fetching offchain docs
|
||||||
// (natspec, source for verification)
|
// (natspec, source for verification)
|
||||||
func (self *Ethereum) HTTPClient() *httpclient.HTTPClient {
|
func (self *Ethereum) HTTP() *node.HTTPClient {
|
||||||
return self.httpclient
|
return self.http
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Ethereum) Solc() (*compiler.Solidity, error) {
|
func (self *Ethereum) Solc() (*compiler.Solidity, error) {
|
||||||
|
|
|
||||||
|
|
@ -23,58 +23,70 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
"github.com/ethereum/go-ethereum/eth/registrar"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
rpc "github.com/ethereum/go-ethereum/rpc/v2"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type abi2method map[[8]byte]*method
|
type abi2method map[[8]byte]*method
|
||||||
|
|
||||||
type NatSpec struct {
|
type NatSpec struct {
|
||||||
jsvm *otto.Otto
|
backend Backend
|
||||||
abiDocJson []byte
|
http *node.HTTPClient
|
||||||
userDoc userDoc
|
reg *registrar.Registrar
|
||||||
tx, data string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// main entry point for to get natspec notice for a transaction
|
type Backend interface {
|
||||||
// the implementation is frontend friendly in that it always gives back
|
GetCode(address common.Address, blockNr rpc.BlockNumber) (string, error)
|
||||||
// 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()
|
func New(backend Backend, http *node.HTTPClient, reg *registrar.Registrar) *NatSpec {
|
||||||
if err != nil {
|
return &NatSpec{backend, http, reg}
|
||||||
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 {
|
type contractInfo struct {
|
||||||
|
jsvm *otto.Otto
|
||||||
|
abiDocJson []byte
|
||||||
|
userDoc userDoc
|
||||||
|
tx *SendTxArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendTxArgs struct {
|
||||||
|
From common.Address `json:"from"`
|
||||||
|
To common.Address `json:"to"`
|
||||||
|
Gas *rpc.HexNumber `json:"gas"`
|
||||||
|
GasPrice *rpc.HexNumber `json:"gasPrice"`
|
||||||
|
Value *rpc.HexNumber `json:"value"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
Nonce *rpc.HexNumber `json:"nonce"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *NatSpec) GetNatSpec(tx *SendTxArgs) (string, error) {
|
||||||
|
to := tx.To
|
||||||
|
// in principle this could be cached, but practically swarm takes care of that
|
||||||
|
info, err := self.GetContractInfo(to)
|
||||||
|
if err != nil {
|
||||||
|
if info == nil {
|
||||||
|
return "", fmt.Errorf("no contract info found for %v: %v", to, err)
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("invalid contract info for %v: %v", to, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ns, err := newContractInfo(info, tx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid contract info for %v: %v", to, err)
|
||||||
|
}
|
||||||
|
notice, err := ns.notice()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("NatSpec notice error for contract %v: %v", to, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return notice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContractInfo struct {
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
Version string `json:"compilerVersion"`
|
Version string `json:"compilerVersion"`
|
||||||
|
|
@ -83,81 +95,56 @@ type contractInfo struct {
|
||||||
DeveloperDoc json.RawMessage `json:"developerDoc"`
|
DeveloperDoc json.RawMessage `json:"developerDoc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(xeth *xeth.XEth, jsontx string, http *httpclient.HTTPClient) (self *NatSpec, err error) {
|
func (self *NatSpec) GetContractInfo(to common.Address) (*ContractInfo, 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
|
// retrieve contract hash from state
|
||||||
codehex := xeth.CodeAt(contractAddress)
|
codehex, err := self.backend.GetCode(to, -1)
|
||||||
codeb := xeth.CodeAtBytes(contractAddress)
|
if err != nil || len(codehex) <= 3 {
|
||||||
|
return nil, fmt.Errorf("contract (%v) not found", to)
|
||||||
if codehex == "0x" {
|
|
||||||
err = fmt.Errorf("contract (%v) not found", contractAddress)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
codehash := common.BytesToHash(crypto.Sha3(codeb))
|
codehash := common.BytesToHash(crypto.Sha3(common.FromHex(codehex)))
|
||||||
// set up nameresolver with natspecreg + urlhint contract addresses
|
|
||||||
reg := registrar.New(xeth)
|
|
||||||
|
|
||||||
// resolve host via HashReg/UrlHint Resolver
|
// resolve host via HashReg/UrlHint Resolver
|
||||||
hash, err := reg.HashToHash(codehash)
|
hash, err := self.reg.HashToHash(codehash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, fmt.Errorf("no content hash registered for contract %v: %v", to, err)
|
||||||
}
|
}
|
||||||
if client.HasScheme("bzz") {
|
var data []byte
|
||||||
content, err = client.Get("bzz://"+hash.Hex()[2:], "")
|
if self.http.HasScheme("bzz") {
|
||||||
if err == nil { // non-fatal
|
data, err = self.http.GetBody("bzz://" + hash.Hex()[2:])
|
||||||
return
|
if err != nil { // non-fatal
|
||||||
|
data = nil
|
||||||
}
|
}
|
||||||
err = nil
|
|
||||||
//falling back to urlhint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uri, err := reg.HashToUrl(hash)
|
//falling back to urlhint
|
||||||
|
if data == nil {
|
||||||
|
uri, err := self.reg.HashToUrl(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get content via http client and authenticate content using hash
|
// get content via http client and authenticate content using hash
|
||||||
content, err = client.GetAuthContent(uri, hash)
|
data, err = self.http.GetAuthBody(uri, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithDocs(infoDoc []byte, tx string, data string) (self *NatSpec, err error) {
|
var info ContractInfo
|
||||||
|
err = json.Unmarshal(data, &info)
|
||||||
var contract contractInfo
|
|
||||||
err = json.Unmarshal(infoDoc, &contract)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self = &NatSpec{
|
func newContractInfo(info *ContractInfo, tx *SendTxArgs) (self *contractInfo, err error) {
|
||||||
|
|
||||||
|
self = &contractInfo{
|
||||||
jsvm: otto.New(),
|
jsvm: otto.New(),
|
||||||
abiDocJson: []byte(contract.AbiDefinition),
|
abiDocJson: []byte(info.AbiDefinition),
|
||||||
userDoc: contract.UserDoc,
|
userDoc: info.UserDoc,
|
||||||
tx: tx,
|
tx: tx,
|
||||||
data: data,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load and require natspec js (but it is meant to be protected environment)
|
// load and require natspec js (but it is meant to be protected environment)
|
||||||
|
|
@ -192,7 +179,7 @@ type userDoc struct {
|
||||||
Methods map[string]*method `json:methods`
|
Methods map[string]*method `json:methods`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) {
|
func (self *contractInfo) makeAbi2method(abiKey [8]byte) (meth *method) {
|
||||||
for signature, m := range self.userDoc.Methods {
|
for signature, m := range self.userDoc.Methods {
|
||||||
name := strings.Split(signature, "(")[0]
|
name := strings.Split(signature, "(")[0]
|
||||||
hash := []byte(common.Bytes2Hex(crypto.Sha3([]byte(signature))))
|
hash := []byte(common.Bytes2Hex(crypto.Sha3([]byte(signature))))
|
||||||
|
|
@ -207,26 +194,30 @@ func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *NatSpec) Notice() (notice string, err error) {
|
func (self *contractInfo) notice() (notice string, err error) {
|
||||||
var abiKey [8]byte
|
var abiKey [8]byte
|
||||||
if len(self.data) < 10 {
|
if len(self.tx.Data) < 10 {
|
||||||
err = fmt.Errorf("Invalid transaction data")
|
err = fmt.Errorf("Invalid transaction data")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
copy(abiKey[:], self.data[2:10])
|
copy(abiKey[:], self.tx.Data[2:10])
|
||||||
meth := self.makeAbi2method(abiKey)
|
meth := self.makeAbi2method(abiKey)
|
||||||
|
|
||||||
if meth == nil {
|
if meth == nil {
|
||||||
err = fmt.Errorf("abi key does not match any method")
|
err = fmt.Errorf("abi key does not match any method")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
notice, err = self.noticeForMethod(self.tx, meth.name, meth.Notice)
|
notice, err = self.noticeForMethod(meth.name, meth.Notice)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) {
|
func (self *contractInfo) noticeForMethod(name, expression string) (notice string, err error) {
|
||||||
|
tx, err := json.Marshal(self.tx)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil {
|
if _, err = self.jsvm.Run("var transaction = " + string(tx) + ";"); err != nil {
|
||||||
return "", fmt.Errorf("natspec.js error setting transaction: %v", err)
|
return "", fmt.Errorf("natspec.js error setting transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,12 +238,13 @@ func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("natspec.js error evaluating expression: %v", err)
|
return "", fmt.Errorf("natspec.js error evaluating expression: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
evalError := "Natspec evaluation failed, wrong input params"
|
evalError := "Natspec evaluation failed, wrong input params"
|
||||||
if value.String() == evalError {
|
if value.String() == evalError {
|
||||||
return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression)
|
return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression)
|
||||||
}
|
}
|
||||||
if len(value.String()) == 0 {
|
if len(value.String()) == 0 {
|
||||||
return "", fmt.Errorf("natspec.js error evaluating expression")
|
return "", fmt.Errorf("natspec.js error evaluating expression: empty notice")
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.String(), nil
|
return value.String(), nil
|
||||||
|
|
@ -49,6 +49,10 @@ type Config struct {
|
||||||
// in memory.
|
// in memory.
|
||||||
DataDir string
|
DataDir string
|
||||||
|
|
||||||
|
// Document Root, durectory path for serving files from local file system
|
||||||
|
// used as root for Node#HTTP.Get("file://") requestss
|
||||||
|
DocRoot string
|
||||||
|
|
||||||
// This field should be a valid secp256k1 private key that will be used for both
|
// This field should be a valid secp256k1 private key that will be used for both
|
||||||
// remote peer identification as well as network traffic encryption. If no key
|
// remote peer identification as well as network traffic encryption. If no key
|
||||||
// is configured, the preset one is loaded from the data dir, generating it if
|
// is configured, the preset one is loaded from the data dir, generating it if
|
||||||
|
|
|
||||||
139
node/http.go
Normal file
139
node/http.go
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newHTTP(docRoot string) *HTTPClient {
|
||||||
|
t := &http.Transport{}
|
||||||
|
if len(docRoot) > 0 {
|
||||||
|
t.RegisterProtocol("file", http.NewFileTransport(http.Dir(docRoot)))
|
||||||
|
}
|
||||||
|
return &HTTPClient{
|
||||||
|
Client: &http.Client{Transport: t},
|
||||||
|
transport: t,
|
||||||
|
schemes: []string{"file"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HTTPClient) registerSchemes(schemes []URLScheme) {
|
||||||
|
for _, scheme := range schemes {
|
||||||
|
self.schemes = append(self.schemes, scheme.Name)
|
||||||
|
self.transport.RegisterProtocol(scheme.Name, scheme.RoundTripper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPClient struct {
|
||||||
|
*http.Client
|
||||||
|
transport *http.Transport
|
||||||
|
schemes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.
|
||||||
|
|
||||||
|
// A Client is higher-level than a RoundTripper (such as Transport) and additionally handles HTTP details such as cookies and redirects.
|
||||||
|
|
||||||
|
type URLScheme struct {
|
||||||
|
Name string
|
||||||
|
RoundTripper http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HTTPClient) HasScheme(scheme string) bool {
|
||||||
|
for _, s := range self.schemes {
|
||||||
|
if s == scheme {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBody(uri) downloads the document at uri, if path is non-empty it
|
||||||
|
// GetBody(uri) fetches the uri and returns the body
|
||||||
|
func (self *HTTPClient) GetBody(uri string) ([]byte, error) {
|
||||||
|
// retrieve body
|
||||||
|
resp, err := self.Get(uri)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if resp != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var body []byte
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode/100 != 2 {
|
||||||
|
return body, fmt.Errorf("HTTP error: %s", resp.Status)
|
||||||
|
}
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download(uri, path) fetches the uri and writes the body into the file
|
||||||
|
func (self *HTTPClient) Download(uri, path string) error {
|
||||||
|
|
||||||
|
resp, err := self.Get(uri)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if resp != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var abspath string
|
||||||
|
abspath, err = filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w, err := os.Create(abspath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
_, err = io.Copy(w, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HTTPClient) GetAuthBody(uri string, hash common.Hash) ([]byte, error) {
|
||||||
|
// retrieve body
|
||||||
|
body, err := self.GetBody(uri)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check hash to authenticate body
|
||||||
|
chash := crypto.Sha3Hash(body)
|
||||||
|
if chash != hash {
|
||||||
|
return nil, fmt.Errorf("body hash mismatch %x != %x (exp)", hash[:], chash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return body, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// type HTTPClientAPI interface {
|
||||||
|
// Get(uri string) (*http.Response, err)
|
||||||
|
// Download(uri, path string) error
|
||||||
|
// GetBody(uri) ([]byte, error)
|
||||||
|
// HasScheme(scheme string) bool
|
||||||
|
// GetAuthBody(uri string, hash common.Hash) ([]byte, error)
|
||||||
|
// }
|
||||||
|
|
@ -14,9 +14,10 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package httpclient
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -27,20 +28,20 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetAuthContent(t *testing.T) {
|
func TestGetAuthBody(t *testing.T) {
|
||||||
dir, err := ioutil.TempDir("", "httpclient-test")
|
dir, err := ioutil.TempDir("", "httpclient-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("cannot create temporary directory:", err)
|
t.Fatal("cannot create temporary directory:", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
client := New(dir)
|
client := newHTTP(dir)
|
||||||
|
|
||||||
text := "test"
|
text := "test"
|
||||||
hash := crypto.Sha3Hash([]byte(text))
|
hash := crypto.Sha3Hash([]byte(text))
|
||||||
if err := ioutil.WriteFile(path.Join(dir, "test.content"), []byte(text), os.ModePerm); err != nil {
|
if err := ioutil.WriteFile(path.Join(dir, "test.content"), []byte(text), os.ModePerm); err != nil {
|
||||||
t.Fatal("could not write test file", err)
|
t.Fatal("could not write test file", err)
|
||||||
}
|
}
|
||||||
content, err := client.GetAuthContent("file:///test.content", hash)
|
content, err := client.GetAuthBody("file:///test.content", hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("no error expected, got %v", err)
|
t.Errorf("no error expected, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -49,8 +50,8 @@ func TestGetAuthContent(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hash = common.Hash{}
|
hash = common.Hash{}
|
||||||
content, err = client.GetAuthContent("file:///test.content", hash)
|
content, err = client.GetAuthBody("file:///test.content", hash)
|
||||||
expected := "content hash mismatch 0000000000000000000000000000000000000000000000000000000000000000 != 9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 (exp)"
|
expected := "body hash mismatch 0000000000000000000000000000000000000000000000000000000000000000 != 9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 (exp)"
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected error, got nothing")
|
t.Errorf("expected error, got nothing")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -61,17 +62,26 @@ func TestGetAuthContent(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type rt struct{}
|
type rterr error
|
||||||
|
type rt struct{ error }
|
||||||
|
|
||||||
func (rt) RoundTrip(req *http.Request) (resp *http.Response, err error) { return }
|
// roundtripper with an error. this can prove the roundtrip for testing
|
||||||
|
// without having the need to write a Response
|
||||||
|
func (e rt) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||||
|
return &http.Response{}, e.error
|
||||||
|
}
|
||||||
|
|
||||||
func TestRegisterScheme(t *testing.T) {
|
func TestHasScheme(t *testing.T) {
|
||||||
client := New("/tmp/")
|
client := newHTTP("")
|
||||||
if client.HasScheme("scheme") {
|
if client.HasScheme("scheme") {
|
||||||
t.Errorf("expected scheme not to be registered")
|
t.Errorf("expected scheme not to be registered")
|
||||||
}
|
}
|
||||||
client.RegisterScheme("scheme", rt{})
|
client.registerSchemes([]URLScheme{{"scheme", &rt{rterr(errors.New("rt"))}}})
|
||||||
if !client.HasScheme("scheme") {
|
if !client.HasScheme("scheme") {
|
||||||
t.Errorf("expected scheme to be registered")
|
t.Errorf("expected scheme to be registered")
|
||||||
}
|
}
|
||||||
|
body, err := client.GetBody("scheme://url.com")
|
||||||
|
if _, ok := err.(rterr); !ok {
|
||||||
|
t.Fatalf("failed to use registered scheme. Got error %v and body %v", body, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
10
node/node.go
10
node/node.go
|
|
@ -44,6 +44,7 @@ var (
|
||||||
type Node struct {
|
type Node struct {
|
||||||
datadir string // Path to the currently used data directory
|
datadir string // Path to the currently used data directory
|
||||||
eventmux *event.TypeMux // Event multiplexer used between the services of a stack
|
eventmux *event.TypeMux // Event multiplexer used between the services of a stack
|
||||||
|
http *HTTPClient // HTTP Client that services can register their URL schemes
|
||||||
|
|
||||||
serverConfig *p2p.Server // Configuration of the underlying P2P networking layer
|
serverConfig *p2p.Server // Configuration of the underlying P2P networking layer
|
||||||
server *p2p.Server // Currently running P2P networking layer
|
server *p2p.Server // Currently running P2P networking layer
|
||||||
|
|
@ -68,6 +69,7 @@ func New(conf *Config) (*Node, error) {
|
||||||
if conf.DataDir != "" {
|
if conf.DataDir != "" {
|
||||||
nodeDbPath = filepath.Join(conf.DataDir, datadirNodeDatabase)
|
nodeDbPath = filepath.Join(conf.DataDir, datadirNodeDatabase)
|
||||||
}
|
}
|
||||||
|
httpClient := newHTTP(conf.DocRoot)
|
||||||
return &Node{
|
return &Node{
|
||||||
datadir: conf.DataDir,
|
datadir: conf.DataDir,
|
||||||
serverConfig: &p2p.Server{
|
serverConfig: &p2p.Server{
|
||||||
|
|
@ -87,6 +89,7 @@ func New(conf *Config) (*Node, error) {
|
||||||
},
|
},
|
||||||
serviceFuncs: []ServiceConstructor{},
|
serviceFuncs: []ServiceConstructor{},
|
||||||
eventmux: new(event.TypeMux),
|
eventmux: new(event.TypeMux),
|
||||||
|
http: httpClient,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,6 +126,7 @@ func (n *Node) Start() error {
|
||||||
datadir: n.datadir,
|
datadir: n.datadir,
|
||||||
services: make(map[reflect.Type]Service),
|
services: make(map[reflect.Type]Service),
|
||||||
EventMux: n.eventmux,
|
EventMux: n.eventmux,
|
||||||
|
HTTP: n.http,
|
||||||
}
|
}
|
||||||
for kind, s := range services { // copy needed for threaded access
|
for kind, s := range services { // copy needed for threaded access
|
||||||
ctx.services[kind] = s
|
ctx.services[kind] = s
|
||||||
|
|
@ -132,6 +136,7 @@ func (n *Node) Start() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
n.http.registerSchemes(service.URLSchemes())
|
||||||
kind := reflect.TypeOf(service)
|
kind := reflect.TypeOf(service)
|
||||||
if _, exists := services[kind]; exists {
|
if _, exists := services[kind]; exists {
|
||||||
return &DuplicateServiceError{Kind: kind}
|
return &DuplicateServiceError{Kind: kind}
|
||||||
|
|
@ -163,6 +168,7 @@ func (n *Node) Start() error {
|
||||||
// Mark the service started for potential cleanup
|
// Mark the service started for potential cleanup
|
||||||
started = append(started, kind)
|
started = append(started, kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish initializing the startup
|
// Finish initializing the startup
|
||||||
n.services = services
|
n.services = services
|
||||||
n.server = running
|
n.server = running
|
||||||
|
|
@ -290,6 +296,10 @@ func (n *Node) APIs() []rpc.API {
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: NewPublicDebugAPI(n),
|
Service: NewPublicDebugAPI(n),
|
||||||
Public: true,
|
Public: true,
|
||||||
|
}, {
|
||||||
|
Namespace: "http",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: n.http,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Inject all the APIs owned by various services
|
// Inject all the APIs owned by various services
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,15 @@ import (
|
||||||
//
|
//
|
||||||
// The following methods are needed to implement a node.Service:
|
// The following methods are needed to implement a node.Service:
|
||||||
// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on
|
// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on
|
||||||
|
// - APIs() []rpc.API - APIs exposed to the rpc
|
||||||
|
// - URLSchemes() []URLScheme - URL schemes handled by the service registered on Node#HTTP
|
||||||
// - Start() error - method invoked when the node is ready to start the service
|
// - Start() error - method invoked when the node is ready to start the service
|
||||||
// - Stop() error - method invoked when the node terminates the service
|
// - Stop() error - method invoked when the node terminates the service
|
||||||
type SampleService struct{}
|
type SampleService struct{}
|
||||||
|
|
||||||
func (s *SampleService) Protocols() []p2p.Protocol { return nil }
|
func (s *SampleService) Protocols() []p2p.Protocol { return nil }
|
||||||
func (s *SampleService) APIs() []rpc.API { return nil }
|
func (s *SampleService) APIs() []rpc.API { return nil }
|
||||||
|
func (s *SampleService) URLSchemes() []node.URLScheme { return nil }
|
||||||
func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil }
|
func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil }
|
||||||
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
|
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -131,6 +132,36 @@ func TestServiceRegistry(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests whether services can be registered and duplicates caught.
|
||||||
|
func TestServiceURLSchemes(t *testing.T) {
|
||||||
|
stack, err := New(testNodeConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create protocol stack: %v", err)
|
||||||
|
}
|
||||||
|
var h *HTTPClient
|
||||||
|
newSchemeUserService := func(ctx *ServiceContext) (Service, error) {
|
||||||
|
h = ctx.HTTP
|
||||||
|
return new(SchemeUserService), nil
|
||||||
|
}
|
||||||
|
// Register a batch of unique services and ensure they start successfully
|
||||||
|
services := []ServiceConstructor{NewSchemeService, newSchemeUserService}
|
||||||
|
for i, constructor := range services {
|
||||||
|
if err := stack.Register(constructor); err != nil {
|
||||||
|
t.Fatalf("service #%d: registration failed: %v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := stack.Start(); err != nil {
|
||||||
|
t.Fatalf("failed to start original service stack: %v", err)
|
||||||
|
}
|
||||||
|
defer stack.Stop()
|
||||||
|
|
||||||
|
body, err := h.GetBody("rt://url.com")
|
||||||
|
if _, ok := err.(rterr); !ok {
|
||||||
|
t.Fatalf("failed to use registered scheme. Got error %v and body %v", body, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that registered services get started and stopped correctly.
|
// Tests that registered services get started and stopped correctly.
|
||||||
func TestServiceLifeCycle(t *testing.T) {
|
func TestServiceLifeCycle(t *testing.T) {
|
||||||
stack, err := New(testNodeConfig)
|
stack, err := New(testNodeConfig)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ type ServiceContext struct {
|
||||||
datadir string // Data directory for protocol persistence
|
datadir string // Data directory for protocol persistence
|
||||||
services map[reflect.Type]Service // Index of the already constructed services
|
services map[reflect.Type]Service // Index of the already constructed services
|
||||||
EventMux *event.TypeMux // Event multiplexer used for decoupled notifications
|
EventMux *event.TypeMux // Event multiplexer used for decoupled notifications
|
||||||
|
HTTP *HTTPClient // HTTP Client services register their URL schemes with
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDatabase opens an existing database with the given name (or creates one
|
// OpenDatabase opens an existing database with the given name (or creates one
|
||||||
|
|
@ -74,6 +75,9 @@ type Service interface {
|
||||||
// APIs retrieves the list of RPC descriptors the service provides
|
// APIs retrieves the list of RPC descriptors the service provides
|
||||||
APIs() []rpc.API
|
APIs() []rpc.API
|
||||||
|
|
||||||
|
// Url Schemes to register on the node http client
|
||||||
|
URLSchemes() []URLScheme
|
||||||
|
|
||||||
// Start is called after all services have been constructed and the networking
|
// Start is called after all services have been constructed and the networking
|
||||||
// layer was also initialized to spawn any goroutines required by the service.
|
// layer was also initialized to spawn any goroutines required by the service.
|
||||||
Start(server *p2p.Server) error
|
Start(server *p2p.Server) error
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
|
@ -31,6 +32,7 @@ type NoopService struct{}
|
||||||
|
|
||||||
func (s *NoopService) Protocols() []p2p.Protocol { return nil }
|
func (s *NoopService) Protocols() []p2p.Protocol { return nil }
|
||||||
func (s *NoopService) APIs() []rpc.API { return nil }
|
func (s *NoopService) APIs() []rpc.API { return nil }
|
||||||
|
func (s *NoopService) URLSchemes() []URLScheme { return nil }
|
||||||
func (s *NoopService) Start(*p2p.Server) error { return nil }
|
func (s *NoopService) Start(*p2p.Server) error { return nil }
|
||||||
func (s *NoopService) Stop() error { return nil }
|
func (s *NoopService) Stop() error { return nil }
|
||||||
|
|
||||||
|
|
@ -48,6 +50,26 @@ func NewNoopServiceB(*ServiceContext) (Service, error) { return new(NoopServiceB
|
||||||
func NewNoopServiceC(*ServiceContext) (Service, error) { return new(NoopServiceC), nil }
|
func NewNoopServiceC(*ServiceContext) (Service, error) { return new(NoopServiceC), nil }
|
||||||
func NewNoopServiceD(*ServiceContext) (Service, error) { return new(NoopServiceD), nil }
|
func NewNoopServiceD(*ServiceContext) (Service, error) { return new(NoopServiceD), nil }
|
||||||
|
|
||||||
|
// Scheme Service is a service that registers a url scheme on the node
|
||||||
|
type SchemeService struct{ NoopService }
|
||||||
|
|
||||||
|
func NewSchemeService(*ServiceContext) (Service, error) { return new(SchemeService), nil }
|
||||||
|
|
||||||
|
// func (s *NoopService) Protocols() []p2p.Protocol { return nil }
|
||||||
|
// func (s *NoopService) APIs() []rpc.API { return nil }
|
||||||
|
// func (s *NoopService) URLSchemes() []URLScheme { return nil }
|
||||||
|
// func (s *NoopService) Start(*p2p.Server) error { return nil }
|
||||||
|
// func (s *NoopService) Stop() error { return nil }
|
||||||
|
|
||||||
|
func (*SchemeService) URLSchemes() []URLScheme {
|
||||||
|
return []URLScheme{{"rt", &rt{rterr(errors.New("rt"))}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scheme User Service is a service that expects a http client on the service context
|
||||||
|
type SchemeUserService struct {
|
||||||
|
NoopService
|
||||||
|
}
|
||||||
|
|
||||||
// InstrumentedService is an implementation of Service for which all interface
|
// InstrumentedService is an implementation of Service for which all interface
|
||||||
// methods can be instrumented both return value as well as event hook wise.
|
// methods can be instrumented both return value as well as event hook wise.
|
||||||
type InstrumentedService struct {
|
type InstrumentedService struct {
|
||||||
|
|
@ -73,6 +95,10 @@ func (s *InstrumentedService) APIs() []rpc.API {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *InstrumentedService) URLSchemes() []URLScheme {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *InstrumentedService) Start(server *p2p.Server) error {
|
func (s *InstrumentedService) Start(server *p2p.Server) error {
|
||||||
if s.startHook != nil {
|
if s.startHook != nil {
|
||||||
s.startHook(server)
|
s.startHook(server)
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
|
||||||
"github.com/ethereum/go-ethereum/common/natspec"
|
|
||||||
"github.com/ethereum/go-ethereum/common/registrar"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/compiler"
|
||||||
|
// "github.com/ethereum/go-ethereum/eth/natspec"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/registrar"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
|
@ -426,12 +426,12 @@ func (self *adminApi) RegisterUrl(req *shared.Request) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *adminApi) StartNatSpec(req *shared.Request) (interface{}, error) {
|
func (self *adminApi) StartNatSpec(req *shared.Request) (interface{}, error) {
|
||||||
self.ethereum.NatSpec = true
|
self.ethereum.NatSpecEnabled = true
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *adminApi) StopNatSpec(req *shared.Request) (interface{}, error) {
|
func (self *adminApi) StopNatSpec(req *shared.Request) (interface{}, error) {
|
||||||
self.ethereum.NatSpec = false
|
self.ethereum.NatSpecEnabled = false
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -441,13 +441,7 @@ func (self *adminApi) GetContractInfo(req *shared.Request) (interface{}, error)
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
return nil, shared.NewDecodeParamError(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
infoDoc, err := natspec.FetchDocsForContract(args.Contract, self.xeth, self.ethereum.HTTPClient())
|
info, err := self.ethereum.NatSpec().GetContractInfo(common.HexToAddress(args.Contract))
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var info interface{}
|
|
||||||
err = self.coder.Decode(infoDoc, &info)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -461,12 +455,12 @@ func (self *adminApi) HttpGet(req *shared.Request) (interface{}, error) {
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
return nil, shared.NewDecodeParamError(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := self.ethereum.HTTPClient().Get(args.Uri, args.Path)
|
body, err := self.ethereum.HTTP().GetBody(args.Uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(resp), nil
|
return string(body), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *adminApi) EnableUserAgent(req *shared.Request) (interface{}, error) {
|
func (self *adminApi) EnableUserAgent(req *shared.Request) (interface{}, error) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ package api
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
"github.com/ethereum/go-ethereum/eth/compiler"
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/natspec"
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/natspec"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||||
|
|
@ -372,10 +372,11 @@ func (self *ethApi) GetNatSpec(req *shared.Request) (interface{}, error) {
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
return nil, shared.NewDecodeParamError(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, args.To, args.Data)
|
tx := &natspec.SendTxArgs{
|
||||||
notice := natspec.GetNotice(self.xeth, jsontx, self.ethereum.HTTPClient())
|
To: common.HexToAddress(args.To),
|
||||||
|
Data: args.Data,
|
||||||
return notice, nil
|
}
|
||||||
|
return self.ethereum.NatSpec().GetNatSpec(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
|
func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event/filter"
|
"github.com/ethereum/go-ethereum/event/filter"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"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"
|
||||||
rpc "github.com/ethereum/go-ethereum/rpc/v2"
|
rpc "github.com/ethereum/go-ethereum/rpc/v2"
|
||||||
|
|
||||||
|
|
@ -112,6 +113,8 @@ func (s *Whisper) APIs() []rpc.API {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Whisper) URLSchemes() []node.URLScheme { return nil }
|
||||||
|
|
||||||
// Protocols returns the whisper sub-protocols ran by this particular client.
|
// Protocols returns the whisper sub-protocols ran by this particular client.
|
||||||
func (self *Whisper) Protocols() []p2p.Protocol {
|
func (self *Whisper) Protocols() []p2p.Protocol {
|
||||||
return []p2p.Protocol{self.protocol}
|
return []p2p.Protocol{self.protocol}
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,13 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/compiler"
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
|
@ -865,7 +865,8 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
|
||||||
return common.ToHex(res), gas.String(), err
|
return common.ToHex(res), gas.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) ConfirmTransaction(tx string) bool {
|
func (self *XEth) ConfirmTransaction(to, data string) bool {
|
||||||
|
tx := fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, to, data)
|
||||||
return self.frontend.ConfirmTransaction(tx)
|
return self.frontend.ConfirmTransaction(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -962,9 +963,7 @@ func (self *XEth) SignTransaction(fromStr, toStr, nonceStr, valueStr, gasStr, ga
|
||||||
|
|
||||||
func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
||||||
|
|
||||||
// this minimalistic recoding is enough (works for natspec.js)
|
if !self.ConfirmTransaction(toStr, codeStr) {
|
||||||
var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, toStr, codeStr)
|
|
||||||
if !self.ConfirmTransaction(jsontx) {
|
|
||||||
err := fmt.Errorf("Transaction not confirmed")
|
err := fmt.Errorf("Transaction not confirmed")
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue