new pinger
This commit is contained in:
parent
90950ff330
commit
66ce64d868
|
@ -7,12 +7,6 @@
|
|||
packages = ["."]
|
||||
revision = "94b504f42fdd503acc3b3c79ec2b517d90e0de8a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/NaySoftware/go-fcm"
|
||||
packages = ["."]
|
||||
revision = "28fff9381d17f35619309c7a5ada41d26030d976"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/StackExchange/wmi"
|
||||
packages = ["."]
|
||||
|
@ -50,6 +44,12 @@
|
|||
]
|
||||
revision = "dcd4997b0664bcfd6ef48e4ae9da8396e08b1cd9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/dustin/go-humanize"
|
||||
|
@ -97,6 +97,8 @@
|
|||
"core/vm",
|
||||
"crypto",
|
||||
"crypto/bn256",
|
||||
"crypto/bn256/cloudflare",
|
||||
"crypto/bn256/google",
|
||||
"crypto/ecies",
|
||||
"crypto/randentropy",
|
||||
"crypto/secp256k1",
|
||||
|
@ -120,6 +122,7 @@
|
|||
"log",
|
||||
"log/term",
|
||||
"metrics",
|
||||
"metrics/exp",
|
||||
"miner",
|
||||
"node",
|
||||
"p2p",
|
||||
|
@ -132,11 +135,10 @@
|
|||
"rpc",
|
||||
"trie",
|
||||
"whisper/mailserver",
|
||||
"whisper/whisperv5",
|
||||
"whisper/whisperv6"
|
||||
]
|
||||
revision = "1e67410e88d2685bc54611a7c9f75c327b553ccc"
|
||||
version = "v1.8.1"
|
||||
revision = "2423ae01e0d2f853512eb2b46954b5ad0754a897"
|
||||
version = "v1.8.4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -330,35 +332,12 @@
|
|||
revision = "85f23d82a045d103ea7f3c89a91fba4a93e6367a"
|
||||
version = "v2.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/rcrowley/go-metrics"
|
||||
packages = [
|
||||
".",
|
||||
"exp"
|
||||
]
|
||||
revision = "8732c616f52954686704c8645fe1a9d59e9df7c1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/rjeczalik/notify"
|
||||
packages = ["."]
|
||||
revision = "c31e5f2cb22b3e4ef3f882f413847669bf2652b9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/robertkrimen/otto"
|
||||
packages = [
|
||||
".",
|
||||
"ast",
|
||||
"dbg",
|
||||
"file",
|
||||
"parser",
|
||||
"registry",
|
||||
"token"
|
||||
]
|
||||
revision = "3b44b4dcb6c00477273595c312908e2412d07da6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/rs/cors"
|
||||
packages = ["."]
|
||||
|
@ -366,21 +345,20 @@
|
|||
version = "v1.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "feature/whisperv6-#665"
|
||||
branch = "sdk"
|
||||
name = "github.com/status-im/status-go"
|
||||
packages = [
|
||||
"extkeys",
|
||||
"geth/account",
|
||||
"geth/common",
|
||||
"geth/log",
|
||||
"geth/mailservice",
|
||||
"geth/node",
|
||||
"geth/params",
|
||||
"geth/rpc",
|
||||
"metrics/whisper",
|
||||
"sdk",
|
||||
"static"
|
||||
]
|
||||
revision = "fb38e6c58ffb12b60bfa6457a52ff715f7d0ba32"
|
||||
revision = "7af2d4cf36b09d101441cf430cdfeb2f740c3289"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -506,15 +484,6 @@
|
|||
packages = ["."]
|
||||
revision = "3c4db4ad4f2db84859454dc805d6eb7d8051a8ce"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/sourcemap.v1"
|
||||
packages = [
|
||||
".",
|
||||
"base64vlq"
|
||||
]
|
||||
revision = "6e83acea0053641eff084973fee085f0c193c61a"
|
||||
version = "v1.0.5"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/urfave/cli.v1"
|
||||
packages = ["."]
|
||||
|
@ -530,6 +499,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "78852882201dc929aa7a558c7d27994e20255f191c855f50ebc28901dd6aaa25"
|
||||
inputs-digest = "6f08bccddf499165736ebc9a3927b8e71c22cf6a606b0bba5a68b91b2157a972"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -16,7 +16,7 @@ ignored = ["github.com/ethereum/go-ethereum/whisper/notifications"]
|
|||
|
||||
[[constraint]]
|
||||
name = "github.com/status-im/status-go"
|
||||
branch = "feature/whisperv6-#665"
|
||||
branch = "sdk"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ethereum/go-ethereum"
|
||||
|
|
1
Makefile
1
Makefile
|
@ -6,4 +6,5 @@ docker-image:
|
|||
allbots:
|
||||
go build -i -o ./pinger -v ./cmd/pinger
|
||||
go build -i -o ./chanreader -v ./cmd/chanreader
|
||||
go build -i -o ./pingerweb -v ./cmd/pingerweb
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ FROM alpine:latest
|
|||
RUN apk add --no-cache ca-certificates bash
|
||||
|
||||
COPY --from=builder /go/src/github.com/mandrigin/status-go-bots/pinger /usr/local/bin/
|
||||
COPY --from=builder /go/src/github.com/mandrigin/status-go-bots/pingerweb /usr/local/bin/
|
||||
COPY --from=builder /go/src/github.com/mandrigin/status-go-bots/chanreader /usr/local/bin/
|
||||
|
||||
WORKDIR /etc/gs-bots/
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package bots
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/mkideal/cli"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
cli.Helper
|
||||
Channel string `cli:"channel" usage:"Channel that bot listens to" dft:"humans-need-not-apply"`
|
||||
Nickname string `cli:"nickname" usage:"Nickname of the bot" dft:"generic bot"`
|
||||
}
|
||||
|
||||
func (c Config) Password() string {
|
||||
return os.Getenv("STATUS_BOT_PASSWORD")
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package bots
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
sn "github.com/status-im/status-go/geth/node"
|
||||
)
|
||||
|
||||
func Quickstart(conf Config, repeat time.Duration, f func(ch *StatusChannel)) *node.Node {
|
||||
config, err := NodeConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("Making config failed: %v", err)
|
||||
}
|
||||
|
||||
nodeManager := sn.NewNodeManager()
|
||||
log.Println("Starting node...")
|
||||
err = nodeManager.StartNode(config)
|
||||
if err != nil {
|
||||
log.Fatalf("Node start failed: %v", err)
|
||||
}
|
||||
|
||||
node, err := nodeManager.Node()
|
||||
if err != nil {
|
||||
log.Fatalf("Getting node failed: %v", err)
|
||||
}
|
||||
|
||||
SignupOrLogin(nodeManager, conf.Password()).Join(conf.Channel, conf.Nickname).RepeatEvery(repeat, f)
|
||||
|
||||
return node
|
||||
}
|
167
bots/utils.go
167
bots/utils.go
|
@ -1,167 +0,0 @@
|
|||
package bots
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
type apiHolder struct {
|
||||
api *node.NodeManager
|
||||
}
|
||||
|
||||
func (a *apiHolder) API() *node.NodeManager {
|
||||
return a.api
|
||||
}
|
||||
|
||||
func (a *apiHolder) CallRPC(command string) string {
|
||||
return a.api.RPCClient().CallRaw(command)
|
||||
}
|
||||
|
||||
func (a *apiHolder) CreateAccount(password string) (address, pubKey, mnemonic string, err error) {
|
||||
am := account.NewManager(a.api)
|
||||
return am.CreateAccount(password)
|
||||
}
|
||||
|
||||
func (a *apiHolder) SelectAccount(address, password string) error {
|
||||
am := account.NewManager(a.api)
|
||||
return am.SelectAccount(address, password)
|
||||
}
|
||||
|
||||
type StatusChannel struct {
|
||||
apiHolder
|
||||
ChannelName string
|
||||
UserName string
|
||||
FilterID string
|
||||
AccountAddress string
|
||||
ChannelKey string
|
||||
}
|
||||
|
||||
func (ch *StatusChannel) RepeatEvery(ti time.Duration, f func(ch *StatusChannel)) {
|
||||
go func() {
|
||||
for {
|
||||
f(ch)
|
||||
time.Sleep(ti)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (ch *StatusChannel) ReadMessages() (result []StatusMessage) {
|
||||
cmd := `{"jsonrpc":"2.0","id":2968,"method":"shh_getFilterMessages","params":["%s"]}`
|
||||
f := unmarshalJSON(ch.CallRPC(fmt.Sprintf(cmd, ch.FilterID)))
|
||||
v := f.(map[string]interface{})["result"]
|
||||
switch vv := v.(type) {
|
||||
case []interface{}:
|
||||
for _, u := range vv {
|
||||
payload := u.(map[string]interface{})["payload"]
|
||||
message := MessageFromPayload(payload.(string))
|
||||
if len(message.ID) > 0 {
|
||||
result = append(result, message)
|
||||
}
|
||||
}
|
||||
default:
|
||||
log.Println(v, "is of a type I don't know how to handle")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (ch *StatusChannel) SendMessage(text string) {
|
||||
cmd := `{"jsonrpc":"2.0","id":0,"method":"shh_post","params":[{"from":"%s","topic":"0xaabb11ee","payload":"%s","symKeyID":"%s","sym-key-password":"status","ttl":2400,"powTarget":0.001,"powTime":1}]}`
|
||||
|
||||
message := NewStatusMessage(ch.UserName, text, ch.ChannelName)
|
||||
|
||||
cmd = fmt.Sprintf(cmd, ch.AccountAddress, message.ToPayload(), ch.ChannelKey)
|
||||
log.Println("-> SENT:", ch.CallRPC(cmd))
|
||||
}
|
||||
|
||||
type StatusSession struct {
|
||||
apiHolder
|
||||
Address string
|
||||
}
|
||||
|
||||
func (s *StatusSession) Join(channelName, username string) *StatusChannel {
|
||||
|
||||
cmd := fmt.Sprintf(`{"jsonrpc":"2.0","id":2950,"method":"shh_generateSymKeyFromPassword","params":["%s"]}`, channelName)
|
||||
|
||||
f := unmarshalJSON(s.CallRPC(cmd))
|
||||
|
||||
key := f.(map[string]interface{})["result"]
|
||||
|
||||
cmd = `{"jsonrpc":"2.0","id":2,"method":"shh_newMessageFilter","params":[{"allowP2P":true,"topics":["0xaabb11ee"],"type":"sym","symKeyID":"%s"}]}`
|
||||
|
||||
f = unmarshalJSON(s.CallRPC(fmt.Sprintf(cmd, key)))
|
||||
|
||||
filterID := f.(map[string]interface{})["result"]
|
||||
|
||||
return &StatusChannel{
|
||||
apiHolder: apiHolder{s.API()},
|
||||
ChannelName: channelName,
|
||||
UserName: username,
|
||||
FilterID: filterID.(string),
|
||||
AccountAddress: s.Address,
|
||||
ChannelKey: key.(string),
|
||||
}
|
||||
}
|
||||
|
||||
func SignupOrLogin(nodeManager *node.NodeManager, password string) *StatusSession {
|
||||
cwd, _ := os.Getwd()
|
||||
db, err := leveldb.OpenFile(cwd+"/data", nil)
|
||||
if err != nil {
|
||||
log.Fatal("can't open levelDB file. ERR: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
accountAddress := getAccountAddress(db)
|
||||
|
||||
api := apiHolder{nodeManager}
|
||||
|
||||
if accountAddress == "" {
|
||||
address, _, _, err := api.CreateAccount(password)
|
||||
if err != nil {
|
||||
log.Fatalf("could not create an account. ERR: %v", err)
|
||||
}
|
||||
saveAccountAddress(address, db)
|
||||
accountAddress = address
|
||||
}
|
||||
|
||||
err = api.SelectAccount(accountAddress, password)
|
||||
log.Println("Logged in as", accountAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to select account. ERR: %+v", err)
|
||||
}
|
||||
log.Println("Selected account succesfully")
|
||||
|
||||
return &StatusSession{
|
||||
apiHolder: api,
|
||||
Address: accountAddress,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
KEY_ADDRESS = "hnny.address"
|
||||
)
|
||||
|
||||
func getAccountAddress(db *leveldb.DB) string {
|
||||
addressBytes, err := db.Get([]byte(KEY_ADDRESS), nil)
|
||||
if err != nil {
|
||||
log.Printf("Error while getting address: %v", err)
|
||||
return ""
|
||||
}
|
||||
return string(addressBytes)
|
||||
}
|
||||
|
||||
func saveAccountAddress(address string, db *leveldb.DB) {
|
||||
db.Put([]byte(KEY_ADDRESS), []byte(address), nil)
|
||||
}
|
||||
|
||||
func unmarshalJSON(j string) interface{} {
|
||||
var v interface{}
|
||||
json.Unmarshal([]byte(j), &v)
|
||||
return v
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
package bots
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
const (
|
||||
messageRegexString = `{:message-id "(?P<ID>.+)",\s+:group-id "(?P<GroupID>.+)",\s+:content "(?P<Content>.+)",\s+:username ["]?(?P<Username>.+)["]?,\s+:type :public-group-message.+:timestamp (?P<Timestamp>\d+)}`
|
||||
)
|
||||
|
||||
var messageRegex *regexp.Regexp = regexp.MustCompile(messageRegexString)
|
||||
|
||||
type StatusMessage struct {
|
||||
ID string `json:"id"`
|
||||
From string `json:"from"`
|
||||
Text string `json:"text"`
|
||||
ChannelName string `json:"channel"`
|
||||
Timestamp int64 `json:"ts"`
|
||||
Raw string `json:"-"`
|
||||
}
|
||||
|
||||
func NewStatusMessage(from, text, channel string) StatusMessage {
|
||||
return StatusMessage{
|
||||
ID: newUUID(),
|
||||
From: from,
|
||||
Text: text,
|
||||
ChannelName: channel,
|
||||
Timestamp: time.Now().Unix() * 1000,
|
||||
}
|
||||
}
|
||||
|
||||
func (m StatusMessage) TimeString() string {
|
||||
t := time.Unix(m.Timestamp/1000, 0)
|
||||
return humanize.RelTime(t, time.Now(), "earlier", "later")
|
||||
}
|
||||
|
||||
func (m StatusMessage) ToPayload() string {
|
||||
payloadFormat := `{:message-id "%s", :group-id "%s", :content "%s", :username "%s", :type :public-group-message, :show? true, :clock-value 1, :requires-ack? false, :content-type "text/plain", :timestamp %d}`
|
||||
message := fmt.Sprintf(payloadFormat,
|
||||
m.ID,
|
||||
m.ChannelName,
|
||||
m.Text,
|
||||
m.From,
|
||||
m.Timestamp)
|
||||
|
||||
return rawrChatMessage(message)
|
||||
}
|
||||
|
||||
func MessageFromPayload(payload string) StatusMessage {
|
||||
message := unrawrChatMessage(payload)
|
||||
|
||||
r := messageRegex.FindStringSubmatch(message)
|
||||
|
||||
if len(r) < len(messageRegex.SubexpNames()) {
|
||||
log.Println("Could not unwrap message: ", message)
|
||||
return StatusMessage{}
|
||||
}
|
||||
|
||||
dict := make(map[string]string)
|
||||
for idx, name := range messageRegex.SubexpNames() {
|
||||
if len(name) > 0 {
|
||||
dict[name] = r[idx]
|
||||
}
|
||||
}
|
||||
|
||||
timestamp, _ := strconv.Atoi(dict["Timestamp"])
|
||||
|
||||
return StatusMessage{
|
||||
ID: dict["ID"],
|
||||
From: dict["Username"],
|
||||
Text: dict["Content"],
|
||||
ChannelName: dict["GroupID"],
|
||||
Timestamp: int64(timestamp),
|
||||
Raw: message,
|
||||
}
|
||||
}
|
||||
|
||||
func rawrChatMessage(raw string) string {
|
||||
bytes := []byte(raw)
|
||||
return fmt.Sprintf("0x%s", hex.EncodeToString(bytes))
|
||||
}
|
||||
|
||||
func unrawrChatMessage(message string) string {
|
||||
bytes, err := hex.DecodeString(message[2:])
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// newUUID generates a random UUID according to RFC 4122
|
||||
func newUUID() string {
|
||||
uuid := make([]byte, 16)
|
||||
n, err := io.ReadFull(rand.Reader, uuid)
|
||||
if n != len(uuid) || err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// variant bits; see section 4.1.1
|
||||
uuid[8] = uuid[8]&^0xc0 | 0x80
|
||||
// version 4 (pseudo-random); see section 4.1.3
|
||||
uuid[6] = uuid[6]&^0xf0 | 0x40
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
|
||||
}
|
|
@ -3,44 +3,59 @@ package main
|
|||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mkideal/cli"
|
||||
|
||||
"github.com/mandrigin/status-go-bots/bots"
|
||||
"github.com/status-im/status-go/sdk"
|
||||
)
|
||||
|
||||
type argT struct {
|
||||
cli.Helper
|
||||
Username string `cli:"username" usage:"Username of the bot account" dft:"the-spectator"`
|
||||
Password string `cli:"password" usage:"Password of the bot account" dft:"the-spectator-pwd"`
|
||||
Channel string `cli:"channel" usage:"Channel that bot listens to" dft:"humans-need-not-apply"`
|
||||
Interval int `cli:"interval" usage:"Send message every x second" dft:"5"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cli.Run(&bots.Config{}, func(ctx *cli.Context) error {
|
||||
conf := ctx.Argv().(*bots.Config)
|
||||
cli.Run(&argT{}, func(ctx *cli.Context) error {
|
||||
conf := ctx.Argv().(*argT)
|
||||
|
||||
messages := NewMessagesStore(1000)
|
||||
defer messages.Close()
|
||||
|
||||
log.Println("conf: ", conf)
|
||||
conn, err := sdk.Connect(conf.Username, conf.Password)
|
||||
if err != nil {
|
||||
panic("Couldn't connect to status")
|
||||
}
|
||||
|
||||
node := bots.Quickstart(*conf, 100*time.Millisecond, func(ch *bots.StatusChannel) {
|
||||
for _, msg := range ch.ReadMessages() {
|
||||
if err := messages.Add(msg); err != nil {
|
||||
ch, err := conn.Join(conf.Channel)
|
||||
if err != nil {
|
||||
panic("Couldn't connect to status")
|
||||
}
|
||||
|
||||
ch.Subscribe(func(msg *sdk.Msg) {
|
||||
log.Println("received a message", msg)
|
||||
if msg != nil {
|
||||
if err := messages.Add(*msg); err != nil {
|
||||
log.Printf("Error while storing message: ERR: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Println("received a nil message!")
|
||||
}
|
||||
})
|
||||
|
||||
log.Println("Node started, %v", node)
|
||||
|
||||
r := gin.Default()
|
||||
r.LoadHTMLGlob("_assets/html/*")
|
||||
r.GET("/json", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"messages": messages.Messages(conf.Channel),
|
||||
"messages": messages.Messages(),
|
||||
})
|
||||
})
|
||||
r.GET("/html", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
||||
"ChannelName": conf.Channel,
|
||||
"Messages": messages.Messages(conf.Channel),
|
||||
"Messages": messages.Messages(),
|
||||
})
|
||||
})
|
||||
r.Run() // listen and serve on 0.0.0.0:8080
|
||||
|
|
|
@ -5,16 +5,27 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/mandrigin/status-go-bots/bots"
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/status-im/status-go/sdk"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
type Msg struct {
|
||||
sdk.Msg
|
||||
}
|
||||
|
||||
func (m Msg) TimeString() string {
|
||||
fmt.Println("m.Timestamp =", m.Timestamp)
|
||||
t := time.Unix(m.Timestamp/int64(time.Millisecond)*10, 0)
|
||||
return humanize.RelTime(t, time.Now(), "earlier", "later")
|
||||
}
|
||||
|
||||
// ByTimestamp implements sort.Interface for []Person based on
|
||||
// the Timestamp field.
|
||||
type ByTimestamp []bots.StatusMessage
|
||||
type ByTimestamp []sdk.Msg
|
||||
|
||||
func (a ByTimestamp) Len() int { return len(a) }
|
||||
func (a ByTimestamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
@ -22,7 +33,7 @@ func (a ByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestam
|
|||
|
||||
type messagesStore struct {
|
||||
db *leveldb.DB
|
||||
messages []bots.StatusMessage
|
||||
messages []sdk.Msg
|
||||
maxCount int
|
||||
}
|
||||
|
||||
|
@ -40,7 +51,7 @@ func NewMessagesStore(maxCount int) *messagesStore {
|
|||
return &messagesStore{db, messages, maxCount}
|
||||
}
|
||||
|
||||
func (ms *messagesStore) Add(message bots.StatusMessage) error {
|
||||
func (ms *messagesStore) Add(message sdk.Msg) error {
|
||||
if len(ms.messages) >= ms.maxCount {
|
||||
if message.Timestamp < ms.messages[0].Timestamp {
|
||||
log.Println("Message is too old, ignoring", message.ID, message.Timestamp)
|
||||
|
@ -52,11 +63,11 @@ func (ms *messagesStore) Add(message bots.StatusMessage) error {
|
|||
log.Println("Message already exists, ignoring", message.ID, message.Timestamp)
|
||||
}
|
||||
|
||||
toRemove := make([]bots.StatusMessage, 0)
|
||||
toRemove := make([]sdk.Msg, 0)
|
||||
fromIdx := 0
|
||||
if len(ms.messages) >= ms.maxCount {
|
||||
fromIdx = len(ms.messages) - ms.maxCount + 1
|
||||
toRemove = make([]bots.StatusMessage, fromIdx)
|
||||
toRemove = make([]sdk.Msg, fromIdx)
|
||||
copy(toRemove, ms.messages[:fromIdx])
|
||||
}
|
||||
messages := append(ms.messages[fromIdx:], message)
|
||||
|
@ -66,17 +77,21 @@ func (ms *messagesStore) Add(message bots.StatusMessage) error {
|
|||
return ms.persist(toRemove, message)
|
||||
}
|
||||
|
||||
func (ms *messagesStore) Messages(channel string) []bots.StatusMessage {
|
||||
messages := messagesFromIterator(ms.db.NewIterator(util.BytesPrefix([]byte(channel)), nil))
|
||||
func (ms *messagesStore) Messages() []Msg {
|
||||
messages := messagesFromIterator(ms.db.NewIterator(nil, nil))
|
||||
sort.Sort(ByTimestamp(messages))
|
||||
return messages
|
||||
result := make([]Msg, len(messages))
|
||||
for idx, msg := range messages {
|
||||
result[idx] = Msg{msg}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (ms *messagesStore) Close() {
|
||||
ms.db.Close()
|
||||
}
|
||||
|
||||
func (ms *messagesStore) persist(toRemove []bots.StatusMessage, toAdd bots.StatusMessage) error {
|
||||
func (ms *messagesStore) persist(toRemove []sdk.Msg, toAdd sdk.Msg) error {
|
||||
batch := new(leveldb.Batch)
|
||||
|
||||
data, err := json.Marshal(toAdd)
|
||||
|
@ -84,6 +99,7 @@ func (ms *messagesStore) persist(toRemove []bots.StatusMessage, toAdd bots.Statu
|
|||
return err
|
||||
}
|
||||
log.Println("Adding: ", toAdd.ID, toAdd.Timestamp, toAdd.ChannelName)
|
||||
log.Printf("Adding: %#v", toAdd)
|
||||
batch.Put([]byte(key(toAdd)), []byte(data))
|
||||
|
||||
for _, messageToRemove := range toRemove {
|
||||
|
@ -100,12 +116,12 @@ func (ms *messagesStore) persist(toRemove []bots.StatusMessage, toAdd bots.Statu
|
|||
|
||||
/* Helper functions */
|
||||
|
||||
func messagesFromIterator(iter iterator.Iterator) []bots.StatusMessage {
|
||||
messages := make([]bots.StatusMessage, 0)
|
||||
func messagesFromIterator(iter iterator.Iterator) []sdk.Msg {
|
||||
messages := make([]sdk.Msg, 0)
|
||||
|
||||
for iter.Next() {
|
||||
// Use key/value.
|
||||
var message bots.StatusMessage
|
||||
var message sdk.Msg
|
||||
if err := json.Unmarshal(iter.Value(), &message); err != nil {
|
||||
log.Println("Cound not unmarshal JSON. ERR: %v", err)
|
||||
} else {
|
||||
|
@ -117,10 +133,6 @@ func messagesFromIterator(iter iterator.Iterator) []bots.StatusMessage {
|
|||
return messages
|
||||
}
|
||||
|
||||
func prefix(message bots.StatusMessage) string {
|
||||
return fmt.Sprintf("%s", message.ChannelName)
|
||||
}
|
||||
|
||||
func key(message bots.StatusMessage) string {
|
||||
return fmt.Sprintf("%s-%s", prefix(message), message.ID)
|
||||
func key(message sdk.Msg) string {
|
||||
return fmt.Sprintf("%s", message.ID())
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// pinger.go
|
||||
// Public chat bot that sends a message every x seconds
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -5,21 +8,42 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/mkideal/cli"
|
||||
|
||||
"github.com/mandrigin/status-go-bots/bots"
|
||||
"github.com/status-im/status-go/sdk"
|
||||
)
|
||||
|
||||
type argT struct {
|
||||
cli.Helper
|
||||
Username string `cli:"username" usage:"Username of the bot account" dft:"pinger"`
|
||||
Password string `cli:"password" usage:"Password of the bot account" dft:"pinger"`
|
||||
Channel string `cli:"channel" usage:"Channel that bot listens to" dft:"humans-need-not-apply"`
|
||||
Interval int `cli:"interval" usage:"Send message every x second" dft:"5"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cli.Run(&bots.Config{}, func(ctx *cli.Context) error {
|
||||
conf := ctx.Argv().(*bots.Config)
|
||||
cli.Run(&argT{}, func(ctx *cli.Context) error {
|
||||
conf := ctx.Argv().(*argT)
|
||||
|
||||
node := bots.Quickstart(*conf, 10*time.Second, func(ch *bots.StatusChannel) {
|
||||
message := fmt.Sprintf("Gopher, gopher: %d", time.Now().Unix())
|
||||
ch.SendMessage(message)
|
||||
})
|
||||
messagesSent := 0
|
||||
|
||||
conn, err := sdk.Connect(conf.Username, conf.Password)
|
||||
if err != nil {
|
||||
panic("Couldn't connect to status")
|
||||
}
|
||||
|
||||
ch, err := conn.Join(conf.Channel)
|
||||
if err != nil {
|
||||
panic("Couldn't connect to status")
|
||||
}
|
||||
|
||||
for range time.Tick(time.Duration(conf.Interval) * time.Second) {
|
||||
messagesSent++
|
||||
message := fmt.Sprintf("PING no %d : %d", messagesSent, time.Now().Unix())
|
||||
ch.Publish(message)
|
||||
fmt.Println("***************************************")
|
||||
fmt.Printf("* SENT: %17d MESSAGES *\n", messagesSent)
|
||||
fmt.Println("***************************************")
|
||||
}
|
||||
|
||||
// wait till node has been stopped
|
||||
node.Wait()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
// pinger.go
|
||||
// Public chat bot that sends a message every x seconds
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mkideal/cli"
|
||||
"github.com/status-im/status-go/sdk"
|
||||
)
|
||||
|
||||
type argT struct {
|
||||
cli.Helper
|
||||
Username string `cli:"username" usage:"Username of the bot account" dft:"pinger"`
|
||||
Password string `cli:"password" usage:"Password of the bot account" dft:"pinger"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cli.Run(&argT{}, func(ctx *cli.Context) error {
|
||||
conf := ctx.Argv().(*argT)
|
||||
|
||||
conn, err := sdk.Connect(conf.Username, conf.Password)
|
||||
if err != nil {
|
||||
panic("Couldn't connect to status")
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
r.GET("/ping/:channel", func(c *gin.Context) {
|
||||
interval := MustParseIntFromQuery(c, "interval")
|
||||
count := MustParseIntFromQuery(c, "count")
|
||||
|
||||
ch, err := conn.Join(c.Param("channel"))
|
||||
if err != nil {
|
||||
panic("Couldn't connect to channel:" + c.Param("channel"))
|
||||
}
|
||||
|
||||
c.Writer.WriteHeader(200)
|
||||
|
||||
messagesSent := 0
|
||||
for range time.Tick(time.Duration(interval) * time.Second) {
|
||||
message := fmt.Sprintf("PING no %d : %d", messagesSent, time.Now().Unix())
|
||||
ch.Publish(message)
|
||||
messagesSent++
|
||||
|
||||
c.Writer.WriteString(fmt.Sprintf("* SENT: %17d MESSAGES -> %s*\n", messagesSent, message))
|
||||
c.Writer.Flush()
|
||||
|
||||
if messagesSent >= count {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.Writer.WriteString("DONE")
|
||||
c.Writer.Flush()
|
||||
c.Writer.CloseNotify()
|
||||
})
|
||||
|
||||
r.Run() // listen and serve on 0.0.0.0:8080
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func MustParseIntFromQuery(c *gin.Context, q string) int {
|
||||
valueStr := c.DefaultQuery(q, "1")
|
||||
value, err := strconv.ParseInt(valueStr, 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return int(value)
|
||||
}
|
|
@ -41,6 +41,17 @@ const (
|
|||
totalAvailableLanguages
|
||||
)
|
||||
|
||||
type entropyStrength int
|
||||
|
||||
// Valid entropy strengths
|
||||
const (
|
||||
EntropyStrength128 entropyStrength = 128 + 32*iota
|
||||
EntropyStrength160
|
||||
EntropyStrength192
|
||||
EntropyStrength224
|
||||
EntropyStrength256
|
||||
)
|
||||
|
||||
// Languages is a list of supported languages for which mnemonic keys can be generated
|
||||
var Languages = [...]string{
|
||||
"English",
|
||||
|
@ -53,6 +64,11 @@ var Languages = [...]string{
|
|||
"Russian",
|
||||
}
|
||||
|
||||
// ErrInvalidEntropyStrength is the error returned by MnemonicPhrase
|
||||
// when the entropy strength is not valid.
|
||||
// Valid entropy strength values are multiple of 32 between 128 and 256.
|
||||
var ErrInvalidEntropyStrength = errors.New("The mnemonic must encode entropy in a multiple of 32 bits, The recommended size of ENT is 128-256 bits")
|
||||
|
||||
var (
|
||||
last11BitsMask = big.NewInt(2047)
|
||||
rightShift11BitsDivider = big.NewInt(2048)
|
||||
|
@ -118,7 +134,7 @@ func (m *Mnemonic) MnemonicSeed(mnemonic string, password string) []byte {
|
|||
}
|
||||
|
||||
// MnemonicPhrase returns a human readable seed for BIP32 Hierarchical Deterministic Wallets
|
||||
func (m *Mnemonic) MnemonicPhrase(strength, language Language) (string, error) {
|
||||
func (m *Mnemonic) MnemonicPhrase(strength entropyStrength, language Language) (string, error) {
|
||||
wordList, err := m.WordList(language)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -128,8 +144,8 @@ func (m *Mnemonic) MnemonicPhrase(strength, language Language) (string, error) {
|
|||
// With more entropy security is improved but the sentence length increases.
|
||||
// We refer to the initial entropy length as ENT. The recommended size of ENT is 128-256 bits.
|
||||
|
||||
if strength%32 > 0 && strength < 128 && strength > 256 {
|
||||
return "", errors.New("The mnemonic must encode entropy in a multiple of 32 bits, The recommended size of ENT is 128-256 bits")
|
||||
if strength%32 > 0 || strength < 128 || strength > 256 {
|
||||
return "", ErrInvalidEntropyStrength
|
||||
}
|
||||
|
||||
// First, an initial entropy of ENT bits is generated
|
||||
|
@ -220,7 +236,7 @@ func (m *Mnemonic) ValidMnemonic(mnemonic string, language Language) bool {
|
|||
|
||||
// WordList returns list of words for a given language
|
||||
func (m *Mnemonic) WordList(language Language) (*WordList, error) {
|
||||
if m.wordLists[language] == nil {
|
||||
if int(language) < 0 || int(language) > len(m.wordLists)-1 || m.wordLists[language] == nil {
|
||||
return nil, fmt.Errorf("language word list is missing (language id: %d)", language)
|
||||
}
|
||||
return m.wordLists[language], nil
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -14,30 +13,32 @@ import (
|
|||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrAddressToAccountMappingFailure = errors.New("cannot retrieve a valid account for a given address")
|
||||
ErrAccountToKeyMappingFailure = errors.New("cannot retrieve a valid key for a given account")
|
||||
ErrWhisperIdentityInjectionFailure = errors.New("failed to inject identity into Whisper")
|
||||
ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities")
|
||||
ErrNoAccountSelected = errors.New("no account has been selected, please login")
|
||||
ErrInvalidMasterKeyCreated = errors.New("can not create master extended key")
|
||||
)
|
||||
|
||||
// Manager represents account manager interface
|
||||
type Manager struct {
|
||||
nodeManager common.NodeManager
|
||||
selectedAccount *common.SelectedExtKey // account that was processed during the last call to SelectAccount()
|
||||
// GethServiceProvider provides required geth services.
|
||||
type GethServiceProvider interface {
|
||||
AccountManager() (*accounts.Manager, error)
|
||||
AccountKeyStore() (*keystore.KeyStore, error)
|
||||
}
|
||||
|
||||
// NewManager returns new node account manager
|
||||
func NewManager(nodeManager common.NodeManager) *Manager {
|
||||
// Manager represents account manager interface.
|
||||
type Manager struct {
|
||||
geth GethServiceProvider
|
||||
selectedAccount *SelectedExtKey // account that was processed during the last call to SelectAccount()
|
||||
}
|
||||
|
||||
// NewManager returns new node account manager.
|
||||
func NewManager(geth GethServiceProvider) *Manager {
|
||||
return &Manager{
|
||||
nodeManager: nodeManager,
|
||||
geth: geth,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +49,7 @@ func NewManager(nodeManager common.NodeManager) *Manager {
|
|||
func (m *Manager) CreateAccount(password string) (address, pubKey, mnemonic string, err error) {
|
||||
// generate mnemonic phrase
|
||||
mn := extkeys.NewMnemonic(extkeys.Salt)
|
||||
mnemonic, err = mn.MnemonicPhrase(128, extkeys.EnglishLanguage)
|
||||
mnemonic, err = mn.MnemonicPhrase(extkeys.EntropyStrength128, extkeys.EnglishLanguage)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("can not create mnemonic seed: %v", err)
|
||||
}
|
||||
|
@ -72,7 +73,7 @@ func (m *Manager) CreateAccount(password string) (address, pubKey, mnemonic stri
|
|||
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
||||
func (m *Manager) CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
||||
keyStore, err := m.nodeManager.AccountKeyStore()
|
||||
keyStore, err := m.geth.AccountKeyStore()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
@ -85,7 +86,7 @@ func (m *Manager) CreateChildAccount(parentAddress, password string) (address, p
|
|||
return "", "", ErrNoAccountSelected
|
||||
}
|
||||
|
||||
account, err := common.ParseAccountString(parentAddress)
|
||||
account, err := ParseAccountString(parentAddress)
|
||||
if err != nil {
|
||||
return "", "", ErrAddressToAccountMappingFailure
|
||||
}
|
||||
|
@ -203,15 +204,14 @@ func (m *Manager) VerifyAccountPassword(keyStoreDir, address, password string) (
|
|||
}
|
||||
|
||||
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
||||
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
||||
// all previous identities are removed).
|
||||
// using provided password. Once verification is done, all previous identities are removed).
|
||||
func (m *Manager) SelectAccount(address, password string) error {
|
||||
keyStore, err := m.nodeManager.AccountKeyStore()
|
||||
keyStore, err := m.geth.AccountKeyStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account, err := common.ParseAccountString(address)
|
||||
account, err := ParseAccountString(address)
|
||||
if err != nil {
|
||||
return ErrAddressToAccountMappingFailure
|
||||
}
|
||||
|
@ -221,22 +221,12 @@ func (m *Manager) SelectAccount(address, password string) error {
|
|||
return fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err)
|
||||
}
|
||||
|
||||
whisperService, err := m.nodeManager.WhisperService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = whisperService.SelectKeyPair(accountKey.PrivateKey)
|
||||
if err != nil {
|
||||
return ErrWhisperIdentityInjectionFailure
|
||||
}
|
||||
|
||||
// persist account key for easier recovery of currently selected key
|
||||
subAccounts, err := m.findSubAccounts(accountKey.ExtendedKey, accountKey.SubAccountIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.selectedAccount = &common.SelectedExtKey{
|
||||
m.selectedAccount = &SelectedExtKey{
|
||||
Address: account.Address,
|
||||
AccountKey: accountKey,
|
||||
SubAccounts: subAccounts,
|
||||
|
@ -246,44 +236,15 @@ func (m *Manager) SelectAccount(address, password string) error {
|
|||
}
|
||||
|
||||
// SelectedAccount returns currently selected account
|
||||
func (m *Manager) SelectedAccount() (*common.SelectedExtKey, error) {
|
||||
func (m *Manager) SelectedAccount() (*SelectedExtKey, error) {
|
||||
if m.selectedAccount == nil {
|
||||
return nil, ErrNoAccountSelected
|
||||
}
|
||||
return m.selectedAccount, nil
|
||||
}
|
||||
|
||||
// ReSelectAccount selects previously selected account, often, after node restart.
|
||||
func (m *Manager) ReSelectAccount() error {
|
||||
selectedAccount := m.selectedAccount
|
||||
if selectedAccount == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
whisperService, err := m.nodeManager.WhisperService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := whisperService.SelectKeyPair(selectedAccount.AccountKey.PrivateKey); err != nil {
|
||||
return ErrWhisperIdentityInjectionFailure
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logout clears whisper identities
|
||||
// Logout clears selectedAccount.
|
||||
func (m *Manager) Logout() error {
|
||||
whisperService, err := m.nodeManager.WhisperService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = whisperService.DeleteKeyPairs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", ErrWhisperClearIdentitiesFailure, err)
|
||||
}
|
||||
|
||||
m.selectedAccount = nil
|
||||
|
||||
return nil
|
||||
|
@ -292,7 +253,7 @@ func (m *Manager) Logout() error {
|
|||
// importExtendedKey processes incoming extended key, extracts required info and creates corresponding account key.
|
||||
// Once account key is formed, that key is put (if not already) into keystore i.e. key is *encoded* into key file.
|
||||
func (m *Manager) importExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) {
|
||||
keyStore, err := m.nodeManager.AccountKeyStore()
|
||||
keyStore, err := m.geth.AccountKeyStore()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
@ -317,7 +278,7 @@ func (m *Manager) importExtendedKey(extKey *extkeys.ExtendedKey, password string
|
|||
// Accounts returns list of addresses for selected account, including
|
||||
// subaccounts.
|
||||
func (m *Manager) Accounts() ([]gethcommon.Address, error) {
|
||||
am, err := m.nodeManager.AccountManager()
|
||||
am, err := m.geth.AccountManager()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -353,13 +314,6 @@ func (m *Manager) Accounts() ([]gethcommon.Address, error) {
|
|||
return filtered, nil
|
||||
}
|
||||
|
||||
// AccountsRPCHandler returns RPC Handler for the Accounts() method.
|
||||
func (m *Manager) AccountsRPCHandler() rpc.Handler {
|
||||
return func(context.Context, ...interface{}) (interface{}, error) {
|
||||
return m.Accounts()
|
||||
}
|
||||
}
|
||||
|
||||
// refreshSelectedAccount re-populates list of sub-accounts of the currently selected account (if any)
|
||||
func (m *Manager) refreshSelectedAccount() {
|
||||
if m.selectedAccount == nil {
|
||||
|
@ -376,7 +330,7 @@ func (m *Manager) refreshSelectedAccount() {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.selectedAccount = &common.SelectedExtKey{
|
||||
m.selectedAccount = &SelectedExtKey{
|
||||
Address: m.selectedAccount.Address,
|
||||
AccountKey: m.selectedAccount.AccountKey,
|
||||
SubAccounts: subAccounts,
|
||||
|
@ -387,7 +341,7 @@ func (m *Manager) refreshSelectedAccount() {
|
|||
// that belong to the currently selected account.
|
||||
// The extKey is CKD#2 := root of sub-accounts of the main account
|
||||
func (m *Manager) findSubAccounts(extKey *extkeys.ExtendedKey, subAccountIndex uint32) ([]accounts.Account, error) {
|
||||
keyStore, err := m.nodeManager.AccountKeyStore()
|
||||
keyStore, err := m.geth.AccountKeyStore()
|
||||
if err != nil {
|
||||
return []accounts.Account{}, err
|
||||
}
|
||||
|
@ -421,12 +375,12 @@ func (m *Manager) findSubAccounts(extKey *extkeys.ExtendedKey, subAccountIndex u
|
|||
// The running node, has a keystore directory which is loaded on start. Key file
|
||||
// for a given address is expected to be in that directory prior to node start.
|
||||
func (m *Manager) AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error) {
|
||||
keyStore, err := m.nodeManager.AccountKeyStore()
|
||||
keyStore, err := m.geth.AccountKeyStore()
|
||||
if err != nil {
|
||||
return accounts.Account{}, nil, err
|
||||
}
|
||||
|
||||
account, err := common.ParseAccountString(address)
|
||||
account, err := ParseAccountString(address)
|
||||
if err != nil {
|
||||
return accounts.Account{}, nil, ErrAddressToAccountMappingFailure
|
||||
}
|
||||
|
|
62
vendor/github.com/status-im/status-go/geth/account/accounts_mock.go
generated
vendored
Normal file
62
vendor/github.com/status-im/status-go/geth/account/accounts_mock.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: geth/account/accounts.go
|
||||
|
||||
// Package transactions is a generated GoMock package.
|
||||
package account
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
accounts "github.com/ethereum/go-ethereum/accounts"
|
||||
keystore "github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockGethServiceProvider is a mock of GethServiceProvider interface
|
||||
type MockGethServiceProvider struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockGethServiceProviderMockRecorder
|
||||
}
|
||||
|
||||
// MockGethServiceProviderMockRecorder is the mock recorder for MockGethServiceProvider
|
||||
type MockGethServiceProviderMockRecorder struct {
|
||||
mock *MockGethServiceProvider
|
||||
}
|
||||
|
||||
// NewMockGethServiceProvider creates a new mock instance
|
||||
func NewMockGethServiceProvider(ctrl *gomock.Controller) *MockGethServiceProvider {
|
||||
mock := &MockGethServiceProvider{ctrl: ctrl}
|
||||
mock.recorder = &MockGethServiceProviderMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockGethServiceProvider) EXPECT() *MockGethServiceProviderMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// AccountManager mocks base method
|
||||
func (m *MockGethServiceProvider) AccountManager() (*accounts.Manager, error) {
|
||||
ret := m.ctrl.Call(m, "AccountManager")
|
||||
ret0, _ := ret[0].(*accounts.Manager)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// AccountManager indicates an expected call of AccountManager
|
||||
func (mr *MockGethServiceProviderMockRecorder) AccountManager() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccountManager", reflect.TypeOf((*MockGethServiceProvider)(nil).AccountManager))
|
||||
}
|
||||
|
||||
// AccountKeyStore mocks base method
|
||||
func (m *MockGethServiceProvider) AccountKeyStore() (*keystore.KeyStore, error) {
|
||||
ret := m.ctrl.Call(m, "AccountKeyStore")
|
||||
ret0, _ := ret[0].(*keystore.KeyStore)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// AccountKeyStore indicates an expected call of AccountKeyStore
|
||||
func (mr *MockGethServiceProviderMockRecorder) AccountKeyStore() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccountKeyStore", reflect.TypeOf((*MockGethServiceProvider)(nil).AccountKeyStore))
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrInvalidAccountAddressOrKey = errors.New("cannot parse address or key to valid account address")
|
||||
)
|
||||
|
||||
// SelectedExtKey is a container for the selected (logged in) external account.
|
||||
type SelectedExtKey struct {
|
||||
Address common.Address
|
||||
AccountKey *keystore.Key
|
||||
SubAccounts []accounts.Account
|
||||
}
|
||||
|
||||
// Hex dumps address of a given extended key as hex string.
|
||||
func (k *SelectedExtKey) Hex() string {
|
||||
if k == nil {
|
||||
return "0x0"
|
||||
}
|
||||
|
||||
return k.Address.Hex()
|
||||
}
|
||||
|
||||
// ParseAccountString parses hex encoded string and returns is as accounts.Account.
|
||||
func ParseAccountString(account string) (accounts.Account, error) {
|
||||
// valid address, convert to account
|
||||
if common.IsHexAddress(account) {
|
||||
return accounts.Account{Address: common.HexToAddress(account)}, nil
|
||||
}
|
||||
|
||||
return accounts.Account{}, ErrInvalidAccountAddressOrKey
|
||||
}
|
||||
|
||||
// FromAddress converts account address from string to common.Address.
|
||||
// The function is useful to format "From" field of send transaction struct.
|
||||
func FromAddress(accountAddress string) common.Address {
|
||||
from, err := ParseAccountString(accountAddress)
|
||||
if err != nil {
|
||||
return common.Address{}
|
||||
}
|
||||
|
||||
return from.Address
|
||||
}
|
||||
|
||||
// ToAddress converts account address from string to *common.Address.
|
||||
// The function is useful to format "To" field of send transaction struct.
|
||||
func ToAddress(accountAddress string) *common.Address {
|
||||
to, err := ParseAccountString(accountAddress)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &to.Address
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package common
|
||||
|
||||
import "github.com/NaySoftware/go-fcm"
|
||||
|
||||
// Notifier manages Push Notifications.
|
||||
type Notifier interface {
|
||||
Send(body string, payload fcm.NotificationPayload, tokens ...string) error
|
||||
}
|
||||
|
||||
// NotificationConstructor returns constructor of configured instance Notifier interface.
|
||||
type NotificationConstructor func() Notifier
|
|
@ -1,51 +0,0 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: geth/common/notification.go
|
||||
|
||||
// Package common is a generated GoMock package.
|
||||
package common
|
||||
|
||||
import (
|
||||
go_fcm "github.com/NaySoftware/go-fcm"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockNotifier is a mock of Notifier interface
|
||||
type MockNotifier struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockNotifierMockRecorder
|
||||
}
|
||||
|
||||
// MockNotifierMockRecorder is the mock recorder for MockNotifier
|
||||
type MockNotifierMockRecorder struct {
|
||||
mock *MockNotifier
|
||||
}
|
||||
|
||||
// NewMockNotifier creates a new mock instance
|
||||
func NewMockNotifier(ctrl *gomock.Controller) *MockNotifier {
|
||||
mock := &MockNotifier{ctrl: ctrl}
|
||||
mock.recorder = &MockNotifierMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockNotifier) EXPECT() *MockNotifierMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Send mocks base method
|
||||
func (m *MockNotifier) Send(body string, payload go_fcm.NotificationPayload, tokens ...string) error {
|
||||
varargs := []interface{}{body, payload}
|
||||
for _, a := range tokens {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "Send", varargs...)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Send indicates an expected call of Send
|
||||
func (mr *MockNotifierMockRecorder) Send(body, payload interface{}, tokens ...interface{}) *gomock.Call {
|
||||
varargs := append([]interface{}{body, payload}, tokens...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockNotifier)(nil).Send), varargs...)
|
||||
}
|
|
@ -1,367 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
"github.com/robertkrimen/otto"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
"github.com/status-im/status-go/static"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrDeprecatedMethod = errors.New("Method is depricated and will be removed in future release")
|
||||
)
|
||||
|
||||
// SelectedExtKey is a container for currently selected (logged in) account
|
||||
type SelectedExtKey struct {
|
||||
Address common.Address
|
||||
AccountKey *keystore.Key
|
||||
SubAccounts []accounts.Account
|
||||
}
|
||||
|
||||
// Hex dumps address of a given extended key as hex string
|
||||
func (k *SelectedExtKey) Hex() string {
|
||||
if k == nil {
|
||||
return "0x0"
|
||||
}
|
||||
|
||||
return k.Address.Hex()
|
||||
}
|
||||
|
||||
// NodeManager defines expected methods for managing Status node
|
||||
type NodeManager interface {
|
||||
// StartNode start Status node, fails if node is already started
|
||||
StartNode(config *params.NodeConfig) error
|
||||
|
||||
// EnsureSync waits until blockchain is synchronized.
|
||||
EnsureSync(ctx context.Context) error
|
||||
|
||||
// StopNode stop the running Status node.
|
||||
// Stopped node cannot be resumed, one starts a new node instead.
|
||||
StopNode() error
|
||||
|
||||
// IsNodeRunning confirm that node is running
|
||||
IsNodeRunning() bool
|
||||
|
||||
// NodeConfig returns reference to running node's configuration
|
||||
NodeConfig() (*params.NodeConfig, error)
|
||||
|
||||
// Node returns underlying Status node
|
||||
Node() (*node.Node, error)
|
||||
|
||||
// PopulateStaticPeers populates node's list of static bootstrap peers
|
||||
PopulateStaticPeers() error
|
||||
|
||||
// AddPeer adds URL of static peer
|
||||
AddPeer(url string) error
|
||||
|
||||
// PeerCount returns number of connected peers
|
||||
PeerCount() int
|
||||
|
||||
// LightEthereumService exposes reference to LES service running on top of the node
|
||||
LightEthereumService() (*les.LightEthereum, error)
|
||||
|
||||
// WhisperService returns reference to running Whisper service
|
||||
WhisperService() (*whisper.Whisper, error)
|
||||
|
||||
// AccountManager returns reference to node's account manager
|
||||
AccountManager() (*accounts.Manager, error)
|
||||
|
||||
// AccountKeyStore returns reference to account manager's keystore
|
||||
AccountKeyStore() (*keystore.KeyStore, error)
|
||||
|
||||
// RPCClient exposes reference to RPC client connected to the running node
|
||||
RPCClient() *rpc.Client
|
||||
}
|
||||
|
||||
// AccountManager defines expected methods for managing Status accounts
|
||||
type AccountManager interface {
|
||||
// CreateAccount creates an internal geth account
|
||||
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
||||
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
||||
// sub-account derivations)
|
||||
CreateAccount(password string) (address, pubKey, mnemonic string, err error)
|
||||
|
||||
// CreateChildAccount creates sub-account for an account identified by parent address.
|
||||
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
||||
CreateChildAccount(parentAddress, password string) (address, pubKey string, err error)
|
||||
|
||||
// RecoverAccount re-creates master key using given details.
|
||||
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
||||
RecoverAccount(password, mnemonic string) (address, pubKey string, err error)
|
||||
|
||||
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
||||
// If no error is returned, then account is considered verified.
|
||||
VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error)
|
||||
|
||||
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
||||
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
||||
// all previous identities are removed).
|
||||
SelectAccount(address, password string) error
|
||||
|
||||
// ReSelectAccount selects previously selected account, often, after node restart.
|
||||
ReSelectAccount() error
|
||||
|
||||
// SelectedAccount returns currently selected account
|
||||
SelectedAccount() (*SelectedExtKey, error)
|
||||
|
||||
// Logout clears whisper identities
|
||||
Logout() error
|
||||
|
||||
// Accounts returns handler to process account list request
|
||||
Accounts() ([]common.Address, error)
|
||||
|
||||
// AccountsRPCHandler returns RPC wrapper for Accounts()
|
||||
AccountsRPCHandler() rpc.Handler
|
||||
|
||||
// AddressToDecryptedAccount tries to load decrypted key for a given account.
|
||||
// The running node, has a keystore directory which is loaded on start. Key file
|
||||
// for a given address is expected to be in that directory prior to node start.
|
||||
AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error)
|
||||
}
|
||||
|
||||
// TransactionResult is a JSON returned from transaction complete function (used internally)
|
||||
type TransactionResult struct {
|
||||
Hash common.Hash
|
||||
Error error
|
||||
}
|
||||
|
||||
// RawDiscardTransactionResult is list of results from CompleteTransactions() (used internally)
|
||||
type RawDiscardTransactionResult struct {
|
||||
Error error
|
||||
}
|
||||
|
||||
// QueuedTxID queued transaction identifier
|
||||
type QueuedTxID string
|
||||
|
||||
// QueuedTx holds enough information to complete the queued transaction.
|
||||
type QueuedTx struct {
|
||||
ID QueuedTxID
|
||||
Context context.Context
|
||||
Args SendTxArgs
|
||||
Result chan TransactionResult
|
||||
}
|
||||
|
||||
// SendTxArgs represents the arguments to submit a new transaction into the transaction pool.
|
||||
// This struct is based on go-ethereum's type in internal/ethapi/api.go, but we have freedom
|
||||
// over the exact layout of this struct.
|
||||
type SendTxArgs struct {
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
}
|
||||
|
||||
// JailCell represents single jail cell, which is basically a JavaScript VM.
|
||||
// It's designed to be a transparent wrapper around otto.VM's methods.
|
||||
type JailCell interface {
|
||||
// Set a value inside VM.
|
||||
Set(string, interface{}) error
|
||||
// Get a value from VM.
|
||||
Get(string) (otto.Value, error)
|
||||
// GetObjectValue returns the given name's otto.Value from the given otto.Value v. Should only be needed in tests.
|
||||
GetObjectValue(otto.Value, string) (otto.Value, error)
|
||||
// Run an arbitrary JS code. Input maybe string or otto.Script.
|
||||
Run(interface{}) (otto.Value, error)
|
||||
// Call an arbitrary JS function by name and args.
|
||||
Call(item string, this interface{}, args ...interface{}) (otto.Value, error)
|
||||
// Stop stops background execution of cell.
|
||||
Stop() error
|
||||
}
|
||||
|
||||
// JailManager defines methods for managing jailed environments
|
||||
type JailManager interface {
|
||||
// Call executes given JavaScript function w/i a jail cell context identified by the chatID.
|
||||
Call(chatID, this, args string) string
|
||||
|
||||
// CreateCell creates a new jail cell.
|
||||
CreateCell(chatID string) (JailCell, error)
|
||||
|
||||
// Parse creates a new jail cell context, with the given chatID as identifier.
|
||||
// New context executes provided JavaScript code, right after the initialization.
|
||||
// DEPRECATED in favour of CreateAndInitCell.
|
||||
Parse(chatID, js string) string
|
||||
|
||||
// CreateAndInitCell creates a new jail cell and initialize it
|
||||
// with web3 and other handlers.
|
||||
CreateAndInitCell(chatID string, code ...string) string
|
||||
|
||||
// Cell returns an existing instance of JailCell.
|
||||
Cell(chatID string) (JailCell, error)
|
||||
|
||||
// Execute allows to run arbitrary JS code within a cell.
|
||||
Execute(chatID, code string) string
|
||||
|
||||
// SetBaseJS allows to setup initial JavaScript to be loaded on each jail.CreateAndInitCell().
|
||||
SetBaseJS(js string)
|
||||
|
||||
// Stop stops all background activity of jail
|
||||
Stop()
|
||||
}
|
||||
|
||||
// APIResponse generic response from API
|
||||
type APIResponse struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// APIDetailedResponse represents a generic response
|
||||
// with possible errors.
|
||||
type APIDetailedResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
FieldErrors []APIFieldError `json:"field_errors,omitempty"`
|
||||
}
|
||||
|
||||
func (r APIDetailedResponse) Error() string {
|
||||
buf := bytes.NewBufferString("")
|
||||
|
||||
for _, err := range r.FieldErrors {
|
||||
buf.WriteString(err.Error() + "\n") // nolint: gas
|
||||
}
|
||||
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
|
||||
// APIFieldError represents a set of errors
|
||||
// related to a parameter.
|
||||
type APIFieldError struct {
|
||||
Parameter string `json:"parameter,omitempty"`
|
||||
Errors []APIError `json:"errors"`
|
||||
}
|
||||
|
||||
func (e APIFieldError) Error() string {
|
||||
if len(e.Errors) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(fmt.Sprintf("Parameter: %s\n", e.Parameter))
|
||||
|
||||
for _, err := range e.Errors {
|
||||
buf.WriteString(err.Error() + "\n") // nolint: gas
|
||||
}
|
||||
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
|
||||
// APIError represents a single error.
|
||||
type APIError struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e APIError) Error() string {
|
||||
return fmt.Sprintf("message=%s", e.Message)
|
||||
}
|
||||
|
||||
// AccountInfo represents account's info
|
||||
type AccountInfo struct {
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
Mnemonic string `json:"mnemonic"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// StopRPCCallError defines a error type specific for killing a execution process.
|
||||
type StopRPCCallError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error returns the internal error associated with the critical error.
|
||||
func (c StopRPCCallError) Error() string {
|
||||
return c.Err.Error()
|
||||
}
|
||||
|
||||
// CompleteTransactionResult is a JSON returned from transaction complete function (used in exposed method)
|
||||
type CompleteTransactionResult struct {
|
||||
ID string `json:"id"`
|
||||
Hash string `json:"hash"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// CompleteTransactionsResult is list of results from CompleteTransactions() (used in exposed method)
|
||||
type CompleteTransactionsResult struct {
|
||||
Results map[string]CompleteTransactionResult `json:"results"`
|
||||
}
|
||||
|
||||
// DiscardTransactionResult is a JSON returned from transaction discard function
|
||||
type DiscardTransactionResult struct {
|
||||
ID string `json:"id"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// DiscardTransactionsResult is a list of results from DiscardTransactions()
|
||||
type DiscardTransactionsResult struct {
|
||||
Results map[string]DiscardTransactionResult `json:"results"`
|
||||
}
|
||||
|
||||
type account struct {
|
||||
Address string
|
||||
Password string
|
||||
}
|
||||
|
||||
// TestConfig contains shared (among different test packages) parameters
|
||||
type TestConfig struct {
|
||||
Node struct {
|
||||
SyncSeconds time.Duration
|
||||
HTTPPort int
|
||||
WSPort int
|
||||
}
|
||||
Account1 account
|
||||
Account2 account
|
||||
Account3 account
|
||||
}
|
||||
|
||||
// NotifyResult is a JSON returned from notify message
|
||||
type NotifyResult struct {
|
||||
Status bool `json:"status"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
const passphraseEnvName = "ACCOUNT_PASSWORD"
|
||||
|
||||
// LoadTestConfig loads test configuration values from disk
|
||||
func LoadTestConfig(networkID int) (*TestConfig, error) {
|
||||
var testConfig TestConfig
|
||||
|
||||
configData := static.MustAsset("config/test-data.json")
|
||||
if err := json.Unmarshal(configData, &testConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if networkID == params.StatusChainNetworkID {
|
||||
accountsData := static.MustAsset("config/status-chain-accounts.json")
|
||||
if err := json.Unmarshal(accountsData, &testConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
accountsData := static.MustAsset("config/public-chain-accounts.json")
|
||||
if err := json.Unmarshal(accountsData, &testConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pass := os.Getenv(passphraseEnvName)
|
||||
testConfig.Account1.Password = pass
|
||||
testConfig.Account2.Password = pass
|
||||
}
|
||||
|
||||
return &testConfig, nil
|
||||
}
|
|
@ -1,610 +0,0 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: geth/common/types.go
|
||||
|
||||
// Package common is a generated GoMock package.
|
||||
package common
|
||||
|
||||
import (
|
||||
context "context"
|
||||
accounts "github.com/ethereum/go-ethereum/accounts"
|
||||
keystore "github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
common "github.com/ethereum/go-ethereum/common"
|
||||
les "github.com/ethereum/go-ethereum/les"
|
||||
node "github.com/ethereum/go-ethereum/node"
|
||||
whisperv6 "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
otto "github.com/robertkrimen/otto"
|
||||
params "github.com/status-im/status-go/geth/params"
|
||||
rpc "github.com/status-im/status-go/geth/rpc"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockNodeManager is a mock of NodeManager interface
|
||||
type MockNodeManager struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockNodeManagerMockRecorder
|
||||
}
|
||||
|
||||
// MockNodeManagerMockRecorder is the mock recorder for MockNodeManager
|
||||
type MockNodeManagerMockRecorder struct {
|
||||
mock *MockNodeManager
|
||||
}
|
||||
|
||||
// NewMockNodeManager creates a new mock instance
|
||||
func NewMockNodeManager(ctrl *gomock.Controller) *MockNodeManager {
|
||||
mock := &MockNodeManager{ctrl: ctrl}
|
||||
mock.recorder = &MockNodeManagerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockNodeManager) EXPECT() *MockNodeManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// StartNode mocks base method
|
||||
func (m *MockNodeManager) StartNode(config *params.NodeConfig) error {
|
||||
ret := m.ctrl.Call(m, "StartNode", config)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// StartNode indicates an expected call of StartNode
|
||||
func (mr *MockNodeManagerMockRecorder) StartNode(config interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartNode", reflect.TypeOf((*MockNodeManager)(nil).StartNode), config)
|
||||
}
|
||||
|
||||
// EnsureSync mocks base method
|
||||
func (m *MockNodeManager) EnsureSync(ctx context.Context) error {
|
||||
ret := m.ctrl.Call(m, "EnsureSync", ctx)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// EnsureSync indicates an expected call of EnsureSync
|
||||
func (mr *MockNodeManagerMockRecorder) EnsureSync(ctx interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureSync", reflect.TypeOf((*MockNodeManager)(nil).EnsureSync), ctx)
|
||||
}
|
||||
|
||||
// StopNode mocks base method
|
||||
func (m *MockNodeManager) StopNode() error {
|
||||
ret := m.ctrl.Call(m, "StopNode")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// StopNode indicates an expected call of StopNode
|
||||
func (mr *MockNodeManagerMockRecorder) StopNode() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopNode", reflect.TypeOf((*MockNodeManager)(nil).StopNode))
|
||||
}
|
||||
|
||||
// IsNodeRunning mocks base method
|
||||
func (m *MockNodeManager) IsNodeRunning() bool {
|
||||
ret := m.ctrl.Call(m, "IsNodeRunning")
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// IsNodeRunning indicates an expected call of IsNodeRunning
|
||||
func (mr *MockNodeManagerMockRecorder) IsNodeRunning() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNodeRunning", reflect.TypeOf((*MockNodeManager)(nil).IsNodeRunning))
|
||||
}
|
||||
|
||||
// NodeConfig mocks base method
|
||||
func (m *MockNodeManager) NodeConfig() (*params.NodeConfig, error) {
|
||||
ret := m.ctrl.Call(m, "NodeConfig")
|
||||
ret0, _ := ret[0].(*params.NodeConfig)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// NodeConfig indicates an expected call of NodeConfig
|
||||
func (mr *MockNodeManagerMockRecorder) NodeConfig() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeConfig", reflect.TypeOf((*MockNodeManager)(nil).NodeConfig))
|
||||
}
|
||||
|
||||
// Node mocks base method
|
||||
func (m *MockNodeManager) Node() (*node.Node, error) {
|
||||
ret := m.ctrl.Call(m, "Node")
|
||||
ret0, _ := ret[0].(*node.Node)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Node indicates an expected call of Node
|
||||
func (mr *MockNodeManagerMockRecorder) Node() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Node", reflect.TypeOf((*MockNodeManager)(nil).Node))
|
||||
}
|
||||
|
||||
// PopulateStaticPeers mocks base method
|
||||
func (m *MockNodeManager) PopulateStaticPeers() error {
|
||||
ret := m.ctrl.Call(m, "PopulateStaticPeers")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// PopulateStaticPeers indicates an expected call of PopulateStaticPeers
|
||||
func (mr *MockNodeManagerMockRecorder) PopulateStaticPeers() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PopulateStaticPeers", reflect.TypeOf((*MockNodeManager)(nil).PopulateStaticPeers))
|
||||
}
|
||||
|
||||
// AddPeer mocks base method
|
||||
func (m *MockNodeManager) AddPeer(url string) error {
|
||||
ret := m.ctrl.Call(m, "AddPeer", url)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddPeer indicates an expected call of AddPeer
|
||||
func (mr *MockNodeManagerMockRecorder) AddPeer(url interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPeer", reflect.TypeOf((*MockNodeManager)(nil).AddPeer), url)
|
||||
}
|
||||
|
||||
// PeerCount mocks base method
|
||||
func (m *MockNodeManager) PeerCount() int {
|
||||
ret := m.ctrl.Call(m, "PeerCount")
|
||||
ret0, _ := ret[0].(int)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// PeerCount indicates an expected call of PeerCount
|
||||
func (mr *MockNodeManagerMockRecorder) PeerCount() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeerCount", reflect.TypeOf((*MockNodeManager)(nil).PeerCount))
|
||||
}
|
||||
|
||||
// LightEthereumService mocks base method
|
||||
func (m *MockNodeManager) LightEthereumService() (*les.LightEthereum, error) {
|
||||
ret := m.ctrl.Call(m, "LightEthereumService")
|
||||
ret0, _ := ret[0].(*les.LightEthereum)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LightEthereumService indicates an expected call of LightEthereumService
|
||||
func (mr *MockNodeManagerMockRecorder) LightEthereumService() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LightEthereumService", reflect.TypeOf((*MockNodeManager)(nil).LightEthereumService))
|
||||
}
|
||||
|
||||
// WhisperService mocks base method
|
||||
func (m *MockNodeManager) WhisperService() (*whisperv6.Whisper, error) {
|
||||
ret := m.ctrl.Call(m, "WhisperService")
|
||||
ret0, _ := ret[0].(*whisperv6.Whisper)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// WhisperService indicates an expected call of WhisperService
|
||||
func (mr *MockNodeManagerMockRecorder) WhisperService() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WhisperService", reflect.TypeOf((*MockNodeManager)(nil).WhisperService))
|
||||
}
|
||||
|
||||
// AccountManager mocks base method
|
||||
func (m *MockNodeManager) AccountManager() (*accounts.Manager, error) {
|
||||
ret := m.ctrl.Call(m, "AccountManager")
|
||||
ret0, _ := ret[0].(*accounts.Manager)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// AccountManager indicates an expected call of AccountManager
|
||||
func (mr *MockNodeManagerMockRecorder) AccountManager() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccountManager", reflect.TypeOf((*MockNodeManager)(nil).AccountManager))
|
||||
}
|
||||
|
||||
// AccountKeyStore mocks base method
|
||||
func (m *MockNodeManager) AccountKeyStore() (*keystore.KeyStore, error) {
|
||||
ret := m.ctrl.Call(m, "AccountKeyStore")
|
||||
ret0, _ := ret[0].(*keystore.KeyStore)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// AccountKeyStore indicates an expected call of AccountKeyStore
|
||||
func (mr *MockNodeManagerMockRecorder) AccountKeyStore() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccountKeyStore", reflect.TypeOf((*MockNodeManager)(nil).AccountKeyStore))
|
||||
}
|
||||
|
||||
// RPCClient mocks base method
|
||||
func (m *MockNodeManager) RPCClient() *rpc.Client {
|
||||
ret := m.ctrl.Call(m, "RPCClient")
|
||||
ret0, _ := ret[0].(*rpc.Client)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RPCClient indicates an expected call of RPCClient
|
||||
func (mr *MockNodeManagerMockRecorder) RPCClient() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RPCClient", reflect.TypeOf((*MockNodeManager)(nil).RPCClient))
|
||||
}
|
||||
|
||||
// MockAccountManager is a mock of AccountManager interface
|
||||
type MockAccountManager struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockAccountManagerMockRecorder
|
||||
}
|
||||
|
||||
// MockAccountManagerMockRecorder is the mock recorder for MockAccountManager
|
||||
type MockAccountManagerMockRecorder struct {
|
||||
mock *MockAccountManager
|
||||
}
|
||||
|
||||
// NewMockAccountManager creates a new mock instance
|
||||
func NewMockAccountManager(ctrl *gomock.Controller) *MockAccountManager {
|
||||
mock := &MockAccountManager{ctrl: ctrl}
|
||||
mock.recorder = &MockAccountManagerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockAccountManager) EXPECT() *MockAccountManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CreateAccount mocks base method
|
||||
func (m *MockAccountManager) CreateAccount(password string) (string, string, string, error) {
|
||||
ret := m.ctrl.Call(m, "CreateAccount", password)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(string)
|
||||
ret2, _ := ret[2].(string)
|
||||
ret3, _ := ret[3].(error)
|
||||
return ret0, ret1, ret2, ret3
|
||||
}
|
||||
|
||||
// CreateAccount indicates an expected call of CreateAccount
|
||||
func (mr *MockAccountManagerMockRecorder) CreateAccount(password interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAccount", reflect.TypeOf((*MockAccountManager)(nil).CreateAccount), password)
|
||||
}
|
||||
|
||||
// CreateChildAccount mocks base method
|
||||
func (m *MockAccountManager) CreateChildAccount(parentAddress, password string) (string, string, error) {
|
||||
ret := m.ctrl.Call(m, "CreateChildAccount", parentAddress, password)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(string)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// CreateChildAccount indicates an expected call of CreateChildAccount
|
||||
func (mr *MockAccountManagerMockRecorder) CreateChildAccount(parentAddress, password interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateChildAccount", reflect.TypeOf((*MockAccountManager)(nil).CreateChildAccount), parentAddress, password)
|
||||
}
|
||||
|
||||
// RecoverAccount mocks base method
|
||||
func (m *MockAccountManager) RecoverAccount(password, mnemonic string) (string, string, error) {
|
||||
ret := m.ctrl.Call(m, "RecoverAccount", password, mnemonic)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(string)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// RecoverAccount indicates an expected call of RecoverAccount
|
||||
func (mr *MockAccountManagerMockRecorder) RecoverAccount(password, mnemonic interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecoverAccount", reflect.TypeOf((*MockAccountManager)(nil).RecoverAccount), password, mnemonic)
|
||||
}
|
||||
|
||||
// VerifyAccountPassword mocks base method
|
||||
func (m *MockAccountManager) VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error) {
|
||||
ret := m.ctrl.Call(m, "VerifyAccountPassword", keyStoreDir, address, password)
|
||||
ret0, _ := ret[0].(*keystore.Key)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// VerifyAccountPassword indicates an expected call of VerifyAccountPassword
|
||||
func (mr *MockAccountManagerMockRecorder) VerifyAccountPassword(keyStoreDir, address, password interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyAccountPassword", reflect.TypeOf((*MockAccountManager)(nil).VerifyAccountPassword), keyStoreDir, address, password)
|
||||
}
|
||||
|
||||
// SelectAccount mocks base method
|
||||
func (m *MockAccountManager) SelectAccount(address, password string) error {
|
||||
ret := m.ctrl.Call(m, "SelectAccount", address, password)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SelectAccount indicates an expected call of SelectAccount
|
||||
func (mr *MockAccountManagerMockRecorder) SelectAccount(address, password interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAccount", reflect.TypeOf((*MockAccountManager)(nil).SelectAccount), address, password)
|
||||
}
|
||||
|
||||
// ReSelectAccount mocks base method
|
||||
func (m *MockAccountManager) ReSelectAccount() error {
|
||||
ret := m.ctrl.Call(m, "ReSelectAccount")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ReSelectAccount indicates an expected call of ReSelectAccount
|
||||
func (mr *MockAccountManagerMockRecorder) ReSelectAccount() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReSelectAccount", reflect.TypeOf((*MockAccountManager)(nil).ReSelectAccount))
|
||||
}
|
||||
|
||||
// SelectedAccount mocks base method
|
||||
func (m *MockAccountManager) SelectedAccount() (*SelectedExtKey, error) {
|
||||
ret := m.ctrl.Call(m, "SelectedAccount")
|
||||
ret0, _ := ret[0].(*SelectedExtKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SelectedAccount indicates an expected call of SelectedAccount
|
||||
func (mr *MockAccountManagerMockRecorder) SelectedAccount() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectedAccount", reflect.TypeOf((*MockAccountManager)(nil).SelectedAccount))
|
||||
}
|
||||
|
||||
// Logout mocks base method
|
||||
func (m *MockAccountManager) Logout() error {
|
||||
ret := m.ctrl.Call(m, "Logout")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Logout indicates an expected call of Logout
|
||||
func (mr *MockAccountManagerMockRecorder) Logout() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Logout", reflect.TypeOf((*MockAccountManager)(nil).Logout))
|
||||
}
|
||||
|
||||
// Accounts mocks base method
|
||||
func (m *MockAccountManager) Accounts() ([]common.Address, error) {
|
||||
ret := m.ctrl.Call(m, "Accounts")
|
||||
ret0, _ := ret[0].([]common.Address)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Accounts indicates an expected call of Accounts
|
||||
func (mr *MockAccountManagerMockRecorder) Accounts() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accounts", reflect.TypeOf((*MockAccountManager)(nil).Accounts))
|
||||
}
|
||||
|
||||
// AccountsRPCHandler mocks base method
|
||||
func (m *MockAccountManager) AccountsRPCHandler() rpc.Handler {
|
||||
ret := m.ctrl.Call(m, "AccountsRPCHandler")
|
||||
ret0, _ := ret[0].(rpc.Handler)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AccountsRPCHandler indicates an expected call of AccountsRPCHandler
|
||||
func (mr *MockAccountManagerMockRecorder) AccountsRPCHandler() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccountsRPCHandler", reflect.TypeOf((*MockAccountManager)(nil).AccountsRPCHandler))
|
||||
}
|
||||
|
||||
// AddressToDecryptedAccount mocks base method
|
||||
func (m *MockAccountManager) AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error) {
|
||||
ret := m.ctrl.Call(m, "AddressToDecryptedAccount", address, password)
|
||||
ret0, _ := ret[0].(accounts.Account)
|
||||
ret1, _ := ret[1].(*keystore.Key)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// AddressToDecryptedAccount indicates an expected call of AddressToDecryptedAccount
|
||||
func (mr *MockAccountManagerMockRecorder) AddressToDecryptedAccount(address, password interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddressToDecryptedAccount", reflect.TypeOf((*MockAccountManager)(nil).AddressToDecryptedAccount), address, password)
|
||||
}
|
||||
|
||||
// MockJailCell is a mock of JailCell interface
|
||||
type MockJailCell struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockJailCellMockRecorder
|
||||
}
|
||||
|
||||
// MockJailCellMockRecorder is the mock recorder for MockJailCell
|
||||
type MockJailCellMockRecorder struct {
|
||||
mock *MockJailCell
|
||||
}
|
||||
|
||||
// NewMockJailCell creates a new mock instance
|
||||
func NewMockJailCell(ctrl *gomock.Controller) *MockJailCell {
|
||||
mock := &MockJailCell{ctrl: ctrl}
|
||||
mock.recorder = &MockJailCellMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockJailCell) EXPECT() *MockJailCellMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Set mocks base method
|
||||
func (m *MockJailCell) Set(arg0 string, arg1 interface{}) error {
|
||||
ret := m.ctrl.Call(m, "Set", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Set indicates an expected call of Set
|
||||
func (mr *MockJailCellMockRecorder) Set(arg0, arg1 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockJailCell)(nil).Set), arg0, arg1)
|
||||
}
|
||||
|
||||
// Get mocks base method
|
||||
func (m *MockJailCell) Get(arg0 string) (otto.Value, error) {
|
||||
ret := m.ctrl.Call(m, "Get", arg0)
|
||||
ret0, _ := ret[0].(otto.Value)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Get indicates an expected call of Get
|
||||
func (mr *MockJailCellMockRecorder) Get(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockJailCell)(nil).Get), arg0)
|
||||
}
|
||||
|
||||
// GetObjectValue mocks base method
|
||||
func (m *MockJailCell) GetObjectValue(arg0 otto.Value, arg1 string) (otto.Value, error) {
|
||||
ret := m.ctrl.Call(m, "GetObjectValue", arg0, arg1)
|
||||
ret0, _ := ret[0].(otto.Value)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetObjectValue indicates an expected call of GetObjectValue
|
||||
func (mr *MockJailCellMockRecorder) GetObjectValue(arg0, arg1 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectValue", reflect.TypeOf((*MockJailCell)(nil).GetObjectValue), arg0, arg1)
|
||||
}
|
||||
|
||||
// Run mocks base method
|
||||
func (m *MockJailCell) Run(arg0 interface{}) (otto.Value, error) {
|
||||
ret := m.ctrl.Call(m, "Run", arg0)
|
||||
ret0, _ := ret[0].(otto.Value)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Run indicates an expected call of Run
|
||||
func (mr *MockJailCellMockRecorder) Run(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockJailCell)(nil).Run), arg0)
|
||||
}
|
||||
|
||||
// Call mocks base method
|
||||
func (m *MockJailCell) Call(item string, this interface{}, args ...interface{}) (otto.Value, error) {
|
||||
varargs := []interface{}{item, this}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "Call", varargs...)
|
||||
ret0, _ := ret[0].(otto.Value)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Call indicates an expected call of Call
|
||||
func (mr *MockJailCellMockRecorder) Call(item, this interface{}, args ...interface{}) *gomock.Call {
|
||||
varargs := append([]interface{}{item, this}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockJailCell)(nil).Call), varargs...)
|
||||
}
|
||||
|
||||
// Stop mocks base method
|
||||
func (m *MockJailCell) Stop() error {
|
||||
ret := m.ctrl.Call(m, "Stop")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Stop indicates an expected call of Stop
|
||||
func (mr *MockJailCellMockRecorder) Stop() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockJailCell)(nil).Stop))
|
||||
}
|
||||
|
||||
// MockJailManager is a mock of JailManager interface
|
||||
type MockJailManager struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockJailManagerMockRecorder
|
||||
}
|
||||
|
||||
// MockJailManagerMockRecorder is the mock recorder for MockJailManager
|
||||
type MockJailManagerMockRecorder struct {
|
||||
mock *MockJailManager
|
||||
}
|
||||
|
||||
// NewMockJailManager creates a new mock instance
|
||||
func NewMockJailManager(ctrl *gomock.Controller) *MockJailManager {
|
||||
mock := &MockJailManager{ctrl: ctrl}
|
||||
mock.recorder = &MockJailManagerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockJailManager) EXPECT() *MockJailManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Call mocks base method
|
||||
func (m *MockJailManager) Call(chatID, this, args string) string {
|
||||
ret := m.ctrl.Call(m, "Call", chatID, this, args)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Call indicates an expected call of Call
|
||||
func (mr *MockJailManagerMockRecorder) Call(chatID, this, args interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockJailManager)(nil).Call), chatID, this, args)
|
||||
}
|
||||
|
||||
// CreateCell mocks base method
|
||||
func (m *MockJailManager) CreateCell(chatID string) (JailCell, error) {
|
||||
ret := m.ctrl.Call(m, "CreateCell", chatID)
|
||||
ret0, _ := ret[0].(JailCell)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// CreateCell indicates an expected call of CreateCell
|
||||
func (mr *MockJailManagerMockRecorder) CreateCell(chatID interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCell", reflect.TypeOf((*MockJailManager)(nil).CreateCell), chatID)
|
||||
}
|
||||
|
||||
// Parse mocks base method
|
||||
func (m *MockJailManager) Parse(chatID, js string) string {
|
||||
ret := m.ctrl.Call(m, "Parse", chatID, js)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Parse indicates an expected call of Parse
|
||||
func (mr *MockJailManagerMockRecorder) Parse(chatID, js interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockJailManager)(nil).Parse), chatID, js)
|
||||
}
|
||||
|
||||
// CreateAndInitCell mocks base method
|
||||
func (m *MockJailManager) CreateAndInitCell(chatID string, code ...string) string {
|
||||
varargs := []interface{}{chatID}
|
||||
for _, a := range code {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "CreateAndInitCell", varargs...)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateAndInitCell indicates an expected call of CreateAndInitCell
|
||||
func (mr *MockJailManagerMockRecorder) CreateAndInitCell(chatID interface{}, code ...interface{}) *gomock.Call {
|
||||
varargs := append([]interface{}{chatID}, code...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAndInitCell", reflect.TypeOf((*MockJailManager)(nil).CreateAndInitCell), varargs...)
|
||||
}
|
||||
|
||||
// Cell mocks base method
|
||||
func (m *MockJailManager) Cell(chatID string) (JailCell, error) {
|
||||
ret := m.ctrl.Call(m, "Cell", chatID)
|
||||
ret0, _ := ret[0].(JailCell)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Cell indicates an expected call of Cell
|
||||
func (mr *MockJailManagerMockRecorder) Cell(chatID interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cell", reflect.TypeOf((*MockJailManager)(nil).Cell), chatID)
|
||||
}
|
||||
|
||||
// Execute mocks base method
|
||||
func (m *MockJailManager) Execute(chatID, code string) string {
|
||||
ret := m.ctrl.Call(m, "Execute", chatID, code)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Execute indicates an expected call of Execute
|
||||
func (mr *MockJailManagerMockRecorder) Execute(chatID, code interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockJailManager)(nil).Execute), chatID, code)
|
||||
}
|
||||
|
||||
// SetBaseJS mocks base method
|
||||
func (m *MockJailManager) SetBaseJS(js string) {
|
||||
m.ctrl.Call(m, "SetBaseJS", js)
|
||||
}
|
||||
|
||||
// SetBaseJS indicates an expected call of SetBaseJS
|
||||
func (mr *MockJailManagerMockRecorder) SetBaseJS(js interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBaseJS", reflect.TypeOf((*MockJailManager)(nil).SetBaseJS), js)
|
||||
}
|
||||
|
||||
// Stop mocks base method
|
||||
func (m *MockJailManager) Stop() {
|
||||
m.ctrl.Call(m, "Stop")
|
||||
}
|
||||
|
||||
// Stop indicates an expected call of Stop
|
||||
func (mr *MockJailManagerMockRecorder) Stop() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockJailManager)(nil).Stop))
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/static"
|
||||
)
|
||||
|
||||
const (
|
||||
// MessageIDKey is a key for message ID
|
||||
// This ID is required to track from which chat a given send transaction request is coming.
|
||||
MessageIDKey = contextKey("message_id")
|
||||
)
|
||||
|
||||
type contextKey string // in order to make sure that our context key does not collide with keys from other packages
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrInvalidAccountAddressOrKey = errors.New("cannot parse address or key to valid account address")
|
||||
)
|
||||
|
||||
// ParseAccountString parses hex encoded string and returns is as accounts.Account.
|
||||
func ParseAccountString(account string) (accounts.Account, error) {
|
||||
// valid address, convert to account
|
||||
if common.IsHexAddress(account) {
|
||||
return accounts.Account{Address: common.HexToAddress(account)}, nil
|
||||
}
|
||||
|
||||
return accounts.Account{}, ErrInvalidAccountAddressOrKey
|
||||
}
|
||||
|
||||
// FromAddress converts account address from string to common.Address.
|
||||
// The function is useful to format "From" field of send transaction struct.
|
||||
func FromAddress(accountAddress string) common.Address {
|
||||
from, err := ParseAccountString(accountAddress)
|
||||
if err != nil {
|
||||
return common.Address{}
|
||||
}
|
||||
|
||||
return from.Address
|
||||
}
|
||||
|
||||
// ToAddress converts account address from string to *common.Address.
|
||||
// The function is useful to format "To" field of send transaction struct.
|
||||
func ToAddress(accountAddress string) *common.Address {
|
||||
to, err := ParseAccountString(accountAddress)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &to.Address
|
||||
}
|
||||
|
||||
// ImportTestAccount imports keystore from static resources, see "static/keys" folder
|
||||
func ImportTestAccount(keystoreDir, accountFile string) error {
|
||||
// make sure that keystore folder exists
|
||||
if _, err := os.Stat(keystoreDir); os.IsNotExist(err) {
|
||||
os.MkdirAll(keystoreDir, os.ModePerm) // nolint: errcheck, gas
|
||||
}
|
||||
|
||||
dst := filepath.Join(keystoreDir, accountFile)
|
||||
err := ioutil.WriteFile(dst, static.MustAsset("keys/"+accountFile), 0644)
|
||||
if err != nil {
|
||||
log.Warn("cannot copy test account PK", "error", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// PanicAfter throws panic() after waitSeconds, unless abort channel receives notification
|
||||
func PanicAfter(waitSeconds time.Duration, abort chan struct{}, desc string) {
|
||||
go func() {
|
||||
select {
|
||||
case <-abort:
|
||||
return
|
||||
case <-time.After(waitSeconds):
|
||||
panic("whatever you were doing takes toooo long: " + desc)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// MessageIDFromContext returns message id from context (if exists)
|
||||
func MessageIDFromContext(ctx context.Context) string {
|
||||
if ctx == nil {
|
||||
return ""
|
||||
}
|
||||
if messageID, ok := ctx.Value(MessageIDKey).(string); ok {
|
||||
return messageID
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// ParseJSONArray parses JSON array into Go array of string
|
||||
func ParseJSONArray(items string) ([]string, error) {
|
||||
var parsedItems []string
|
||||
err := json.Unmarshal([]byte(items), &parsedItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parsedItems, nil
|
||||
}
|
||||
|
||||
// Fatalf is used to halt the execution.
|
||||
// When called the function prints stack end exits.
|
||||
// Failure is logged into both StdErr and StdOut.
|
||||
func Fatalf(reason interface{}, args ...interface{}) {
|
||||
// decide on output stream
|
||||
w := io.MultiWriter(os.Stdout, os.Stderr)
|
||||
outf, _ := os.Stdout.Stat() // nolint: gas
|
||||
errf, _ := os.Stderr.Stat() // nolint: gas
|
||||
if outf != nil && errf != nil && os.SameFile(outf, errf) {
|
||||
w = os.Stderr
|
||||
}
|
||||
|
||||
// find out whether error or string has been passed as a reason
|
||||
r := reflect.ValueOf(reason)
|
||||
if r.Kind() == reflect.String {
|
||||
fmt.Fprintf(w, "Fatal Failure: %v\n%v\n", reason.(string), args)
|
||||
} else {
|
||||
fmt.Fprintf(w, "Fatal Failure: %v\n", reason.(error))
|
||||
}
|
||||
|
||||
debug.PrintStack()
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// CreateTransaction returns a transaction object.
|
||||
func CreateTransaction(ctx context.Context, args SendTxArgs) *QueuedTx {
|
||||
return &QueuedTx{
|
||||
ID: QueuedTxID(uuid.New()),
|
||||
Context: ctx,
|
||||
Args: args,
|
||||
Result: make(chan TransactionResult, 1),
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
# log [![GoDoc](https://godoc.org/github.com/status-im/status-go/geth/log?status.png)](https://godoc.org/github.com/status-im/status-go/geth/log)
|
||||
Package log implements logger for status-go.
|
||||
|
||||
Download:
|
||||
```shell
|
||||
go get github.com/status-im/status-go/geth/log
|
||||
```
|
||||
|
||||
* * *
|
||||
Package log implements logger for status-go.
|
||||
|
||||
This logger handles two loggers - it's own and ethereum-go logger.
|
||||
Both are used as "singletons" - using global shared variables.
|
||||
|
||||
## Usage
|
||||
First, import package into your code:
|
||||
|
||||
```
|
||||
import "github.com/status-im/status-go/geth/log
|
||||
```
|
||||
|
||||
Then simply use `Info/Error/Debug/etc` functions to log at desired level:
|
||||
|
||||
```
|
||||
log.Info("Info message")
|
||||
log.Debug("Debug message")
|
||||
log.Error("Error message")
|
||||
```
|
||||
|
||||
Slightly more complicated logging:
|
||||
|
||||
```
|
||||
log.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate)
|
||||
```
|
||||
|
||||
Note, in this case parameters should be in in pairs (key, value).
|
||||
|
||||
This logger is based upon log15-logger, so see its documentation for advanced usage: https://github.com/inconshreveable/log15
|
||||
|
||||
## Initialization
|
||||
By default logger is set to log to stdout with Error level via `init()` function.
|
||||
You may change both level and file output by `log.SetLevel()` and `log.SetLogFile()` functions:
|
||||
|
||||
```
|
||||
log.SetLevel("DEBUG")
|
||||
log.SetLogFile("/path/to/geth.log")
|
||||
```
|
||||
|
||||
|
||||
|
||||
* * *
|
||||
Automatically generated by [autoreadme](https://github.com/jimmyfrasche/autoreadme) on 2017.09.15
|
|
@ -1,136 +0,0 @@
|
|||
/*Package log implements logger for status-go.
|
||||
|
||||
This logger handles two loggers - it's own and ethereum-go logger.
|
||||
Both are used as "singletons" - using global shared variables.
|
||||
|
||||
Usage
|
||||
|
||||
First, import package into your code:
|
||||
|
||||
import "github.com/status-im/status-go/geth/log
|
||||
|
||||
Then simply use `Info/Error/Debug/etc` functions to log at desired level:
|
||||
|
||||
log.Info("Info message")
|
||||
log.Debug("Debug message")
|
||||
log.Error("Error message")
|
||||
|
||||
Slightly more complicated logging:
|
||||
|
||||
log.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate)
|
||||
|
||||
Note, in this case parameters should be in in pairs (key, value).
|
||||
|
||||
This logger is based upon log15-logger, so see its documentation for advanced usage: https://github.com/inconshreveable/log15
|
||||
|
||||
|
||||
Initialization
|
||||
|
||||
By default logger is set to log to stdout with Error level via `init()` function.
|
||||
You may change both level and file output by `log.SetLevel()` and `log.SetLogFile()` functions:
|
||||
|
||||
log.SetLevel("DEBUG")
|
||||
log.SetLogFile("/path/to/geth.log")
|
||||
|
||||
*/
|
||||
package log
|
||||
|
||||
//go:generate autoreadme -f
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// Logger is a wrapper around log.Logger.
|
||||
type Logger struct {
|
||||
log.Logger
|
||||
level log.Lvl
|
||||
handler log.Handler
|
||||
}
|
||||
|
||||
// logger is package scope instance of Logger
|
||||
var logger = Logger{
|
||||
Logger: log.New("geth", "StatusIM"),
|
||||
level: log.LvlError,
|
||||
handler: log.StreamHandler(os.Stdout, log.TerminalFormat(true)),
|
||||
}
|
||||
|
||||
func init() {
|
||||
setHandler(logger.level, logger.handler)
|
||||
}
|
||||
|
||||
// SetLevel inits status and ethereum-go logging packages,
|
||||
// enabling logging and setting up proper log level.
|
||||
//
|
||||
// Our log levels are in form "DEBUG|ERROR|WARN|etc", while
|
||||
// ethereum-go expects names in lower case: "debug|error|warn|etc".
|
||||
func SetLevel(level string) {
|
||||
lvl := levelFromString(level)
|
||||
|
||||
logger.level = lvl
|
||||
setHandler(lvl, logger.handler)
|
||||
}
|
||||
|
||||
// SetLogFile configures logger to write output into file.
|
||||
// This call preserves current logging level.
|
||||
func SetLogFile(filename string) error {
|
||||
handler, err := log.FileHandler(filename, log.TerminalFormat(false))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.handler = handler
|
||||
setHandler(logger.level, handler)
|
||||
return nil
|
||||
}
|
||||
|
||||
func levelFromString(level string) log.Lvl {
|
||||
lvl, err := log.LvlFromString(strings.ToLower(level))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Incorrect log level: %s, using defaults\n", level)
|
||||
lvl = log.LvlInfo
|
||||
}
|
||||
return lvl
|
||||
}
|
||||
|
||||
// setHandler is a helper that allows log (re)initialization
|
||||
// with different level and handler. Useful for testing.
|
||||
func setHandler(lvl log.Lvl, handler log.Handler) {
|
||||
h := log.LvlFilterHandler(lvl, handler)
|
||||
logger.SetHandler(h)
|
||||
log.Root().SetHandler(h) // ethereum-go logger
|
||||
}
|
||||
|
||||
// Trace is a package scope alias for logger.Trace
|
||||
func Trace(msg string, ctx ...interface{}) {
|
||||
logger.Trace(msg, ctx...)
|
||||
}
|
||||
|
||||
// Debug is a package scope for logger.Debug
|
||||
func Debug(msg string, ctx ...interface{}) {
|
||||
logger.Debug(msg, ctx...)
|
||||
}
|
||||
|
||||
// Info is a package scope for logger.Info
|
||||
func Info(msg string, ctx ...interface{}) {
|
||||
logger.Info(msg, ctx...)
|
||||
}
|
||||
|
||||
// Warn is a package scope for logger.Warn
|
||||
func Warn(msg string, ctx ...interface{}) {
|
||||
logger.Warn(msg, ctx...)
|
||||
}
|
||||
|
||||
// Error is a package scope for logger.Error
|
||||
func Error(msg string, ctx ...interface{}) {
|
||||
logger.Error(msg, ctx...)
|
||||
}
|
||||
|
||||
// Crit is a package scope for logger.Crit
|
||||
func Crit(msg string, ctx ...interface{}) {
|
||||
logger.Crit(msg, ctx...)
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
// ServiceProvider provides node and required services.
|
||||
type ServiceProvider interface {
|
||||
Node() (*node.Node, error)
|
||||
GethNode() (*node.Node, error)
|
||||
WhisperService() (*whisper.Whisper, error)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,11 +29,15 @@ var (
|
|||
// PublicAPI defines a MailServer public API.
|
||||
type PublicAPI struct {
|
||||
provider ServiceProvider
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewPublicAPI returns a new PublicAPI.
|
||||
func NewPublicAPI(provider ServiceProvider) *PublicAPI {
|
||||
return &PublicAPI{provider}
|
||||
return &PublicAPI{
|
||||
provider: provider,
|
||||
log: log.New("package", "status-go/geth/mailservice.PublicAPI"),
|
||||
}
|
||||
}
|
||||
|
||||
// MessagesRequest is a payload send to a MailServer to get messages.
|
||||
|
@ -66,7 +71,7 @@ func setMessagesRequestDefaults(r *MessagesRequest) {
|
|||
|
||||
// RequestMessages sends a request for historic messages to a MailServer.
|
||||
func (api *PublicAPI) RequestMessages(_ context.Context, r MessagesRequest) (bool, error) {
|
||||
log.Info("RequestMessages", "request", r)
|
||||
api.log.Info("RequestMessages", "request", r)
|
||||
|
||||
setMessagesRequestDefaults(&r)
|
||||
|
||||
|
@ -75,7 +80,7 @@ func (api *PublicAPI) RequestMessages(_ context.Context, r MessagesRequest) (boo
|
|||
return false, err
|
||||
}
|
||||
|
||||
node, err := api.provider.Node()
|
||||
node, err := api.provider.GethNode()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: geth/mailservice/mailservice.go
|
||||
// Source: github.com/status-im/status-go/geth/mailservice (interfaces: ServiceProvider)
|
||||
|
||||
// Package mailservice is a generated GoMock package.
|
||||
package mailservice
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
node "github.com/ethereum/go-ethereum/node"
|
||||
whisperv6 "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockServiceProvider is a mock of ServiceProvider interface
|
||||
|
@ -34,17 +35,17 @@ func (m *MockServiceProvider) EXPECT() *MockServiceProviderMockRecorder {
|
|||
return m.recorder
|
||||
}
|
||||
|
||||
// Node mocks base method
|
||||
func (m *MockServiceProvider) Node() (*node.Node, error) {
|
||||
ret := m.ctrl.Call(m, "Node")
|
||||
// GethNode mocks base method
|
||||
func (m *MockServiceProvider) GethNode() (*node.Node, error) {
|
||||
ret := m.ctrl.Call(m, "GethNode")
|
||||
ret0, _ := ret[0].(*node.Node)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Node indicates an expected call of Node
|
||||
func (mr *MockServiceProviderMockRecorder) Node() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Node", reflect.TypeOf((*MockServiceProvider)(nil).Node))
|
||||
// GethNode indicates an expected call of Node
|
||||
func (mr *MockServiceProviderMockRecorder) GethNode() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GethNode", reflect.TypeOf((*MockServiceProvider)(nil).GethNode))
|
||||
}
|
||||
|
||||
// WhisperService mocks base method
|
||||
|
|
|
@ -1,453 +0,0 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/mailservice"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrNodeExists = errors.New("node is already running")
|
||||
ErrNoRunningNode = errors.New("there is no running node")
|
||||
ErrInvalidNodeManager = errors.New("node manager is not properly initialized")
|
||||
ErrInvalidWhisperService = errors.New("whisper service is unavailable")
|
||||
ErrInvalidLightEthereumService = errors.New("LES service is unavailable")
|
||||
ErrInvalidAccountManager = errors.New("could not retrieve account manager")
|
||||
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
||||
ErrRPCClient = errors.New("failed to init RPC client")
|
||||
)
|
||||
|
||||
// RPCClientError reported when rpc client is initialized.
|
||||
type RPCClientError error
|
||||
|
||||
// EthNodeError is reported when node crashed on start up.
|
||||
type EthNodeError error
|
||||
|
||||
// NodeManager manages Status node (which abstracts contained geth node)
|
||||
// nolint: golint
|
||||
// should be fixed at https://github.com/status-im/status-go/issues/200
|
||||
type NodeManager struct {
|
||||
mu sync.RWMutex
|
||||
config *params.NodeConfig // Status node configuration
|
||||
node *node.Node // reference to Geth P2P stack/node
|
||||
|
||||
whisperService *whisper.Whisper // reference to Whisper service
|
||||
lesService *les.LightEthereum // reference to LES service
|
||||
rpcClient *rpc.Client // reference to RPC client
|
||||
}
|
||||
|
||||
// NewNodeManager makes new instance of node manager
|
||||
func NewNodeManager() *NodeManager {
|
||||
return &NodeManager{}
|
||||
}
|
||||
|
||||
// StartNode start Status node, fails if node is already started
|
||||
func (m *NodeManager) StartNode(config *params.NodeConfig) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.startNode(config)
|
||||
}
|
||||
|
||||
// startNode start Status node, fails if node is already started
|
||||
func (m *NodeManager) startNode(config *params.NodeConfig) error {
|
||||
if err := m.isNodeAvailable(); err == nil {
|
||||
return ErrNodeExists
|
||||
}
|
||||
m.initLog(config)
|
||||
|
||||
ethNode, err := MakeNode(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.node = ethNode
|
||||
m.config = config
|
||||
|
||||
// activate MailService required for Offline Inboxing
|
||||
if err := ethNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
|
||||
return mailservice.New(m), nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start underlying node
|
||||
if err := ethNode.Start(); err != nil {
|
||||
return EthNodeError(err)
|
||||
}
|
||||
// init RPC client for this node
|
||||
localRPCClient, err := m.node.Attach()
|
||||
if err == nil {
|
||||
m.rpcClient, err = rpc.NewClient(localRPCClient, m.config.UpstreamConfig)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Failed to create an RPC client", "error", err)
|
||||
return RPCClientError(err)
|
||||
}
|
||||
// populate static peers exits when node stopped
|
||||
go func() {
|
||||
if err := m.PopulateStaticPeers(); err != nil {
|
||||
log.Error("Static peers population", "error", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopNode stop Status node. Stopped node cannot be resumed.
|
||||
func (m *NodeManager) StopNode() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.stopNode()
|
||||
}
|
||||
|
||||
// stopNode stop Status node. Stopped node cannot be resumed.
|
||||
func (m *NodeManager) stopNode() error {
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.node.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
m.node = nil
|
||||
m.config = nil
|
||||
m.lesService = nil
|
||||
m.whisperService = nil
|
||||
m.rpcClient = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetChainData removes chain data if node is not running.
|
||||
func (m *NodeManager) ResetChainData(config *params.NodeConfig) error {
|
||||
if m.IsNodeRunning() {
|
||||
return ErrNodeExists
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
chainDataDir := filepath.Join(config.DataDir, config.Name, "lightchaindata")
|
||||
if _, err := os.Stat(chainDataDir); os.IsNotExist(err) {
|
||||
// is it really an error, if we want to remove it as next step?
|
||||
return err
|
||||
}
|
||||
err := os.RemoveAll(chainDataDir)
|
||||
if err == nil {
|
||||
log.Info("Chain data has been removed", "dir", chainDataDir)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// IsNodeRunning confirm that node is running
|
||||
func (m *NodeManager) IsNodeRunning() bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Node returns underlying Status node
|
||||
func (m *NodeManager) Node() (*node.Node, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.node, nil
|
||||
}
|
||||
|
||||
// PopulateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
||||
func (m *NodeManager) PopulateStaticPeers() error {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.populateStaticPeers()
|
||||
}
|
||||
|
||||
// populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
||||
func (m *NodeManager) populateStaticPeers() error {
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !m.config.BootClusterConfig.Enabled {
|
||||
log.Info("Boot cluster is disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, enode := range m.config.BootClusterConfig.BootNodes {
|
||||
err := m.addPeer(enode)
|
||||
if err != nil {
|
||||
log.Warn("Boot node addition failed", "error", err)
|
||||
continue
|
||||
}
|
||||
log.Info("Boot node added", "enode", enode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) removeStaticPeers() error {
|
||||
if !m.config.BootClusterConfig.Enabled {
|
||||
log.Info("Boot cluster is disabled")
|
||||
return nil
|
||||
}
|
||||
server := m.node.Server()
|
||||
if server == nil {
|
||||
return ErrNoRunningNode
|
||||
}
|
||||
for _, enode := range m.config.BootClusterConfig.BootNodes {
|
||||
err := m.removePeer(enode)
|
||||
if err != nil {
|
||||
log.Warn("Boot node deletion failed", "error", err)
|
||||
return err
|
||||
}
|
||||
log.Info("Boot node deleted", "enode", enode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReconnectStaticPeers removes and adds static peers to a server.
|
||||
func (m *NodeManager) ReconnectStaticPeers() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if err := m.removeStaticPeers(); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.populateStaticPeers()
|
||||
}
|
||||
|
||||
// AddPeer adds new static peer node
|
||||
func (m *NodeManager) AddPeer(url string) error {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.addPeer(url)
|
||||
}
|
||||
|
||||
// addPeer adds new static peer node
|
||||
func (m *NodeManager) addPeer(url string) error {
|
||||
// Try to add the url as a static peer and return
|
||||
parsedNode, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.node.Server().AddPeer(parsedNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) removePeer(url string) error {
|
||||
parsedNode, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.node.Server().RemovePeer(parsedNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PeerCount returns the number of connected peers.
|
||||
func (m *NodeManager) PeerCount() int {
|
||||
if !m.IsNodeRunning() {
|
||||
return 0
|
||||
}
|
||||
return m.node.Server().PeerCount()
|
||||
}
|
||||
|
||||
// NodeConfig exposes reference to running node's configuration
|
||||
func (m *NodeManager) NodeConfig() (*params.NodeConfig, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.config, nil
|
||||
}
|
||||
|
||||
// LightEthereumService exposes reference to LES service running on top of the node
|
||||
func (m *NodeManager) LightEthereumService() (*les.LightEthereum, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if m.lesService == nil {
|
||||
if err := m.node.Service(&m.lesService); err != nil {
|
||||
log.Warn("Cannot obtain LES service", "error", err)
|
||||
return nil, ErrInvalidLightEthereumService
|
||||
}
|
||||
}
|
||||
if m.lesService == nil {
|
||||
return nil, ErrInvalidLightEthereumService
|
||||
}
|
||||
return m.lesService, nil
|
||||
}
|
||||
|
||||
// WhisperService exposes reference to Whisper service running on top of the node
|
||||
func (m *NodeManager) WhisperService() (*whisper.Whisper, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if m.whisperService == nil {
|
||||
if err := m.node.Service(&m.whisperService); err != nil {
|
||||
log.Warn("Cannot obtain whisper service", "error", err)
|
||||
return nil, ErrInvalidWhisperService
|
||||
}
|
||||
}
|
||||
if m.whisperService == nil {
|
||||
return nil, ErrInvalidWhisperService
|
||||
}
|
||||
return m.whisperService, nil
|
||||
}
|
||||
|
||||
// AccountManager exposes reference to node's accounts manager
|
||||
func (m *NodeManager) AccountManager() (*accounts.Manager, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountManager := m.node.AccountManager()
|
||||
if accountManager == nil {
|
||||
return nil, ErrInvalidAccountManager
|
||||
}
|
||||
return accountManager, nil
|
||||
}
|
||||
|
||||
// AccountKeyStore exposes reference to accounts key store
|
||||
func (m *NodeManager) AccountKeyStore() (*keystore.KeyStore, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if err := m.isNodeAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountManager := m.node.AccountManager()
|
||||
if accountManager == nil {
|
||||
return nil, ErrInvalidAccountManager
|
||||
}
|
||||
|
||||
backends := accountManager.Backends(keystore.KeyStoreType)
|
||||
if len(backends) == 0 {
|
||||
return nil, ErrAccountKeyStoreMissing
|
||||
}
|
||||
|
||||
keyStore, ok := backends[0].(*keystore.KeyStore)
|
||||
if !ok {
|
||||
return nil, ErrAccountKeyStoreMissing
|
||||
}
|
||||
|
||||
return keyStore, nil
|
||||
}
|
||||
|
||||
// RPCClient exposes reference to RPC client connected to the running node.
|
||||
func (m *NodeManager) RPCClient() *rpc.Client {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.rpcClient
|
||||
}
|
||||
|
||||
// initLog initializes global logger parameters based on
|
||||
// provided node configurations.
|
||||
func (m *NodeManager) initLog(config *params.NodeConfig) {
|
||||
log.SetLevel(config.LogLevel)
|
||||
|
||||
if config.LogFile != "" {
|
||||
err := log.SetLogFile(config.LogFile)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to open log file, using stdout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isNodeAvailable check if we have a node running and make sure is fully started
|
||||
func (m *NodeManager) isNodeAvailable() error {
|
||||
if m.node == nil || m.node.Server() == nil {
|
||||
return ErrNoRunningNode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// tickerResolution is the delta to check blockchain sync progress.
|
||||
const tickerResolution = time.Second
|
||||
|
||||
// EnsureSync waits until blockchain synchronization
|
||||
// is complete and returns.
|
||||
func (m *NodeManager) EnsureSync(ctx context.Context) error {
|
||||
// Don't wait for any blockchain sync for the
|
||||
// local private chain as blocks are never mined.
|
||||
if m.config.NetworkID == params.StatusChainNetworkID {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.ensureSync(ctx)
|
||||
}
|
||||
|
||||
func (m *NodeManager) ensureSync(ctx context.Context) error {
|
||||
les, err := m.LightEthereumService()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get LES service: %v", err)
|
||||
}
|
||||
|
||||
downloader := les.Downloader()
|
||||
if downloader == nil {
|
||||
return errors.New("LightEthereumService downloader is nil")
|
||||
}
|
||||
|
||||
progress := downloader.Progress()
|
||||
if m.PeerCount() > 0 && progress.CurrentBlock >= progress.HighestBlock {
|
||||
log.Debug("Synchronization completed", "current block", progress.CurrentBlock, "highest block", progress.HighestBlock)
|
||||
return nil
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(tickerResolution)
|
||||
defer ticker.Stop()
|
||||
|
||||
progressTicker := time.NewTicker(time.Minute)
|
||||
defer progressTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.New("timeout during node synchronization")
|
||||
case <-ticker.C:
|
||||
if m.PeerCount() == 0 {
|
||||
log.Debug("No established connections with any peers, continue waiting for a sync")
|
||||
continue
|
||||
}
|
||||
if downloader.Synchronising() {
|
||||
log.Debug("Synchronization is in progress")
|
||||
continue
|
||||
}
|
||||
progress = downloader.Progress()
|
||||
if progress.CurrentBlock >= progress.HighestBlock {
|
||||
log.Info("Synchronization completed", "current block", progress.CurrentBlock, "highest block", progress.HighestBlock)
|
||||
return nil
|
||||
}
|
||||
log.Debug("Synchronization is not finished", "current", progress.CurrentBlock, "highest", progress.HighestBlock)
|
||||
case <-progressTicker.C:
|
||||
progress = downloader.Progress()
|
||||
log.Warn("Synchronization is not finished", "current", progress.CurrentBlock, "highest", progress.HighestBlock)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,13 +14,13 @@ import (
|
|||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/whisper/mailserver"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
shhmetrics "github.com/status-im/status-go/metrics/whisper"
|
||||
)
|
||||
|
@ -35,6 +35,9 @@ var (
|
|||
ErrNodeStartFailure = errors.New("error starting p2p node")
|
||||
)
|
||||
|
||||
// All general log messages in this package should be routed through this logger.
|
||||
var logger = log.New("package", "status-go/geth/node")
|
||||
|
||||
// MakeNode create a geth node entity
|
||||
func MakeNode(config *params.NodeConfig) (*node.Node, error) {
|
||||
// make sure data directory exists
|
||||
|
@ -51,10 +54,10 @@ func MakeNode(config *params.NodeConfig) (*node.Node, error) {
|
|||
stackConfig := defaultEmbeddedNodeConfig(config)
|
||||
|
||||
if len(config.NodeKeyFile) > 0 {
|
||||
log.Info("Loading private key file", "file", config.NodeKeyFile)
|
||||
logger.Info("Loading private key file", "file", config.NodeKeyFile)
|
||||
pk, err := crypto.LoadECDSA(config.NodeKeyFile)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed loading private key file '%s': %v", config.NodeKeyFile, err))
|
||||
logger.Error("Failed loading private key file", "file", config.NodeKeyFile, "error", err)
|
||||
}
|
||||
|
||||
// override node's private key
|
||||
|
@ -92,7 +95,7 @@ func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
|
|||
Version: config.Version,
|
||||
P2P: p2p.Config{
|
||||
NoDiscovery: !config.Discovery,
|
||||
DiscoveryV5: true,
|
||||
DiscoveryV5: config.Discovery,
|
||||
BootstrapNodes: nil,
|
||||
BootstrapNodesV5: nil,
|
||||
ListenAddr: config.ListenAddr,
|
||||
|
@ -115,8 +118,9 @@ func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
|
|||
nc.HTTPPort = config.HTTPPort
|
||||
}
|
||||
|
||||
if config.BootClusterConfig.Enabled {
|
||||
nc.P2P.BootstrapNodes = makeBootstrapNodes(config.BootClusterConfig.BootNodes)
|
||||
if config.ClusterConfig.Enabled {
|
||||
nc.P2P.StaticNodes = parseNodes(config.ClusterConfig.StaticNodes)
|
||||
nc.P2P.BootstrapNodes = parseNodes(config.ClusterConfig.BootNodes)
|
||||
}
|
||||
|
||||
return nc
|
||||
|
@ -125,7 +129,7 @@ func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
|
|||
// activateEthService configures and registers the eth.Ethereum service with a given node.
|
||||
func activateEthService(stack *node.Node, config *params.NodeConfig) error {
|
||||
if !config.LightEthConfig.Enabled {
|
||||
log.Info("LES protocol is disabled")
|
||||
logger.Info("LES protocol is disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -154,8 +158,9 @@ func activateEthService(stack *node.Node, config *params.NodeConfig) error {
|
|||
|
||||
// activateShhService configures Whisper and adds it to the given node.
|
||||
func activateShhService(stack *node.Node, config *params.NodeConfig) error {
|
||||
|
||||
if !config.WhisperConfig.Enabled {
|
||||
log.Info("SHH protocol is disabled")
|
||||
logger.Info("SHH protocol is disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -178,7 +183,7 @@ func activateShhService(stack *node.Node, config *params.NodeConfig) error {
|
|||
}
|
||||
}
|
||||
|
||||
log.Info("Register MailServer")
|
||||
logger.Info("Register MailServer")
|
||||
|
||||
var mailServer mailserver.WMailServer
|
||||
whisperService.RegisterServer(&mailServer)
|
||||
|
@ -216,12 +221,11 @@ func makeWSHost(config *params.NodeConfig) string {
|
|||
return config.WSHost
|
||||
}
|
||||
|
||||
// makeBootstrapNodes returns default (hence bootstrap) list of peers
|
||||
func makeBootstrapNodes(enodes []string) []*discover.Node {
|
||||
var bootstrapNodes []*discover.Node
|
||||
for _, enode := range enodes {
|
||||
bootstrapNodes = append(bootstrapNodes, discover.MustParseNode(enode))
|
||||
// parseNodes creates list of discover.Node out of enode strings.
|
||||
func parseNodes(enodes []string) []*discover.Node {
|
||||
nodes := make([]*discover.Node, len(enodes))
|
||||
for i, enode := range enodes {
|
||||
nodes[i] = discover.MustParseNode(enode)
|
||||
}
|
||||
|
||||
return bootstrapNodes
|
||||
return nodes
|
||||
}
|
||||
|
|
|
@ -0,0 +1,408 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
|
||||
"github.com/status-im/status-go/geth/mailservice"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrNodeExists = errors.New("node is already running")
|
||||
ErrNoRunningNode = errors.New("there is no running node")
|
||||
ErrInvalidStatusNode = errors.New("status node is not properly initialized")
|
||||
ErrInvalidService = errors.New("service is unavailable")
|
||||
ErrInvalidAccountManager = errors.New("could not retrieve account manager")
|
||||
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
||||
ErrRPCClient = errors.New("failed to init RPC client")
|
||||
)
|
||||
|
||||
// RPCClientError reported when rpc client is initialized.
|
||||
type RPCClientError error
|
||||
|
||||
// EthNodeError is reported when node crashed on start up.
|
||||
type EthNodeError error
|
||||
|
||||
// StatusNode abstracts contained geth node and provides helper methods to
|
||||
// interact with it.
|
||||
type StatusNode struct {
|
||||
mu sync.RWMutex
|
||||
config *params.NodeConfig // Status node configuration
|
||||
gethNode *node.Node // reference to Geth P2P stack/node
|
||||
|
||||
rpcClient *rpc.Client // reference to RPC client
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// New makes new instance of StatusNode.
|
||||
func New() *StatusNode {
|
||||
return &StatusNode{
|
||||
log: log.New("package", "status-go/geth/node.StatusNode"),
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts current StatusNode, will fail if it's already started.
|
||||
func (n *StatusNode) Start(config *params.NodeConfig) error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
return n.start(config)
|
||||
}
|
||||
|
||||
// start starts current StatusNode, will fail if it's already started.
|
||||
func (n *StatusNode) start(config *params.NodeConfig) error {
|
||||
if err := n.isAvailable(); err == nil {
|
||||
return ErrNodeExists
|
||||
}
|
||||
|
||||
ethNode, err := MakeNode(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.gethNode = ethNode
|
||||
n.config = config
|
||||
|
||||
// activate MailService required for Offline Inboxing
|
||||
if err := ethNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
|
||||
return mailservice.New(n), nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start underlying node
|
||||
if err := ethNode.Start(); err != nil {
|
||||
return EthNodeError(err)
|
||||
}
|
||||
// init RPC client for this node
|
||||
localRPCClient, err := n.gethNode.Attach()
|
||||
if err == nil {
|
||||
n.rpcClient, err = rpc.NewClient(localRPCClient, n.config.UpstreamConfig)
|
||||
}
|
||||
if err != nil {
|
||||
n.log.Error("Failed to create an RPC client", "error", err)
|
||||
return RPCClientError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop will stop current StatusNode. A stopped node cannot be resumed.
|
||||
func (n *StatusNode) Stop() error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
return n.stop()
|
||||
}
|
||||
|
||||
// stop will stop current StatusNode. A stopped node cannot be resumed.
|
||||
func (n *StatusNode) stop() error {
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := n.gethNode.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
n.gethNode = nil
|
||||
n.config = nil
|
||||
n.rpcClient = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetChainData removes chain data if node is not running.
|
||||
func (n *StatusNode) ResetChainData(config *params.NodeConfig) error {
|
||||
if n.IsRunning() {
|
||||
return ErrNodeExists
|
||||
}
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
chainDataDir := filepath.Join(config.DataDir, config.Name, "lightchaindata")
|
||||
if _, err := os.Stat(chainDataDir); os.IsNotExist(err) {
|
||||
// is it really an error, if we want to remove it as next step?
|
||||
return err
|
||||
}
|
||||
err := os.RemoveAll(chainDataDir)
|
||||
if err == nil {
|
||||
n.log.Info("Chain data has been removed", "dir", chainDataDir)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// IsRunning confirm that node is running.
|
||||
func (n *StatusNode) IsRunning() bool {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GethNode returns underlying geth node.
|
||||
func (n *StatusNode) GethNode() (*node.Node, error) {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n.gethNode, nil
|
||||
}
|
||||
|
||||
// populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
||||
func (n *StatusNode) populateStaticPeers() error {
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !n.config.ClusterConfig.Enabled {
|
||||
n.log.Info("Static peers are disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, enode := range n.config.ClusterConfig.StaticNodes {
|
||||
err := n.addPeer(enode)
|
||||
if err != nil {
|
||||
n.log.Warn("Static peer addition failed", "error", err)
|
||||
continue
|
||||
}
|
||||
n.log.Info("Static peer added", "enode", enode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *StatusNode) removeStaticPeers() error {
|
||||
if !n.config.ClusterConfig.Enabled {
|
||||
n.log.Info("Static peers are disabled")
|
||||
return nil
|
||||
}
|
||||
server := n.gethNode.Server()
|
||||
if server == nil {
|
||||
return ErrNoRunningNode
|
||||
}
|
||||
for _, enode := range n.config.ClusterConfig.StaticNodes {
|
||||
err := n.removePeer(enode)
|
||||
if err != nil {
|
||||
n.log.Warn("Static peer deletion failed", "error", err)
|
||||
return err
|
||||
}
|
||||
n.log.Info("Static peer deleted", "enode", enode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReconnectStaticPeers removes and adds static peers to a server.
|
||||
func (n *StatusNode) ReconnectStaticPeers() error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
if err := n.removeStaticPeers(); err != nil {
|
||||
return err
|
||||
}
|
||||
return n.populateStaticPeers()
|
||||
}
|
||||
|
||||
// AddPeer adds new static peer node
|
||||
func (n *StatusNode) AddPeer(url string) error {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return err
|
||||
}
|
||||
return n.addPeer(url)
|
||||
}
|
||||
|
||||
// addPeer adds new static peer node
|
||||
func (n *StatusNode) addPeer(url string) error {
|
||||
// Try to add the url as a static peer and return
|
||||
parsedNode, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.gethNode.Server().AddPeer(parsedNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *StatusNode) removePeer(url string) error {
|
||||
parsedNode, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.gethNode.Server().RemovePeer(parsedNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PeerCount returns the number of connected peers.
|
||||
func (n *StatusNode) PeerCount() int {
|
||||
if !n.IsRunning() {
|
||||
return 0
|
||||
}
|
||||
return n.gethNode.Server().PeerCount()
|
||||
}
|
||||
|
||||
// Config exposes reference to running node's configuration
|
||||
func (n *StatusNode) Config() (*params.NodeConfig, error) {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n.config, nil
|
||||
}
|
||||
|
||||
// gethService is a wrapper for gethNode.Service which retrieves a currently
|
||||
// running service registered of a specific type.
|
||||
func (n *StatusNode) gethService(serviceInstance interface{}, serviceName string) error {
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := n.gethNode.Service(serviceInstance); err != nil || serviceInstance == nil {
|
||||
n.log.Warn("Cannot obtain ", serviceName, " service", "error", err)
|
||||
return ErrInvalidService
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LightEthereumService exposes reference to LES service running on top of the node
|
||||
func (n *StatusNode) LightEthereumService() (l *les.LightEthereum, err error) {
|
||||
return l, n.gethService(&l, "LES")
|
||||
}
|
||||
|
||||
// WhisperService exposes reference to Whisper service running on top of the node
|
||||
func (n *StatusNode) WhisperService() (w *whisper.Whisper, err error) {
|
||||
return w, n.gethService(&w, "whisper")
|
||||
}
|
||||
|
||||
// AccountManager exposes reference to node's accounts manager
|
||||
func (n *StatusNode) AccountManager() (*accounts.Manager, error) {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountManager := n.gethNode.AccountManager()
|
||||
if accountManager == nil {
|
||||
return nil, ErrInvalidAccountManager
|
||||
}
|
||||
return accountManager, nil
|
||||
}
|
||||
|
||||
// AccountKeyStore exposes reference to accounts key store
|
||||
func (n *StatusNode) AccountKeyStore() (*keystore.KeyStore, error) {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if err := n.isAvailable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountManager := n.gethNode.AccountManager()
|
||||
if accountManager == nil {
|
||||
return nil, ErrInvalidAccountManager
|
||||
}
|
||||
|
||||
backends := accountManager.Backends(keystore.KeyStoreType)
|
||||
if len(backends) == 0 {
|
||||
return nil, ErrAccountKeyStoreMissing
|
||||
}
|
||||
|
||||
keyStore, ok := backends[0].(*keystore.KeyStore)
|
||||
if !ok {
|
||||
return nil, ErrAccountKeyStoreMissing
|
||||
}
|
||||
|
||||
return keyStore, nil
|
||||
}
|
||||
|
||||
// RPCClient exposes reference to RPC client connected to the running node.
|
||||
func (n *StatusNode) RPCClient() *rpc.Client {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
return n.rpcClient
|
||||
}
|
||||
|
||||
// isAvailable check if we have a node running and make sure is fully started
|
||||
func (n *StatusNode) isAvailable() error {
|
||||
if n.gethNode == nil || n.gethNode.Server() == nil {
|
||||
return ErrNoRunningNode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// tickerResolution is the delta to check blockchain sync progress.
|
||||
const tickerResolution = time.Second
|
||||
|
||||
// EnsureSync waits until blockchain synchronization
|
||||
// is complete and returns.
|
||||
func (n *StatusNode) EnsureSync(ctx context.Context) error {
|
||||
// Don't wait for any blockchain sync for the
|
||||
// local private chain as blocks are never mined.
|
||||
if n.config.NetworkID == params.StatusChainNetworkID {
|
||||
return nil
|
||||
}
|
||||
|
||||
return n.ensureSync(ctx)
|
||||
}
|
||||
|
||||
func (n *StatusNode) ensureSync(ctx context.Context) error {
|
||||
les, err := n.LightEthereumService()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get LES service: %v", err)
|
||||
}
|
||||
|
||||
downloader := les.Downloader()
|
||||
if downloader == nil {
|
||||
return errors.New("LightEthereumService downloader is nil")
|
||||
}
|
||||
|
||||
progress := downloader.Progress()
|
||||
if n.PeerCount() > 0 && progress.CurrentBlock >= progress.HighestBlock {
|
||||
n.log.Debug("Synchronization completed", "current block", progress.CurrentBlock, "highest block", progress.HighestBlock)
|
||||
return nil
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(tickerResolution)
|
||||
defer ticker.Stop()
|
||||
|
||||
progressTicker := time.NewTicker(time.Minute)
|
||||
defer progressTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.New("timeout during node synchronization")
|
||||
case <-ticker.C:
|
||||
if n.PeerCount() == 0 {
|
||||
n.log.Debug("No established connections with any peers, continue waiting for a sync")
|
||||
continue
|
||||
}
|
||||
if downloader.Synchronising() {
|
||||
n.log.Debug("Synchronization is in progress")
|
||||
continue
|
||||
}
|
||||
progress = downloader.Progress()
|
||||
if progress.CurrentBlock >= progress.HighestBlock {
|
||||
n.log.Info("Synchronization completed", "current block", progress.CurrentBlock, "highest block", progress.HighestBlock)
|
||||
return nil
|
||||
}
|
||||
n.log.Debug("Synchronization is not finished", "current", progress.CurrentBlock, "highest", progress.HighestBlock)
|
||||
case <-progressTicker.C:
|
||||
progress = downloader.Progress()
|
||||
n.log.Warn("Synchronization is not finished", "current", progress.CurrentBlock, "highest", progress.HighestBlock)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package params
|
||||
|
||||
type subClusterData struct {
|
||||
Number int `json:"number"`
|
||||
Hash string `json:"hash"`
|
||||
StaticNodes []string `json:"staticnodes"`
|
||||
}
|
||||
|
||||
type clusterData struct {
|
||||
NetworkID int `json:"networkID"`
|
||||
Prod subClusterData `json:"prod"`
|
||||
Dev subClusterData `json:"dev"`
|
||||
}
|
||||
|
||||
var ropstenCluster = clusterData{
|
||||
NetworkID: 3,
|
||||
Prod: subClusterData{StaticNodes: []string{
|
||||
"enode://dffef3874011709b12d1e540d83ddb19a9db8614ad9151d05bcf813585e45cbebba5aaea223fe315786c401d8cecb1ad2de9f179680c536ea30311fb21fa934b@188.166.100.178:30303",
|
||||
"enode://03f3661686d30509d621dbe5ee2e3082923f25e94fd41a2dd8dd34bb12a0c4e8fbde52247c6c55e86dc209a8e7c4a5ae56058c65f7b01734d3ab73818b44e2a3@188.166.33.47:30303",
|
||||
}},
|
||||
Dev: subClusterData{StaticNodes: []string{
|
||||
"enode://dffef3874011709b12d1e540d83ddb19a9db8614ad9151d05bcf813585e45cbebba5aaea223fe315786c401d8cecb1ad2de9f179680c536ea30311fb21fa934b@188.166.100.178:30303",
|
||||
"enode://03f3661686d30509d621dbe5ee2e3082923f25e94fd41a2dd8dd34bb12a0c4e8fbde52247c6c55e86dc209a8e7c4a5ae56058c65f7b01734d3ab73818b44e2a3@188.166.33.47:30303",
|
||||
}},
|
||||
}
|
||||
|
||||
var rinkebyCluster = clusterData{
|
||||
NetworkID: 4,
|
||||
Prod: subClusterData{StaticNodes: []string{
|
||||
"enode://fda3f6273a0f2da4ac5858d1f52e5afaf9def281121be3d37558c67d4d9ca26c6ad7a0520b2cd7454120fb770e86d5760487c9924b2166e65485f606e56d60fc@51.15.69.144:30303",
|
||||
"enode://ba41aa829287a0a9076d9bffed97c8ce2e491b99873288c9e886f16fd575306ac6c656db4fbf814f5a9021aec004ffa9c0ae8650f92fd10c12eeb7c364593eb3@51.15.69.147:30303",
|
||||
"enode://28ecf5272b560ca951f4cd7f1eb8bd62da5853b026b46db432c4b01797f5b0114819a090a72acd7f32685365ecd8e00450074fa0673039aefe10f3fb666e0f3f@51.15.76.249:30303",
|
||||
}},
|
||||
Dev: subClusterData{StaticNodes: []string{
|
||||
"enode://7512c8f6e7ffdcc723cf77e602a1de9d8cc2e8ad35db309464819122cd773857131aee390fec33894db13da730c8432bb248eed64039e3810e156e979b2847cb@51.15.78.243:30303",
|
||||
"enode://1cc27a5a41130a5c8b90db5b2273dc28f7b56f3edfc0dcc57b665d451274b26541e8de49ea7a074281906a82209b9600239c981163b6ff85c3038a8e2bc5d8b8@51.15.68.93:30303",
|
||||
"enode://798d17064141b8f88df718028a8272b943d1cb8e696b3dab56519c70b77b1d3469b56b6f4ce3788457646808f5c7299e9116626f2281f30b959527b969a71e4f@51.15.75.244:30303",
|
||||
}},
|
||||
}
|
||||
|
||||
var mainnetCluster = clusterData{
|
||||
NetworkID: 1,
|
||||
Prod: subClusterData{},
|
||||
Dev: subClusterData{StaticNodes: []string{
|
||||
"enode://3aeaff0868b19e03fabe33e6e0fcc821094e1601be44edd6f45e3f0171ed964e13623e49987bddd6c517304d2a45dfe66da51e47b2e11d59c4b30cd6094db43d@163.172.176.22:30303",
|
||||
"enode://687343483ca41132a16c9ab67b49e9997a34ec38ddb6dd60bf45f9a0ea4c50362f902553d813af44ab1cdb246fc384d4c74b4437c15cefe3bb0e87b399dbb5bb@163.172.176.22:30403",
|
||||
"enode://2a3d6c1c86546831e5bb2684ff0ed6d931bdacf3c6cd344706452a1e78c41442d38c62317096175dcea6517959f40ac789f76356348e0a17ee53563cbdf2db48@163.172.176.22:30503",
|
||||
"enode://71bb01b58165e3262aea2d3b06dbf9abb8d5512d96e5000e7e41ab2138b47be685935d3eb119fc25e1413db00d8db231fd9d59555a1cd75229821559b6a4eb51@51.15.85.243:30303",
|
||||
"enode://7afd119c549a7ab02b3f7bd77ef3490b6d660d5c49d0734a0c8bb23195ced4ace0bf5cde673cd5cfd07dd8d759277f3d8408eb73dc3c217bbe00f0027d06eee9@51.15.85.243:30403",
|
||||
"enode://da8af0869e4e8047f21c1ac016b94a7b7d8e935dddd28d4272f88a1ceaee7c15e7deec9b6fd195ed3bc43748893111ebf2b2479ff44a8025ab8d598f3c97b589@51.15.85.243:30503",
|
||||
"enode://7ebaa6a8ce2547f10e34fab9cc5626b86d67934a86e1fb36145c0b89fcc7b9315dd6d0a8cc5808d11a55bdc14c78ff675ca956dfec53837b4f1a97392b15ec23@51.15.35.110:30303",
|
||||
}},
|
||||
}
|
||||
|
||||
var defaultClusters = []clusterData{ropstenCluster, rinkebyCluster, mainnetCluster}
|
|
@ -11,22 +11,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/static"
|
||||
)
|
||||
|
||||
// default node configuration options
|
||||
var (
|
||||
UseMainnetFlag = "false" // to be overridden via -ldflags '-X geth/params.UseMainnetFlag'
|
||||
UseMainnet = false
|
||||
)
|
||||
|
||||
func init() {
|
||||
if UseMainnetFlag == "true" { // set at compile time, here we make sure to set corresponding boolean flag
|
||||
UseMainnet = true
|
||||
}
|
||||
}
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrMissingDataDir = errors.New("missing required 'DataDir' parameter")
|
||||
|
@ -37,6 +25,10 @@ var (
|
|||
ErrAuthorizationKeyFileNotSet = errors.New("authorization key file is not set")
|
||||
)
|
||||
|
||||
// ----------
|
||||
// LightEthConfig
|
||||
// ----------
|
||||
|
||||
// LightEthConfig holds LES-related configuration
|
||||
// Status nodes are always lightweight clients (due to mobile platform constraints)
|
||||
type LightEthConfig struct {
|
||||
|
@ -50,6 +42,10 @@ type LightEthConfig struct {
|
|||
DatabaseCache int
|
||||
}
|
||||
|
||||
// ----------
|
||||
// FirebaseConfig
|
||||
// ----------
|
||||
|
||||
// FirebaseConfig holds FCM-related configuration
|
||||
type FirebaseConfig struct {
|
||||
// AuthorizationKeyFile file path that contains FCM authorization key
|
||||
|
@ -79,6 +75,10 @@ func (c *FirebaseConfig) ReadAuthorizationKeyFile() ([]byte, error) {
|
|||
return key, nil
|
||||
}
|
||||
|
||||
// ----------
|
||||
// WhisperConfig
|
||||
// ----------
|
||||
|
||||
// WhisperConfig holds SHH-related configuration
|
||||
type WhisperConfig struct {
|
||||
// Enabled flag specifies whether protocol is enabled
|
||||
|
@ -138,6 +138,10 @@ func (c *WhisperConfig) String() string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
// ----------
|
||||
// SwarmConfig
|
||||
// ----------
|
||||
|
||||
// SwarmConfig holds Swarm-related configuration
|
||||
type SwarmConfig struct {
|
||||
// Enabled flag specifies whether protocol is enabled
|
||||
|
@ -150,25 +154,34 @@ func (c *SwarmConfig) String() string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
// BootClusterConfig holds configuration for supporting boot cluster, which is a temporary
|
||||
// ----------
|
||||
// ClusterConfig
|
||||
// ----------
|
||||
|
||||
// ClusterConfig holds configuration for supporting cluster peers, which is a temporary
|
||||
// means for mobile devices to get connected to Ethereum network (UDP-based discovery
|
||||
// may not be available, so we need means to discover the network manually).
|
||||
type BootClusterConfig struct {
|
||||
type ClusterConfig struct {
|
||||
// Enabled flag specifies whether feature is enabled
|
||||
Enabled bool
|
||||
|
||||
// BootNodes list of bootstrap nodes for a given network (Ropsten, Rinkeby, Homestead),
|
||||
// StaticNodes lists the static nodes taken from compiled or passed cluster.json
|
||||
StaticNodes []string
|
||||
|
||||
// BootNodes list of cluster peer nodes for a given network (Ropsten, Rinkeby, Homestead),
|
||||
// for a given mode (production vs development)
|
||||
BootNodes []string
|
||||
}
|
||||
|
||||
// String dumps config object as nicely indented JSON
|
||||
func (c *BootClusterConfig) String() string {
|
||||
func (c *ClusterConfig) String() string {
|
||||
data, _ := json.MarshalIndent(c, "", " ") // nolint: gas
|
||||
return string(data)
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// ----------
|
||||
// UpstreamRPCConfig
|
||||
// ----------
|
||||
|
||||
// UpstreamRPCConfig stores configuration for upstream rpc connection.
|
||||
type UpstreamRPCConfig struct {
|
||||
|
@ -180,7 +193,9 @@ type UpstreamRPCConfig struct {
|
|||
URL string
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// ----------
|
||||
// NodeConfig
|
||||
// ----------
|
||||
|
||||
// NodeConfig stores configuration options for a node
|
||||
type NodeConfig struct {
|
||||
|
@ -199,7 +214,7 @@ type NodeConfig struct {
|
|||
// If KeyStoreDir is empty, the default location is the "keystore" subdirectory of DataDir.
|
||||
KeyStoreDir string
|
||||
|
||||
// PrivateKeyFile is a filename with node ID (private key)
|
||||
// NodeKeyFile is a filename with node ID (private key)
|
||||
// This file should contain a valid secp256k1 private key that will be used for both
|
||||
// remote peer identification as well as network traffic encryption.
|
||||
NodeKeyFile string
|
||||
|
@ -256,6 +271,8 @@ type NodeConfig struct {
|
|||
// handshake phase, counted separately for inbound and outbound connections.
|
||||
MaxPendingPeers int
|
||||
|
||||
log log.Logger
|
||||
|
||||
// LogFile is filename where exposed logs get written to
|
||||
LogFile string
|
||||
|
||||
|
@ -268,8 +285,12 @@ type NodeConfig struct {
|
|||
// UpstreamConfig extra config for providing upstream infura server.
|
||||
UpstreamConfig UpstreamRPCConfig `json:"UpstreamConfig"`
|
||||
|
||||
// BootClusterConfig extra configuration for supporting cluster
|
||||
BootClusterConfig *BootClusterConfig `json:"BootClusterConfig," validate:"structonly"`
|
||||
// ClusterConfigFile contains the file name of the cluster configuration. If
|
||||
// empty the statical configuration data will be taken.
|
||||
ClusterConfigFile string `json:"ClusterConfigFile"`
|
||||
|
||||
// ClusterConfig extra configuration for supporting cluster peers.
|
||||
ClusterConfig *ClusterConfig `json:"ClusterConfig," validate:"structonly"`
|
||||
|
||||
// LightEthConfig extra configuration for LES
|
||||
LightEthConfig *LightEthConfig `json:"LightEthConfig," validate:"structonly"`
|
||||
|
@ -282,7 +303,7 @@ type NodeConfig struct {
|
|||
}
|
||||
|
||||
// NewNodeConfig creates new node configuration object
|
||||
func NewNodeConfig(dataDir string, networkID uint64, devMode bool) (*NodeConfig, error) {
|
||||
func NewNodeConfig(dataDir string, clstrCfgFile string, networkID uint64, devMode bool) (*NodeConfig, error) {
|
||||
nodeConfig := &NodeConfig{
|
||||
DevMode: devMode,
|
||||
NetworkID: networkID,
|
||||
|
@ -299,11 +320,14 @@ func NewNodeConfig(dataDir string, networkID uint64, devMode bool) (*NodeConfig,
|
|||
MaxPeers: MaxPeers,
|
||||
MaxPendingPeers: MaxPendingPeers,
|
||||
IPCFile: IPCFile,
|
||||
log: log.New("package", "status-go/geth/params.NodeConfig"),
|
||||
LogFile: LogFile,
|
||||
LogLevel: LogLevel,
|
||||
LogToStderr: LogToStderr,
|
||||
BootClusterConfig: &BootClusterConfig{
|
||||
ClusterConfigFile: clstrCfgFile,
|
||||
ClusterConfig: &ClusterConfig{
|
||||
Enabled: true,
|
||||
StaticNodes: []string{},
|
||||
BootNodes: []string{},
|
||||
},
|
||||
LightEthConfig: &LightEthConfig{
|
||||
|
@ -344,7 +368,7 @@ func LoadNodeConfig(configJSON string) (*NodeConfig, error) {
|
|||
}
|
||||
|
||||
func loadNodeConfig(configJSON string) (*NodeConfig, error) {
|
||||
nodeConfig, err := NewNodeConfig("", 0, true)
|
||||
nodeConfig, err := NewNodeConfig("", "", 0, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -386,8 +410,8 @@ func (c *NodeConfig) Validate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if c.BootClusterConfig.Enabled {
|
||||
if err := validate.Struct(c.BootClusterConfig); err != nil {
|
||||
if c.ClusterConfig.Enabled {
|
||||
if err := validate.Struct(c.ClusterConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -429,13 +453,14 @@ func (c *NodeConfig) Save() error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("config file saved: %v", configFilePath))
|
||||
c.log.Info("config file saved", "path", configFilePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateConfig traverses configuration and adjusts dependent fields
|
||||
// (we have a development/production and mobile/full node dependent configurations)
|
||||
func (c *NodeConfig) updateConfig() error {
|
||||
// Update separate configurations.
|
||||
if err := c.updateGenesisConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -444,7 +469,7 @@ func (c *NodeConfig) updateConfig() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := c.updateBootClusterConfig(); err != nil {
|
||||
if err := c.updateClusterConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -522,46 +547,34 @@ func (c *NodeConfig) updateUpstreamConfig() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// updateBootClusterConfig loads boot nodes and CHT for a given network and mode.
|
||||
// updateClusterConfig loads static peer nodes and CHT for a given network and mode.
|
||||
// This is necessary until we have LES protocol support CHT sync, and better node
|
||||
// discovery on mobile devices)
|
||||
func (c *NodeConfig) updateBootClusterConfig() error {
|
||||
if !c.BootClusterConfig.Enabled {
|
||||
func (c *NodeConfig) updateClusterConfig() error {
|
||||
if !c.ClusterConfig.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Remove this thing as this is an ugly hack.
|
||||
// Once CHT sync sub-protocol is working in LES, we will rely on it, as it provides
|
||||
// decentralized solution. For now, in order to avoid forcing users to long sync times
|
||||
// we use central static resource
|
||||
type subClusterConfig struct {
|
||||
Number int `json:"number"`
|
||||
Hash string `json:"hash"`
|
||||
BootNodes []string `json:"bootnodes"`
|
||||
}
|
||||
type clusterConfig struct {
|
||||
NetworkID int `json:"networkID"`
|
||||
GenesisHash string `json:"genesisHash"`
|
||||
Prod subClusterConfig `json:"prod"`
|
||||
Dev subClusterConfig `json:"dev"`
|
||||
}
|
||||
|
||||
chtFile, err := static.Asset("config/staticpeers.json")
|
||||
var clusters []clusterData
|
||||
if c.ClusterConfigFile != "" {
|
||||
// Load cluster configuration from external file.
|
||||
configFile, err := ioutil.ReadFile(c.ClusterConfigFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("staticpeers.json could not be loaded: %s", err)
|
||||
return fmt.Errorf("cluster configuration file '%s' could not be loaded: %s", c.ClusterConfigFile, err)
|
||||
}
|
||||
|
||||
var clusters []clusterConfig
|
||||
err = json.Unmarshal(chtFile, &clusters)
|
||||
err = json.Unmarshal(configFile, &clusters)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal staticpeers.json: %s", err)
|
||||
return fmt.Errorf("failed to unmarshal cluster configuration file: %s", err)
|
||||
}
|
||||
} else {
|
||||
clusters = defaultClusters
|
||||
}
|
||||
|
||||
for _, cluster := range clusters {
|
||||
if cluster.NetworkID == int(c.NetworkID) {
|
||||
c.BootClusterConfig.BootNodes = cluster.Prod.BootNodes
|
||||
c.ClusterConfig.StaticNodes = cluster.Prod.StaticNodes
|
||||
if c.DevMode {
|
||||
c.BootClusterConfig.BootNodes = cluster.Dev.BootNodes
|
||||
c.ClusterConfig.StaticNodes = cluster.Dev.StaticNodes
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package common
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -7,8 +7,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// RPCCall represents a unit of a rpc request which is to be executed.
|
||||
type RPCCall struct {
|
||||
// Call represents a unit of a rpc request which is to be executed.
|
||||
type Call struct {
|
||||
ID int64
|
||||
Method string
|
||||
Params []interface{}
|
||||
|
@ -20,9 +20,9 @@ var (
|
|||
ErrInvalidToAddress = errors.New("Failed to parse To Address")
|
||||
)
|
||||
|
||||
// ParseFromAddress returns the address associated with the RPCCall.
|
||||
func (r RPCCall) ParseFromAddress() (gethcommon.Address, error) {
|
||||
params, ok := r.Params[0].(map[string]interface{})
|
||||
// ParseFromAddress returns the address associated with the Call.
|
||||
func (c Call) ParseFromAddress() (gethcommon.Address, error) {
|
||||
params, ok := c.Params[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return gethcommon.HexToAddress("0x"), ErrInvalidFromAddress
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ func (r RPCCall) ParseFromAddress() (gethcommon.Address, error) {
|
|||
}
|
||||
|
||||
// ParseToAddress returns the gethcommon.Address associated with the call.
|
||||
func (r RPCCall) ParseToAddress() (gethcommon.Address, error) {
|
||||
params, ok := r.Params[0].(map[string]interface{})
|
||||
func (c Call) ParseToAddress() (gethcommon.Address, error) {
|
||||
params, ok := c.Params[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return gethcommon.HexToAddress("0x"), ErrInvalidToAddress
|
||||
}
|
||||
|
@ -50,14 +50,13 @@ func (r RPCCall) ParseToAddress() (gethcommon.Address, error) {
|
|||
return gethcommon.HexToAddress(to), nil
|
||||
}
|
||||
|
||||
// ParseData returns the bytes associated with the call.
|
||||
func (r RPCCall) ParseData() hexutil.Bytes {
|
||||
params, ok := r.Params[0].(map[string]interface{})
|
||||
func (c Call) parseDataField(fieldName string) hexutil.Bytes {
|
||||
params, ok := c.Params[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return hexutil.Bytes("0x")
|
||||
}
|
||||
|
||||
data, ok := params["data"].(string)
|
||||
data, ok := params[fieldName].(string)
|
||||
if !ok {
|
||||
data = "0x"
|
||||
}
|
||||
|
@ -70,10 +69,20 @@ func (r RPCCall) ParseData() hexutil.Bytes {
|
|||
return byteCode
|
||||
}
|
||||
|
||||
// ParseData returns the bytes associated with the call in the deprecated "data" field.
|
||||
func (c Call) ParseData() hexutil.Bytes {
|
||||
return c.parseDataField("data")
|
||||
}
|
||||
|
||||
// ParseInput returns the bytes associated with the call.
|
||||
func (c Call) ParseInput() hexutil.Bytes {
|
||||
return c.parseDataField("input")
|
||||
}
|
||||
|
||||
// ParseValue returns the hex big associated with the call.
|
||||
// nolint: dupl
|
||||
func (r RPCCall) ParseValue() *hexutil.Big {
|
||||
params, ok := r.Params[0].(map[string]interface{})
|
||||
func (c Call) ParseValue() *hexutil.Big {
|
||||
params, ok := c.Params[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
//return (*hexutil.Big)(big.NewInt("0x0"))
|
||||
|
@ -94,8 +103,8 @@ func (r RPCCall) ParseValue() *hexutil.Big {
|
|||
|
||||
// ParseGas returns the hex big associated with the call.
|
||||
// nolint: dupl
|
||||
func (r RPCCall) ParseGas() *hexutil.Uint64 {
|
||||
params, ok := r.Params[0].(map[string]interface{})
|
||||
func (c Call) ParseGas() *hexutil.Uint64 {
|
||||
params, ok := c.Params[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -116,8 +125,8 @@ func (r RPCCall) ParseGas() *hexutil.Uint64 {
|
|||
|
||||
// ParseGasPrice returns the hex big associated with the call.
|
||||
// nolint: dupl
|
||||
func (r RPCCall) ParseGasPrice() *hexutil.Big {
|
||||
params, ok := r.Params[0].(map[string]interface{})
|
||||
func (c Call) ParseGasPrice() *hexutil.Big {
|
||||
params, ok := c.Params[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -134,29 +143,3 @@ func (r RPCCall) ParseGasPrice() *hexutil.Big {
|
|||
|
||||
return (*hexutil.Big)(parsedValue)
|
||||
}
|
||||
|
||||
// ToSendTxArgs converts RPCCall to SendTxArgs.
|
||||
func (r RPCCall) ToSendTxArgs() SendTxArgs {
|
||||
var err error
|
||||
var fromAddr, toAddr gethcommon.Address
|
||||
|
||||
fromAddr, err = r.ParseFromAddress()
|
||||
if err != nil {
|
||||
fromAddr = gethcommon.HexToAddress("0x0")
|
||||
}
|
||||
|
||||
toAddr, err = r.ParseToAddress()
|
||||
if err != nil {
|
||||
toAddr = gethcommon.HexToAddress("0x0")
|
||||
}
|
||||
|
||||
input := r.ParseData()
|
||||
return SendTxArgs{
|
||||
To: &toAddr,
|
||||
From: fromAddr,
|
||||
Value: r.ParseValue(),
|
||||
Input: input,
|
||||
Gas: r.ParseGas(),
|
||||
GasPrice: r.ParseGasPrice(),
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -101,7 +100,7 @@ func (c *Client) callBatchMethods(ctx context.Context, msgs json.RawMessage) str
|
|||
|
||||
data, err := json.Marshal(responses)
|
||||
if err != nil {
|
||||
log.Error("Failed to marshal batch responses:", err)
|
||||
c.log.Error("Failed to marshal batch responses:", "error", err)
|
||||
return newErrorResponse(errInvalidMessageCode, err, defaultMsgID)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
|
@ -30,6 +31,7 @@ type Client struct {
|
|||
|
||||
handlersMx sync.RWMutex // mx guards handlers
|
||||
handlers map[string]Handler // locally registered handlers
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewClient initializes Client and tries to connect to both,
|
||||
|
@ -41,6 +43,7 @@ func NewClient(client *gethrpc.Client, upstream params.UpstreamRPCConfig) (*Clie
|
|||
c := Client{
|
||||
local: client,
|
||||
handlers: make(map[string]Handler),
|
||||
log: log.New("package", "status-go/geth/rpc.Client"),
|
||||
}
|
||||
|
||||
var err error
|
||||
|
|
|
@ -4,14 +4,17 @@
|
|||
package whisper
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
)
|
||||
|
||||
// EnvelopeTracer traces incoming envelopes.
|
||||
type EnvelopeTracer struct{}
|
||||
|
||||
// All general log messages in this package should be routed through this logger.
|
||||
var logger = log.New("package", "status-go/metrics/whisper")
|
||||
|
||||
// Trace is called for every incoming envelope.
|
||||
func (t *EnvelopeTracer) Trace(envelope *whisper.EnvelopeMeta) {
|
||||
log.Debug("Received Whisper envelope", "hash", envelope.Hash, "data", envelope)
|
||||
logger.Debug("Received Whisper envelope", "hash", envelope.Hash, "data", envelope)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Channel : ...
|
||||
type Channel struct {
|
||||
conn *Conn
|
||||
channelName string
|
||||
filterID string
|
||||
channelKey string
|
||||
topic string
|
||||
subscriptions []*Subscription
|
||||
}
|
||||
|
||||
// Publish : ...
|
||||
func (c *Channel) Publish(body string) error {
|
||||
cfg, _ := c.conn.statusNode.Config()
|
||||
powTime := cfg.WhisperConfig.MinimumPoW
|
||||
|
||||
message := NewMsg(c.conn.userName, body, c.channelName)
|
||||
cmd := fmt.Sprintf(standardMessageFormat, c.conn.address, c.channelKey, message.ToPayload(), c.topic, powTime)
|
||||
|
||||
log.Println("-> Sending:", cmd)
|
||||
log.Println("-> SENT:", c.conn.statusNode.RPCClient().CallRaw(cmd))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgHandler is a callback function that processes messages delivered to
|
||||
// asynchronous subscribers.
|
||||
type MsgHandler func(msg *Msg)
|
||||
|
||||
// Subscribe : ...
|
||||
func (c *Channel) Subscribe(fn MsgHandler) (*Subscription, error) {
|
||||
log.Println("Subscribed to channel '", c.channelName, "'")
|
||||
subscription := &Subscription{}
|
||||
go subscription.Subscribe(c, fn)
|
||||
c.subscriptions = append(c.subscriptions, subscription)
|
||||
|
||||
return subscription, nil
|
||||
}
|
||||
|
||||
func (c *Channel) removeSubscription(sub *Subscription) {
|
||||
var subs []*Subscription
|
||||
for _, s := range c.subscriptions {
|
||||
if s != sub {
|
||||
subs = append(subs, s)
|
||||
}
|
||||
}
|
||||
c.subscriptions = subs
|
||||
}
|
|
@ -1,16 +1,21 @@
|
|||
package bots
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
)
|
||||
|
||||
func NodeConfig() (*params.NodeConfig, error) {
|
||||
type Config struct {
|
||||
NodeConfig *params.NodeConfig
|
||||
}
|
||||
|
||||
// DefaultConfig : prepares a default config
|
||||
func DefaultConfig() *Config {
|
||||
configFormat := `{
|
||||
"APIModules": "db,eth,net,web3,shh,personal,admin",
|
||||
"BootClusterConfig": {
|
||||
"ClusterConfig": {
|
||||
"BootNodes": [
|
||||
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@51.15.63.93:30303",
|
||||
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@51.15.79.88:30303",
|
||||
|
@ -88,5 +93,11 @@ func NodeConfig() (*params.NodeConfig, error) {
|
|||
cwd := "/data/sg_bots/node"
|
||||
|
||||
config := fmt.Sprintf(configFormat, cwd, cwd, cwd, cwd)
|
||||
return params.LoadNodeConfig(config)
|
||||
cfg, err := params.LoadNodeConfig(config)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Config{NodeConfig: cfg}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
)
|
||||
|
||||
// Conn : TODO ...
|
||||
type Conn struct {
|
||||
statusNode *node.StatusNode
|
||||
address string
|
||||
userName string
|
||||
}
|
||||
|
||||
func New() *Conn {
|
||||
return &Conn{}
|
||||
}
|
||||
|
||||
func (c *Conn) Start(config *Config) error {
|
||||
statusNode := node.New()
|
||||
log.Println("Starting node...")
|
||||
|
||||
err := statusNode.Start(config.NodeConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Node start failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
c.statusNode = statusNode
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Connect will attempt to connect to the STATUS messaging system.
|
||||
// The url can contain username/password semantics. e.g. http://derek:pass@localhost:4222
|
||||
func Connect(user, password string) (*Conn, error) {
|
||||
var err error
|
||||
|
||||
log.Println("Using default config ...")
|
||||
config := DefaultConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("Making config failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := New()
|
||||
c.Start(config)
|
||||
|
||||
return c, c.SignupOrLogin(user, password)
|
||||
}
|
||||
|
||||
// Login logs in to the network with the given credentials
|
||||
func (c *Conn) Login(user, password string) error {
|
||||
am := account.NewManager(c.statusNode)
|
||||
w, err := c.statusNode.WhisperService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr := getAccountAddress()
|
||||
_, accountKey, err := am.AddressToDecryptedAccount(addr, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("ADDING PRIVATE KEY :", accountKey.PrivateKey)
|
||||
keyID, err := w.AddKeyPair(accountKey.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.address = keyID
|
||||
c.userName = user
|
||||
log.Println("Logging in as", c.address)
|
||||
|
||||
return am.SelectAccount(addr, password)
|
||||
}
|
||||
|
||||
// Signup creates a new account with the given credentials
|
||||
func (c *Conn) Signup(user, password string) error {
|
||||
am := account.NewManager(c.statusNode)
|
||||
address, _, _, err := am.CreateAccount(password)
|
||||
if err != nil {
|
||||
log.Fatalf("could not create an account. ERR: %v", err)
|
||||
}
|
||||
saveAccountAddress(address)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignupOrLogin will attempt to login with given credentials, in first instance
|
||||
// or will sign up in case login does not work
|
||||
func (c *Conn) SignupOrLogin(user, password string) error {
|
||||
if err := c.Login(user, password); err != nil {
|
||||
c.Signup(user, password)
|
||||
return c.Login(user, password)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Join a specific channel by name
|
||||
func (c *Conn) Join(channelName string) (*Channel, error) {
|
||||
return c.joinPublicChannel(channelName)
|
||||
}
|
||||
|
||||
func (c *Conn) joinPublicChannel(channelName string) (*Channel, error) {
|
||||
cmd := fmt.Sprintf(generateSymKeyFromPasswordFormat, channelName)
|
||||
f := unmarshalJSON(c.statusNode.RPCClient().CallRaw(cmd))
|
||||
|
||||
key := f.(map[string]interface{})["result"].(string)
|
||||
id := int(f.(map[string]interface{})["id"].(float64))
|
||||
|
||||
// p := "0x68656c6c6f20776f726c64"
|
||||
src := []byte(channelName)
|
||||
p := "0x" + hex.EncodeToString(src)
|
||||
|
||||
cmd = fmt.Sprintf(web3ShaFormat, p, id)
|
||||
f1 := unmarshalJSON(c.statusNode.RPCClient().CallRaw(cmd))
|
||||
topic := f1.(map[string]interface{})["result"].(string)
|
||||
topic = topic[0:10]
|
||||
|
||||
cmd = fmt.Sprintf(newMessageFilterFormat, topic, key)
|
||||
res := c.statusNode.RPCClient().CallRaw(cmd)
|
||||
f3 := unmarshalJSON(res)
|
||||
filterID := f3.(map[string]interface{})["result"].(string)
|
||||
|
||||
return &Channel{
|
||||
conn: c,
|
||||
channelName: channelName,
|
||||
filterID: filterID,
|
||||
topic: topic,
|
||||
channelKey: key,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
// TODO : this should be received as an input
|
||||
const KEY_ADDRESS = "hnny.address.lol"
|
||||
|
||||
func getAccountAddress() string {
|
||||
cwd, _ := os.Getwd()
|
||||
println(cwd + "/data")
|
||||
db, err := leveldb.OpenFile(cwd+"/data", nil)
|
||||
if err != nil {
|
||||
log.Fatal("can't open levelDB file. ERR: ", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
addressBytes, err := db.Get([]byte(KEY_ADDRESS), nil)
|
||||
if err != nil {
|
||||
log.Printf("Error while getting address: %v", err)
|
||||
return ""
|
||||
}
|
||||
return string(addressBytes)
|
||||
}
|
||||
|
||||
func saveAccountAddress(address string) {
|
||||
cwd, _ := os.Getwd()
|
||||
db, err := leveldb.OpenFile(cwd+"/data", nil)
|
||||
if err != nil {
|
||||
log.Fatal("can't open levelDB file. ERR: ", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
db.Put([]byte(KEY_ADDRESS), []byte(address), nil)
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package sdk
|
||||
|
||||
var (
|
||||
generateSymKeyFromPasswordFormat = `{"jsonrpc":"2.0","id":2950,"method":"shh_generateSymKeyFromPassword","params":["%s"]}`
|
||||
newMessageFilterFormat = `{"jsonrpc":"2.0","id":2,"method":"shh_newMessageFilter","params":[{"allowP2P":true,"topics":["%s"],"type":"sym","symKeyID":"%s"}]}`
|
||||
getFilterMessagesFormat = `{"jsonrpc":"2.0","id":2968,"method":"shh_getFilterMessages","params":["%s"]}`
|
||||
standardMessageFormat = `{"jsonrpc":"2.0","id":633,"method":"shh_post","params":[{"sig":"%s","symKeyID":"%s","payload":"%s","topic":"%s","ttl":10,"powTarget":%g,"powTime":1}]}`
|
||||
messagePayloadFormat = `["~#c4",["%s","text/plain","~:public-group-user-message",%d,%d]]`
|
||||
web3ShaFormat = `{"jsonrpc":"2.0","method":"web3_sha3","params":["%s"],"id":%d}`
|
||||
)
|
|
@ -0,0 +1,70 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
)
|
||||
|
||||
// Msg is a structure used by Subscribers and Publish().
|
||||
type Msg struct {
|
||||
From string `json:"from"`
|
||||
Text string `json:"text"`
|
||||
ChannelName string `json:"channel"`
|
||||
Timestamp int64 `json:"ts"`
|
||||
Raw string `json:"-"`
|
||||
}
|
||||
|
||||
// NewMsg : Creates a new Msg with a generated UUID
|
||||
func NewMsg(from, text, channel string) *Msg {
|
||||
return &Msg{
|
||||
From: from,
|
||||
Text: text,
|
||||
ChannelName: channel,
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Msg) ID() string {
|
||||
return fmt.Sprintf("%X", sha3.Sum256([]byte(m.Raw)))
|
||||
}
|
||||
|
||||
// ToPayload converts current struct to a valid payload
|
||||
func (m *Msg) ToPayload() string {
|
||||
message := fmt.Sprintf(messagePayloadFormat,
|
||||
m.Text,
|
||||
m.Timestamp*100,
|
||||
m.Timestamp)
|
||||
|
||||
return rawrChatMessage(message)
|
||||
}
|
||||
|
||||
// MessageFromPayload : TODO ...
|
||||
func MessageFromPayload(payload string) (*Msg, error) {
|
||||
message, err := unrawrChatMessage(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var x []interface{}
|
||||
err = json.Unmarshal([]byte(message), &x)
|
||||
if err != nil {
|
||||
return nil, errors.New("unsupported message type, json err: " + err.Error())
|
||||
}
|
||||
if len(x) < 1 {
|
||||
return nil, errors.New("unsupported message type, no messages")
|
||||
}
|
||||
if x[0].(string) != "~#c4" {
|
||||
return nil, errors.New("unsupported message type, wrong prefix: " + x[0].(string))
|
||||
}
|
||||
properties := x[1].([]interface{})
|
||||
|
||||
return &Msg{
|
||||
From: "TODO : someone",
|
||||
Text: properties[0].(string),
|
||||
Timestamp: int64(properties[3].(float64)),
|
||||
Raw: string(message),
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package sdk
|
||||
|
||||
func hello() {
|
||||
println("hello")
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Subscription struct {
|
||||
unsubscribe chan bool
|
||||
channel *Channel
|
||||
}
|
||||
|
||||
func (s *Subscription) Subscribe(channel *Channel, fn MsgHandler) {
|
||||
s.channel = channel
|
||||
s.unsubscribe = make(chan bool)
|
||||
for {
|
||||
select {
|
||||
case <-s.unsubscribe:
|
||||
return
|
||||
default:
|
||||
cmd := fmt.Sprintf(getFilterMessagesFormat, channel.filterID)
|
||||
response := channel.conn.statusNode.RPCClient().CallRaw(cmd)
|
||||
f := unmarshalJSON(response)
|
||||
v := f.(map[string]interface{})["result"]
|
||||
switch vv := v.(type) {
|
||||
case []interface{}:
|
||||
for _, u := range vv {
|
||||
payload := u.(map[string]interface{})["payload"]
|
||||
message, err := MessageFromPayload(payload.(string))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
fn(message)
|
||||
}
|
||||
}
|
||||
default:
|
||||
log.Println(v, "is of a type I don't know how to handle")
|
||||
}
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Subscription) Unsubscribe() {
|
||||
s.unsubscribe <- true
|
||||
s.channel.removeSubscription(s)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func unmarshalJSON(j string) interface{} {
|
||||
var v interface{}
|
||||
json.Unmarshal([]byte(j), &v)
|
||||
return v
|
||||
}
|
||||
|
||||
// newUUID generates a random UUID according to RFC 4122
|
||||
func newUUID() string {
|
||||
uuid := make([]byte, 16)
|
||||
n, err := io.ReadFull(rand.Reader, uuid)
|
||||
if n != len(uuid) || err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// variant bits; see section 4.1.1
|
||||
uuid[8] = uuid[8]&^0xc0 | 0x80
|
||||
// version 4 (pseudo-random); see section 4.1.3
|
||||
uuid[6] = uuid[6]&^0xf0 | 0x40
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
|
||||
}
|
||||
|
||||
func rawrChatMessage(raw string) string {
|
||||
bytes := []byte(raw)
|
||||
|
||||
return fmt.Sprintf("0x%s", hex.EncodeToString(bytes))
|
||||
}
|
||||
|
||||
func unrawrChatMessage(message string) ([]byte, error) {
|
||||
return hex.DecodeString(message[2:])
|
||||
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue