diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index bc105ef37c..f10013569e 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -33,6 +33,7 @@ var Modules = map[string]string{ "swarmfs": SwarmfsJs, "txpool": TxpoolJs, "les": LESJs, + "lespay": LESPAYJs, } const ChequebookJs = ` @@ -856,3 +857,20 @@ web3._extend({ ] }); ` + +const LESPAYJs = ` +web3._extend({ + property: 'lespay', + methods: + [ + new web3._extend.Method({ + name: 'connection', + call: 'lespay_connection', + params: 6 + }), + ], + properties: + [ + ] +}); +` diff --git a/les/api.go b/les/api.go index 0d944e7dcd..f17f8ed943 100644 --- a/les/api.go +++ b/les/api.go @@ -17,13 +17,16 @@ package les import ( + "context" "errors" "fmt" "time" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" ) var ( @@ -349,3 +352,102 @@ func (api *PrivateLightAPI) GetCheckpointContractAddress() (string, error) { } return api.backend.oracle.config.Address.Hex(), nil } + +type PrivateLespayAPI struct { + peerSet *peerSet + clientHandler *clientHandler + dht *discv5.Network + tokenSale *tokenSale +} + +// NewPrivateLespayAPI creates a new LESPAY API. +func NewPrivateLespayAPI(peerSet *peerSet, clientHandler *clientHandler, dht *discv5.Network, tokenSale *tokenSale) *PrivateLespayAPI { + return &PrivateLespayAPI{ + peerSet: peerSet, + clientHandler: clientHandler, + dht: dht, + tokenSale: tokenSale, + } +} + +func (api *PrivateLespayAPI) makeCall(ctx context.Context, remote bool, nodeStr string, cmd []byte) ([]byte, error) { + var ( + id enode.ID + freeID string + peer *peer + node *enode.Node + ) + if nodeStr != "" { + if peer = api.peerSet.Peer(nodeStr); peer != nil { + id = peer.ID() + freeID = peer.freeClientId() + } else { + var err error + if node, err = enode.Parse(enode.ValidSchemes, nodeStr); err == nil { + id = node.ID() + freeID = node.IP().String() + } else { + return nil, err + } + } + } + + if remote { + var ( + reply []byte + cancelFn func() bool + ) + delivered := make(chan struct{}) + if peer != nil { + // remote call to a connected peer through LES + if api.clientHandler == nil { + return nil, errors.New("client handler not available") + } + cancelFn = api.clientHandler.makeLespayCall(peer, [][]byte{cmd}, func(replies [][]byte) bool { + if len(replies) == 1 { + reply = replies[0] + } + close(delivered) + return reply != nil + }) + } else { + // remote call through UDP TALK + if api.dht == nil { + return nil, errors.New("UDP DHT not available") + } + cancelFn = api.dht.SendTalkRequest(node, "lespay", [][]byte{cmd}, func(payload interface{}) bool { + if replies, ok := payload.([][]byte); ok && len(replies) == 1 { + reply = replies[0] + } + close(delivered) + return reply != nil + }) + } + select { + case <-ctx.Done(): + cancelFn() + return nil, ctx.Err() + case <-delivered: + return reply, nil + } + } else { + if api.tokenSale == nil { + return nil, errors.New("token sale module not available") + } + // execute call locally + return api.tokenSale.runCommand(cmd, id, freeID), nil + } + +} + +func (api *PrivateLespayAPI) Connection(ctx context.Context, remote bool, node string, requestedCapacity, stayConnected uint64, paymentModule []string, setCap bool) (results tsConnectionResults, err error) { + params := tsConnectionParams{requestedCapacity, stayConnected, paymentModule, setCap} + enc, _ := rlp.EncodeToBytes(¶ms) + var resEnc []byte + resEnc, err = api.makeCall(ctx, remote, node, enc) + if err != nil { + return + } + err = rlp.DecodeBytes(resEnc, &results) + return +} diff --git a/les/client.go b/les/client.go index c460f4c09d..26c5175bcf 100644 --- a/les/client.go +++ b/les/client.go @@ -48,6 +48,7 @@ import ( type LightEthereum struct { lesCommons + srvr *p2p.Server reqDist *requestDistributor retriever *retrieveManager odr *LesOdr @@ -206,6 +207,12 @@ func (s *LightEthereum) APIs() []rpc.API { Service: NewPrivateLightAPI(&s.lesCommons), Public: false, }, + { + Namespace: "lespay", + Version: "1.0", + Service: NewPrivateLespayAPI(s.lesCommons.peers, s.handler, s.srvr.DiscV5, nil), + Public: false, + }, }...) } @@ -235,6 +242,7 @@ func (s *LightEthereum) Protocols() []p2p.Protocol { // light ethereum protocol implementation. func (s *LightEthereum) Start(srvr *p2p.Server) error { log.Warn("Light client mode is an experimental feature") + s.srvr = srvr // Start bloom request workers. s.wg.Add(bloomServiceThreads) diff --git a/les/client_handler.go b/les/client_handler.go index e1f36078b1..20d19363c2 100644 --- a/les/client_handler.go +++ b/les/client_handler.go @@ -352,7 +352,7 @@ func (h *clientHandler) handleMsg(p *peer) error { return nil } -func (h *clientHandler) sendLespayCommands(p *peer, cmds [][]byte, handler func([][]byte) bool) func() bool { +func (h *clientHandler) makeLespayCall(p *peer, cmds [][]byte, handler func([][]byte) bool) func() bool { reqID := genReqID() if p.SendLespay(reqID, cmds) != nil { return nil diff --git a/les/server.go b/les/server.go index 636bedd922..706c7506ac 100644 --- a/les/server.go +++ b/les/server.go @@ -149,6 +149,12 @@ func (s *LesServer) APIs() []rpc.API { Service: NewPrivateDebugAPI(s), Public: false, }, + { + Namespace: "lespay", + Version: "1.0", + Service: NewPrivateLespayAPI(s.lesCommons.peers, nil, s.srvr.DiscV5, s.tokenSale), + Public: false, + }, } } diff --git a/les/server_handler.go b/les/server_handler.go index 4f22b85e3b..954cbae2d9 100644 --- a/les/server_handler.go +++ b/les/server_handler.go @@ -981,9 +981,9 @@ func (h *serverHandler) broadcastHeaders() { } } -func (h *serverHandler) talkRequestHandler(id enode.ID, addr *net.UDPAddr, payload []byte) ([]byte, bool) { - var cmds [][]byte - if err := rlp.DecodeBytes(payload, &cmds); err != nil { +func (h *serverHandler) talkRequestHandler(id enode.ID, addr *net.UDPAddr, payload interface{}) (interface{}, bool) { + cmds, ok := payload.([][]byte) + if !ok { return nil, false } results := h.server.tokenSale.runCommands(cmds, id, addr.IP.String()) diff --git a/les/tokensale.go b/les/tokensale.go index 209f3a6fa2..9d785fe41e 100644 --- a/les/tokensale.go +++ b/les/tokensale.go @@ -319,11 +319,6 @@ const ( tsConnection ) -/*func (t *tokenSale) connection(id enode.ID, freeID string, requestedCapacity uint64, stayConnected time.Duration, paymentModule []string, setCap bool) (availableCapacity, tokenBalance, tokensMissing, pcBalance, pcMissing uint64, paymentRequired []uint64, err error) { -func (t *tokenSale) deposit(id enode.ID, paymentModule string, proofOfPayment []byte) (pcValue, pcBalance uint64, err error) { -func (t *tokenSale) buyTokens(id enode.ID, maxSpend, minReceive uint64, spendAll bool) (pcBalance, tokenBalance, spend, receive uint64, success bool) { -func (t *tokenSale) paymentInfo(paymentModule []string) map[string][]byte {*/ - type ( tsDepositParams struct { PaymentModule string diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go index d020c48adf..370a7237fc 100644 --- a/p2p/discv5/net.go +++ b/p2p/discv5/net.go @@ -87,8 +87,8 @@ type Network struct { } type ( - TalkRequestHandler func(enode.ID, *net.UDPAddr, []byte) ([]byte, bool) - TalkResponseHandler func([]byte) bool + TalkRequestHandler func(enode.ID, *net.UDPAddr, interface{}) (interface{}, bool) + TalkResponseHandler func(interface{}) bool ) // transport is implemented by the UDP transport. @@ -116,7 +116,7 @@ type findnodeQuery struct { type talkQuery struct { remote *Node talkID string - payload rlp.RawValue + payload interface{} key string handler TalkResponseHandler } @@ -1387,7 +1387,7 @@ func (q *talkQuery) deferQuery() { q.remote.deferQuery(q) } -func (net *Network) SendTalkRequest(to *enode.Node, talkID string, payload []byte, handler TalkResponseHandler) func() bool { +func (net *Network) SendTalkRequest(to *enode.Node, talkID string, payload interface{}, handler TalkResponseHandler) func() bool { var nodeID NodeID copy(nodeID[:], crypto.FromECDSAPub(to.Pubkey())[1:]) node := net.nodes[nodeID] diff --git a/p2p/discv5/udp.go b/p2p/discv5/udp.go index 4f630dab1f..0526e6b566 100644 --- a/p2p/discv5/udp.go +++ b/p2p/discv5/udp.go @@ -120,11 +120,13 @@ type ( } talkRequest struct { - TalkID, Payload []byte + TalkID []byte + Payload interface{} } talkResponse struct { - ReplyTok, Payload []byte + ReplyTok []byte + Payload interface{} } rpcNode struct {