2
0
mirror of synced 2025-02-20 17:48:08 +00:00

Initial comit

This commit is contained in:
Adrià Cidre 2018-04-30 17:57:11 +02:00
commit 9b46298eca
No known key found for this signature in database
GPG Key ID: D246A27D58A92CAB
11 changed files with 509 additions and 0 deletions

21
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,21 @@
# Problem
An overview of the background required to understand the problem.
A problem description.
# Implementation
Known steps towards feature implementation.
What needs further specifying and investigating.
# Acceptance Criteria
Rules for the future PR to be accepted.
# Notes
Random notes to keep in mind while implementing it. Mostly about related issues and future plans and thoughts.
# Future Steps
Steps which should be taken after this issue has been resolved.

8
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,8 @@
A short summary which serves as a squashed-commit message.
A description to understand introduced changes without reading the code.
Important changes:
- [x] Something worth noting for reviewers.
Closes #

71
.gitignore vendored Normal file
View File

@ -0,0 +1,71 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/statusd-data
/cmd/statusd/statusd-data
/cmd/statusd/statusd
/cmd/statusd-cli/statusd-cli
/wnode-status-data
/cmd/wnode-status/wnode-status-data
/cmd/wnode-status/wnode-status
/tmp
*/**/*un~
*/**/*.test
*un~
.DS_Store
*/**/.DS_Store
.ethtest
*/**/*tx_database*
*/**/*dapps*
vendor/github.com/ethereum/go-ethereum/vendor
node_modules/
tags
#*
.#*
*#
*~
.project
.settings
# used by the Makefile
/build/_workspace/
/build/bin/
/vendor/github.com/karalabe/xgo
# travis
profile.tmp
profile.cov
# vagrant
.vagrant
# tests
.ethereumtest/
/vendor/**/*_test.go
*.test
#
# golang
coverage.out
coverage-all.out
coverage.html
# vim swap files
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
Session.vim
.undodir/*
/.idea/
# SDK examples
/sdk/examples/ponger/data/
/sdk/examples/pinger/data/

59
chan.go Normal file
View File

@ -0,0 +1,59 @@
package sdk
import (
"fmt"
"log"
)
// Channel : ...
type Channel struct {
conn *Conn
channelName string
filterID string
channelKey string
topic string
subscriptions []*Subscription
}
// Publish : Publishes a message with the given body on the current channel
func (c *Channel) Publish(body string) error {
message := NewMsg(c.conn.userName, body, c.channelName)
cmd := fmt.Sprintf(standardMessageFormat,
c.conn.address,
c.channelKey,
message.ToPayload(),
c.topic,
c.conn.minimumPoW,
)
c.conn.rpc.Call(cmd)
return nil
}
// 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
}
// Close current channel and all its subscriptions
func (c *Channel) Close() {
for _, sub := range c.subscriptions {
c.removeSubscription(sub)
}
}
func (c *Channel) removeSubscription(sub *Subscription) {
var subs []*Subscription
for _, s := range c.subscriptions {
if s != sub {
subs = append(subs, s)
}
}
c.subscriptions = subs
}

116
conn.go Normal file
View File

@ -0,0 +1,116 @@
package sdk
import (
"encoding/hex"
"fmt"
"github.com/valyala/gorpc"
)
// Conn : TODO ...
type Conn struct {
rpc *gorpc.Client
address string
userName string
channels []*Channel
minimumPoW string
}
func NewConn(address string) *Conn {
rpc := &gorpc.Client{
Addr: address, // "rpc.server.addr:12345",
}
rpc.Start()
return &Conn{
rpc: rpc,
minimumPoW: "0.01",
}
}
func (c *Conn) Close() {
for _, channel := range c.channels {
channel.Close()
}
}
// Login logs in to the network with the given credentials
func (c *Conn) Login(addr, pwd string) error {
cmd := fmt.Sprintf(statusLoginFormat, addr, pwd)
res, err := c.rpc.Call(cmd)
if err != nil {
return err
}
// TODO(adriacidre) unmarshall and treat the response
println(res)
return nil
}
// Signup creates a new account with the given credentials
func (c *Conn) Signup(pwd string) error {
cmd := fmt.Sprintf(statusSignupFormat, pwd)
res, err := c.rpc.Call(cmd)
println("------")
println(res)
println(err.Error())
println("------")
if err != nil {
return err
}
// TODO(adriacidre) unmarshall and treat the response
println(res)
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(password)
return c.Login(user, password)
}
return nil
}
// Join a specific channel by name
func (c *Conn) Join(channelName string) (*Channel, error) {
ch, err := c.joinPublicChannel(channelName)
if err != nil {
c.channels = append(c.channels, ch)
}
return ch, err
}
func (c *Conn) joinPublicChannel(channelName string) (*Channel, error) {
cmd := fmt.Sprintf(generateSymKeyFromPasswordFormat, channelName)
res, _ := c.rpc.Call(cmd)
f := unmarshalJSON(res.(string))
key := f.(map[string]interface{})["result"].(string)
id := int(f.(map[string]interface{})["id"].(float64))
src := []byte(channelName)
p := "0x" + hex.EncodeToString(src)
cmd = fmt.Sprintf(web3ShaFormat, p, id)
res, _ = c.rpc.Call(cmd)
topic := res.(map[string]interface{})["result"].(string)
topic = topic[0:10]
cmd = fmt.Sprintf(newMessageFilterFormat, topic, key)
res, _ = c.rpc.Call(cmd)
f3 := unmarshalJSON(res.(string))
filterID := f3.(map[string]interface{})["result"].(string)
return &Channel{
conn: c,
channelName: channelName,
filterID: filterID,
topic: topic,
channelKey: key,
}, nil
}

14
dictionary.go Normal file
View File

@ -0,0 +1,14 @@
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}]}`
web3ShaFormat = `{"jsonrpc":"2.0","method":"web3_sha3","params":["%s"],"id":%d}`
statusLoginFormat = `{"jsonrpc":"2.0","method":"status_login","params":["%s","%s"]}`
statusSignupFormat = `{"jsonrpc":"2.0","method":"status_signup","params":["%s","%s"]}`
statusJoinPublicChannel = `{"jsonrpc":"2.0","method":"status_joinpublicchannel","params":["%s"]}`
messagePayloadFormat = `["~#c4",["%s","text/plain","~:public-group-user-message",%d,%d]]`
)

25
examples/pinger/main.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"fmt"
"time"
"github.com/status-im/status-go-sdk"
)
func main() {
conn := sdk.NewConn("localhost:30303")
if err := conn.Signup("111222333"); err != nil {
panic("Couldn't create an account")
}
ch, err := conn.Join("supu")
if err != nil {
panic("Couldn't connect to status")
}
for range time.Tick(10 * time.Second) {
message := fmt.Sprintf("PING : %d", time.Now().Unix())
ch.Publish(message)
}
}

37
examples/ponger/main.go Normal file
View File

@ -0,0 +1,37 @@
package main
import (
"fmt"
"log"
"runtime"
"strings"
"time"
"github.com/status-im/status-go-sdk"
)
func main() {
conn := sdk.NewConn()
if err := conn.SignupOrLogin("supu", "password"); err != nil {
panic(err)
}
ch, err := conn.Join("supu")
if err != nil {
panic("Couldn't connect to status")
}
ch.Subscribe(func(m *sdk.Msg) {
log.Println("Message from ", m.From, " with body: ", m.Text)
if strings.Contains(m.Text, "PING :") {
time.Sleep(5 * time.Second)
message := fmt.Sprintf("PONG : %d", time.Now().Unix())
ch.Publish(message)
}
})
runtime.Goexit()
}

63
msg.go Normal file
View File

@ -0,0 +1,63 @@
package sdk
import (
"encoding/json"
"errors"
"fmt"
"time"
)
// Msg is a structure used by Subscribers and Publish().
type Msg struct {
ID string `json:"id"`
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{
ID: newUUID(),
From: from,
Text: text,
ChannelName: channel,
Timestamp: time.Now().Unix() * 1000,
}
}
// 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{}
json.Unmarshal([]byte(message), &x)
if len(x) < 1 {
return nil, errors.New("unsupported message type")
}
if x[0].(string) != "~#c4" {
return nil, errors.New("unsupported message type")
}
properties := x[1].([]interface{})
return &Msg{
From: "TODO : someone",
Text: properties[0].(string),
Timestamp: int64(properties[3].(float64)),
Raw: string(message),
}, nil
}

56
subscription.go Normal file
View File

@ -0,0 +1,56 @@
package sdk
import (
"fmt"
"log"
"time"
)
// MsgHandler is a callback function that processes messages delivered to
// asynchronous subscribers.
type MsgHandler func(msg *Msg)
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, err := channel.conn.rpc.Call(cmd)
if err != nil {
log.Fatalf("Error when sending request to server: %s", err)
}
f := unmarshalJSON(response.(string))
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
utils.go 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:])
}