new pinger

This commit is contained in:
Igor Mandrigin 2018-04-19 11:45:18 +02:00
parent 90950ff330
commit 66ce64d868
45 changed files with 1488 additions and 2649 deletions

61
Gopkg.lock generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

75
cmd/pingerweb/main.go Normal file
View File

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

View File

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

View File

@ -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")
ErrAddressToAccountMappingFailure = errors.New("cannot retrieve a valid account for a given address")
ErrAccountToKeyMappingFailure = errors.New("cannot retrieve a valid key for a given account")
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
}

View 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))
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,29 +303,32 @@ 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,
DataDir: dataDir,
Name: ClientIdentifier,
Version: Version,
RPCEnabled: RPCEnabledDefault,
HTTPHost: HTTPHost,
HTTPPort: HTTPPort,
ListenAddr: ListenAddr,
APIModules: APIModules,
WSHost: WSHost,
WSPort: WSPort,
MaxPeers: MaxPeers,
MaxPendingPeers: MaxPendingPeers,
IPCFile: IPCFile,
LogFile: LogFile,
LogLevel: LogLevel,
LogToStderr: LogToStderr,
BootClusterConfig: &BootClusterConfig{
Enabled: true,
BootNodes: []string{},
DevMode: devMode,
NetworkID: networkID,
DataDir: dataDir,
Name: ClientIdentifier,
Version: Version,
RPCEnabled: RPCEnabledDefault,
HTTPHost: HTTPHost,
HTTPPort: HTTPPort,
ListenAddr: ListenAddr,
APIModules: APIModules,
WSHost: WSHost,
WSPort: WSPort,
MaxPeers: MaxPeers,
MaxPendingPeers: MaxPendingPeers,
IPCFile: IPCFile,
log: log.New("package", "status-go/geth/params.NodeConfig"),
LogFile: LogFile,
LogLevel: LogLevel,
LogToStderr: LogToStderr,
ClusterConfigFile: clstrCfgFile,
ClusterConfig: &ClusterConfig{
Enabled: true,
StaticNodes: []string{},
BootNodes: []string{},
},
LightEthConfig: &LightEthConfig{
Enabled: true,
@ -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")
if err != nil {
return fmt.Errorf("staticpeers.json could not be loaded: %s", err)
}
var clusters []clusterConfig
err = json.Unmarshal(chtFile, &clusters)
if err != nil {
return fmt.Errorf("failed to unmarshal staticpeers.json: %s", err)
var clusters []clusterData
if c.ClusterConfigFile != "" {
// Load cluster configuration from external file.
configFile, err := ioutil.ReadFile(c.ClusterConfigFile)
if err != nil {
return fmt.Errorf("cluster configuration file '%s' could not be loaded: %s", c.ClusterConfigFile, err)
}
err = json.Unmarshal(configFile, &clusters)
if err != nil {
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
}

View File

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

View File

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

View File

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

View File

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

54
vendor/github.com/status-im/status-go/sdk/chan.go generated vendored Normal file
View File

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

View File

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

138
vendor/github.com/status-im/status-go/sdk/conn.go generated vendored Normal file
View File

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

39
vendor/github.com/status-im/status-go/sdk/db.go generated vendored Normal file
View File

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

View File

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

70
vendor/github.com/status-im/status-go/sdk/msg.go generated vendored Normal file
View File

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

5
vendor/github.com/status-im/status-go/sdk/sdk.go generated vendored Normal file
View File

@ -0,0 +1,5 @@
package sdk
func hello() {
println("hello")
}

View File

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

39
vendor/github.com/status-im/status-go/sdk/utils.go generated vendored Normal file
View File

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