aboutsummaryrefslogtreecommitdiffstats
path: root/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'rpc')
-rw-r--r--rpc/api/api.go6
-rw-r--r--rpc/api/shh.go171
-rw-r--r--rpc/api/shh_args.go158
-rw-r--r--rpc/api/ssh_js.go30
-rw-r--r--rpc/api/txpool.go2
-rw-r--r--rpc/api/utils.go8
6 files changed, 370 insertions, 5 deletions
diff --git a/rpc/api/api.go b/rpc/api/api.go
index e870ec58e..206647946 100644
--- a/rpc/api/api.go
+++ b/rpc/api/api.go
@@ -13,7 +13,8 @@ const (
MergedApiName = "merged"
MinerApiName = "miner"
NetApiName = "net"
- txPoolApiName = "txpool"
+ ShhApiName = "shh"
+ TxPoolApiName = "txpool"
PersonalApiName = "personal"
Web3ApiName = "web3"
)
@@ -21,7 +22,8 @@ const (
var (
// List with all API's which are offered over the IPC interface by default
DefaultIpcApis = strings.Join([]string{
- AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, txPoolApiName, PersonalApiName, Web3ApiName,
+ AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
+ ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
}, ",")
)
diff --git a/rpc/api/shh.go b/rpc/api/shh.go
new file mode 100644
index 000000000..04c53c93e
--- /dev/null
+++ b/rpc/api/shh.go
@@ -0,0 +1,171 @@
+package api
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+ "github.com/ethereum/go-ethereum/xeth"
+)
+
+var (
+ // mapping between methods and handlers
+ shhMapping = map[string]shhhandler{
+ "shh_version": (*shhApi).Version,
+ "shh_post": (*shhApi).Post,
+ "shh_hasIdentity": (*shhApi).HasIdentity,
+ "shh_newIdentity": (*shhApi).NewIdentity,
+ "shh_newFilter": (*shhApi).NewFilter,
+ "shh_uninstallFilter": (*shhApi).UninstallFilter,
+ "shh_getFilterChanges": (*shhApi).GetFilterChanges,
+ }
+)
+
+func newWhisperOfflineError(method string) error {
+ return shared.NewNotAvailableError(method, "whisper offline")
+}
+
+// net callback handler
+type shhhandler func(*shhApi, *shared.Request) (interface{}, error)
+
+// shh api provider
+type shhApi struct {
+ xeth *xeth.XEth
+ ethereum *eth.Ethereum
+ methods map[string]shhhandler
+ codec codec.ApiCoder
+}
+
+// create a new whisper api instance
+func NewShhApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *shhApi {
+ return &shhApi{
+ xeth: xeth,
+ ethereum: eth,
+ methods: shhMapping,
+ codec: coder.New(nil),
+ }
+}
+
+// collection with supported methods
+func (self *shhApi) Methods() []string {
+ methods := make([]string, len(self.methods))
+ i := 0
+ for k := range self.methods {
+ methods[i] = k
+ i++
+ }
+ return methods
+}
+
+// Execute given request
+func (self *shhApi) Execute(req *shared.Request) (interface{}, error) {
+ if callback, ok := self.methods[req.Method]; ok {
+ return callback(self, req)
+ }
+
+ return nil, shared.NewNotImplementedError(req.Method)
+}
+
+func (self *shhApi) Name() string {
+ return ShhApiName
+}
+
+func (self *shhApi) Version(req *shared.Request) (interface{}, error) {
+ w := self.xeth.Whisper()
+ if w == nil {
+ return nil, newWhisperOfflineError(req.Method)
+ }
+
+ return w.Version(), nil
+}
+
+func (self *shhApi) Post(req *shared.Request) (interface{}, error) {
+ w := self.xeth.Whisper()
+ if w == nil {
+ return nil, newWhisperOfflineError(req.Method)
+ }
+
+ args := new(WhisperMessageArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, err
+ }
+
+ err := w.Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
+ if err != nil {
+ return false, err
+ }
+
+ return true, nil
+}
+
+func (self *shhApi) HasIdentity(req *shared.Request) (interface{}, error) {
+ w := self.xeth.Whisper()
+ if w == nil {
+ return nil, newWhisperOfflineError(req.Method)
+ }
+
+ args := new(WhisperIdentityArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, err
+ }
+
+ return w.HasIdentity(args.Identity), nil
+}
+
+func (self *shhApi) NewIdentity(req *shared.Request) (interface{}, error) {
+ w := self.xeth.Whisper()
+ if w == nil {
+ return nil, newWhisperOfflineError(req.Method)
+ }
+
+ return w.NewIdentity(), nil
+}
+
+func (self *shhApi) NewFilter(req *shared.Request) (interface{}, error) {
+ args := new(WhisperFilterArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, err
+ }
+
+ id := self.xeth.NewWhisperFilter(args.To, args.From, args.Topics)
+ return newHexNum(big.NewInt(int64(id)).Bytes()), nil
+}
+
+func (self *shhApi) UninstallFilter(req *shared.Request) (interface{}, error) {
+ args := new(FilterIdArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, err
+ }
+ return self.xeth.UninstallWhisperFilter(args.Id), nil
+}
+
+func (self *shhApi) GetFilterChanges(req *shared.Request) (interface{}, error) {
+ w := self.xeth.Whisper()
+ if w == nil {
+ return nil, newWhisperOfflineError(req.Method)
+ }
+
+ // Retrieve all the new messages arrived since the last request
+ args := new(FilterIdArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, err
+ }
+
+ return self.xeth.WhisperMessagesChanged(args.Id), nil
+}
+
+func (self *shhApi) GetMessages(req *shared.Request) (interface{}, error) {
+ w := self.xeth.Whisper()
+ if w == nil {
+ return nil, newWhisperOfflineError(req.Method)
+ }
+
+ // Retrieve all the cached messages matching a specific, existing filter
+ args := new(FilterIdArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, err
+ }
+
+ return self.xeth.WhisperMessages(args.Id), nil
+}
diff --git a/rpc/api/shh_args.go b/rpc/api/shh_args.go
new file mode 100644
index 000000000..00abac232
--- /dev/null
+++ b/rpc/api/shh_args.go
@@ -0,0 +1,158 @@
+package api
+
+import (
+ "encoding/json"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+type WhisperMessageArgs struct {
+ Payload string
+ To string
+ From string
+ Topics []string
+ Priority uint32
+ Ttl uint32
+}
+
+func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []struct {
+ Payload string
+ To string
+ From string
+ Topics []string
+ Priority interface{}
+ Ttl interface{}
+ }
+
+ if err = json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ if len(obj) < 1 {
+ return shared.NewInsufficientParamsError(len(obj), 1)
+ }
+ args.Payload = obj[0].Payload
+ args.To = obj[0].To
+ args.From = obj[0].From
+ args.Topics = obj[0].Topics
+
+ var num *big.Int
+ if num, err = numString(obj[0].Priority); err != nil {
+ return err
+ }
+ args.Priority = uint32(num.Int64())
+
+ if num, err = numString(obj[0].Ttl); err != nil {
+ return err
+ }
+ args.Ttl = uint32(num.Int64())
+
+ return nil
+}
+
+type WhisperIdentityArgs struct {
+ Identity string
+}
+
+func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []interface{}
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ if len(obj) < 1 {
+ return shared.NewInsufficientParamsError(len(obj), 1)
+ }
+
+ argstr, ok := obj[0].(string)
+ if !ok {
+ return shared.NewInvalidTypeError("arg0", "not a string")
+ }
+
+ args.Identity = argstr
+
+ return nil
+}
+
+type WhisperFilterArgs struct {
+ To string
+ From string
+ Topics [][]string
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
+// JSON message blob into a WhisperFilterArgs structure.
+func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
+ // Unmarshal the JSON message and sanity check
+ var obj []struct {
+ To interface{} `json:"to"`
+ From interface{} `json:"from"`
+ Topics interface{} `json:"topics"`
+ }
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+ if len(obj) < 1 {
+ return shared.NewInsufficientParamsError(len(obj), 1)
+ }
+ // Retrieve the simple data contents of the filter arguments
+ if obj[0].To == nil {
+ args.To = ""
+ } else {
+ argstr, ok := obj[0].To.(string)
+ if !ok {
+ return shared.NewInvalidTypeError("to", "is not a string")
+ }
+ args.To = argstr
+ }
+ if obj[0].From == nil {
+ args.From = ""
+ } else {
+ argstr, ok := obj[0].From.(string)
+ if !ok {
+ return shared.NewInvalidTypeError("from", "is not a string")
+ }
+ args.From = argstr
+ }
+ // Construct the nested topic array
+ if obj[0].Topics != nil {
+ // Make sure we have an actual topic array
+ list, ok := obj[0].Topics.([]interface{})
+ if !ok {
+ return shared.NewInvalidTypeError("topics", "is not an array")
+ }
+ // Iterate over each topic and handle nil, string or array
+ topics := make([][]string, len(list))
+ for idx, field := range list {
+ switch value := field.(type) {
+ case nil:
+ topics[idx] = []string{}
+
+ case string:
+ topics[idx] = []string{value}
+
+ case []interface{}:
+ topics[idx] = make([]string, len(value))
+ for i, nested := range value {
+ switch value := nested.(type) {
+ case nil:
+ topics[idx][i] = ""
+
+ case string:
+ topics[idx][i] = value
+
+ default:
+ return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string")
+ }
+ }
+ default:
+ return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array")
+ }
+ }
+ args.Topics = topics
+ }
+ return nil
+}
diff --git a/rpc/api/ssh_js.go b/rpc/api/ssh_js.go
new file mode 100644
index 000000000..f401f70f1
--- /dev/null
+++ b/rpc/api/ssh_js.go
@@ -0,0 +1,30 @@
+package api
+
+const Shh_JS = `
+web3._extend({
+ property: 'shh',
+ methods:
+ [
+ new web3._extend.Method({
+ name: 'post',
+ call: 'shh_post',
+ params: 6,
+ inputFormatter: [web3._extend.formatters.formatInputString,
+ web3._extend.formatters.formatInputString,
+ web3._extend.formatters.formatInputString,
+ ,
+ , web3._extend.formatters.formatInputInt
+ , web3._extend.formatters.formatInputInt],
+ outputFormatter: web3._extend.formatters.formatOutputBool
+ }),
+ ],
+ properties:
+ [
+ new web3._extend.Property({
+ name: 'version',
+ getter: 'shh_version',
+ outputFormatter: web3._extend.formatters.formatOutputInt
+ })
+ ]
+});
+`
diff --git a/rpc/api/txpool.go b/rpc/api/txpool.go
index f340c501f..ebbe199b1 100644
--- a/rpc/api/txpool.go
+++ b/rpc/api/txpool.go
@@ -56,7 +56,7 @@ func (self *txPoolApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *txPoolApi) Name() string {
- return txPoolApiName
+ return TxPoolApiName
}
func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) {
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
index b44a325a8..ad8a97e92 100644
--- a/rpc/api/utils.go
+++ b/rpc/api/utils.go
@@ -31,7 +31,9 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
apis[i] = NewMinerApi(eth, codec)
case NetApiName:
apis[i] = NewNetApi(xeth, eth, codec)
- case txPoolApiName:
+ case ShhApiName:
+ apis[i] = NewShhApi(xeth, eth, codec)
+ case TxPoolApiName:
apis[i] = NewTxPoolApi(xeth, eth, codec)
case PersonalApiName:
apis[i] = NewPersonalApi(xeth, eth, codec)
@@ -55,7 +57,9 @@ func Javascript(name string) string {
return Miner_JS
case NetApiName:
return Net_JS
- case txPoolApiName:
+ case ShhApiName:
+ return Shh_JS
+ case TxPoolApiName:
return TxPool_JS
case PersonalApiName:
return Personal_JS