added shh API

This commit is contained in:
Bas van Kervel 2015-06-10 12:35:12 +02:00 committed by Bas van Kervel
parent bd38428f33
commit 7e41d7ac51
6 changed files with 370 additions and 5 deletions

@ -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,
}, ",")
)

171
rpc/api/shh.go Normal file

@ -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
}

158
rpc/api/shh_args.go Normal file

@ -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
}

30
rpc/api/ssh_js.go Normal file

@ -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
})
]
});
`

@ -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) {

@ -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