All work so farpubsub_testing
@ -1,3 +1,6 @@ |
|||||
# votingRelay |
# votingRelay |
||||
|
|
||||
dVote library for Relay |
dVote library for Relay |
||||
|
|
||||
|
With application running, you can submit fake votes with a json request of the form: |
||||
|
curl -H 'Content-Type: application/json'e":"package","Nonce":"bm9uY2U=","KeyProof":"cHJvb2Y=","Package":"dm90ZXBhY2thZ2U=","Timestamp":"2018-12-14T15:04:05Z"}' http://localhost:8080/submit |
@ -0,0 +1,84 @@ |
|||||
|
package batch |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"github.com/vocdoni/dvote-relay/types" |
||||
|
"github.com/vocdoni/dvote-relay/db" |
||||
|
"encoding/json" |
||||
|
) |
||||
|
|
||||
|
var rdb *db.LevelDbStorage |
||||
|
var bdb *db.LevelDbStorage |
||||
|
var BatchSignal chan bool |
||||
|
var BatchSize int |
||||
|
var err error |
||||
|
|
||||
|
func Setup(l *db.LevelDbStorage) { |
||||
|
rdb = l.WithPrefix([]byte("relay_")) |
||||
|
bdb = l.WithPrefix([]byte("batch_")) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//add (queue for counting)
|
||||
|
func Add(ballot types.Ballot) error { |
||||
|
//this is probably adding []
|
||||
|
//err := bdb.Put(fmt.Sprintf("%v", p.Nullifier)),[]byte(fmt.Sprintf("%v", p)))
|
||||
|
var b []byte |
||||
|
var n []byte |
||||
|
var err error |
||||
|
b, err = json.Marshal(ballot) |
||||
|
n, err = json.Marshal(ballot.Nullifier) |
||||
|
err = bdb.Put(n,b) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
//this actually needs to see if it was added
|
||||
|
if bdb.Count() >= BatchSize { |
||||
|
BatchSignal <- true |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
//create (return batch)
|
||||
|
//k is []byte 'batch_' + nullifier
|
||||
|
//v is []byte package
|
||||
|
//returns slice of nullifiers, batch json
|
||||
|
func Fetch() ([]string, []string) { |
||||
|
var n []string |
||||
|
var b []string |
||||
|
iter := bdb.Iter() |
||||
|
for iter.Next() { |
||||
|
k := iter.Key() |
||||
|
v := iter.Value() |
||||
|
err := iter.Error() |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
n = append(n, string(k[6:])) |
||||
|
b = append(b, string(v)) |
||||
|
} |
||||
|
iter.Release() |
||||
|
// jn, err := json.Marshal(n)
|
||||
|
// if err != nil {
|
||||
|
// panic(err)
|
||||
|
// }
|
||||
|
// jb, err := json.Marshal(b)
|
||||
|
// if err != nil {
|
||||
|
// panic(err)
|
||||
|
// }
|
||||
|
return n, b |
||||
|
} |
||||
|
|
||||
|
//move from bdb to rdb once pinned
|
||||
|
func Compact(n []string) { |
||||
|
for _, k := range n { |
||||
|
//fmt.Println(k)
|
||||
|
v, err := bdb.Get([]byte(k)) |
||||
|
if err != nil { |
||||
|
fmt.Println(err.Error()) |
||||
|
} |
||||
|
rdb.Put([]byte(k), v) |
||||
|
bdb.Delete([]byte(k)) |
||||
|
} |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
package batch |
@ -0,0 +1,63 @@ |
|||||
|
package data |
||||
|
|
||||
|
import ( |
||||
|
"os" |
||||
|
"fmt" |
||||
|
"bytes" |
||||
|
"io/ioutil" |
||||
|
shell "github.com/ipfs/go-ipfs-api" |
||||
|
) |
||||
|
|
||||
|
func Publish(object []byte) string { |
||||
|
sh := shell.NewShell("localhost:5001") |
||||
|
cid, err := sh.Add(bytes.NewBuffer(object)) |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(os.Stderr, "error: %s", err) |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
return cid |
||||
|
} |
||||
|
|
||||
|
func Pin(path string) { |
||||
|
sh := shell.NewShell("localhost:5001") |
||||
|
err := sh.Pin(path) |
||||
|
if err != nil{ |
||||
|
fmt.Fprintf(os.Stderr, "error: %s", err) |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
func Retrieve(hash string) []byte { |
||||
|
sh := shell.NewShell("localhost:5001") |
||||
|
reader, err := sh.Cat(hash) |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(os.Stderr, "error: %s", err) |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
content, err := ioutil.ReadAll(reader) |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(os.Stderr, "error: %s", err) |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
return content |
||||
|
} |
||||
|
|
||||
|
func PsSubscribe(topic string) *shell.PubSubSubscription { |
||||
|
sh := shell.NewShell("localhost:5001") |
||||
|
sub, err := sh.PubSubSubscribe(topic) |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(os.Stderr, "error: %s", err) |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
return sub |
||||
|
} |
||||
|
|
||||
|
func PsPublish(topic, data string) { |
||||
|
sh := shell.NewShell("localhost:5001") |
||||
|
err := sh.PubSubPublish(topic, data) |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(os.Stderr, "error: %s", err) |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
package data |
||||
|
|
||||
|
import ( |
||||
|
"testing" |
||||
|
"fmt" |
||||
|
"encoding/json" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
func TestPublishAndRetrieve(t *testing.T) { |
||||
|
t.Log("Testing adding json") |
||||
|
|
||||
|
exampleVote := votePacket{ |
||||
|
000001, |
||||
|
"12309801002", |
||||
|
"nynnynnnynnnyy", |
||||
|
"132498-0-02103908", |
||||
|
} |
||||
|
|
||||
|
testObject, err := json.Marshal(exampleVote) |
||||
|
if err != nil { |
||||
|
t.Errorf("Bad test JSON: %s", err) |
||||
|
} |
||||
|
prepub := string(testObject) |
||||
|
|
||||
|
hash := publish(testObject) |
||||
|
content := retrieve(hash) |
||||
|
postpub := string(content) |
||||
|
//fmt.Println(hash)
|
||||
|
//fmt.Println(string(content))
|
||||
|
if strings.Compare(prepub,postpub) != 0 { |
||||
|
t.Errorf("Published file doesn't match. Expected:\n %s \n Got: \n %s \n", prepub, postpub) |
||||
|
} |
||||
|
} |
@ -0,0 +1,113 @@ |
|||||
|
package db |
||||
|
|
||||
|
// modified from https://github.com/iden3/go-iden3/blob/master/db/leveldb.go
|
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
|
||||
|
"github.com/syndtr/goleveldb/leveldb" |
||||
|
// "github.com/syndtr/goleveldb/leveldb/errors"
|
||||
|
"github.com/syndtr/goleveldb/leveldb/opt" |
||||
|
"github.com/syndtr/goleveldb/leveldb/util" |
||||
|
"github.com/syndtr/goleveldb/leveldb/iterator" |
||||
|
) |
||||
|
|
||||
|
|
||||
|
type LevelDbStorage struct { |
||||
|
ldb *leveldb.DB |
||||
|
prefix []byte |
||||
|
} |
||||
|
|
||||
|
func NewLevelDbStorage(path string, errorIfMissing bool) (*LevelDbStorage, error) { |
||||
|
o := &opt.Options{ |
||||
|
ErrorIfMissing: errorIfMissing, |
||||
|
} |
||||
|
ldb, err := leveldb.OpenFile(path, o) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return &LevelDbStorage{ldb, []byte{}}, nil |
||||
|
} |
||||
|
|
||||
|
type storageInfo struct { |
||||
|
KeyCount int |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) Count() int { |
||||
|
|
||||
|
keycount := 0 |
||||
|
db := l.ldb |
||||
|
iter := db.NewIterator(util.BytesPrefix(l.prefix), nil) |
||||
|
for iter.Next() { |
||||
|
keycount++ |
||||
|
} |
||||
|
iter.Release() |
||||
|
if err := iter.Error(); err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
return keycount |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) Info() string { |
||||
|
|
||||
|
keycount := 0 |
||||
|
db := l.ldb |
||||
|
iter := db.NewIterator(util.BytesPrefix(l.prefix), nil) |
||||
|
for iter.Next() { |
||||
|
keycount++ |
||||
|
} |
||||
|
iter.Release() |
||||
|
if err := iter.Error(); err != nil { |
||||
|
return err.Error() |
||||
|
} |
||||
|
json, _ := json.MarshalIndent( |
||||
|
storageInfo{keycount}, |
||||
|
"", " ", |
||||
|
) |
||||
|
return string(json) |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) WithPrefix(prefix []byte) *LevelDbStorage { |
||||
|
return &LevelDbStorage{l.ldb, append(l.prefix, prefix...)} |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) Get(key []byte) ([]byte, error) { |
||||
|
v, err := l.ldb.Get(append(l.prefix, key[:]...), nil) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return v, err |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) Put(key []byte, value []byte) error { |
||||
|
err := l.ldb.Put(append(l.prefix, key[:]...), value, nil) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) Delete(key []byte) error { |
||||
|
err := l.ldb.Delete(append(l.prefix, key[:]...), nil) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) Close() { |
||||
|
if err := l.ldb.Close(); err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) Iter() iterator.Iterator { |
||||
|
db := l.ldb |
||||
|
i := db.NewIterator(util.BytesPrefix(l.prefix), nil) |
||||
|
return i |
||||
|
} |
||||
|
|
||||
|
func (l *LevelDbStorage) LevelDB() *leveldb.DB { |
||||
|
return l.ldb |
||||
|
} |
||||
|
|
@ -0,0 +1,104 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"os" |
||||
|
"strconv" |
||||
|
"time" |
||||
|
"fmt" |
||||
|
"math/rand" |
||||
|
"encoding/json" |
||||
|
"encoding/base64" |
||||
|
"net/http" |
||||
|
"bytes" |
||||
|
"io/ioutil" |
||||
|
"github.com/vocdoni/dvote-relay/types" |
||||
|
) |
||||
|
|
||||
|
func makeBallot() string { |
||||
|
var bal types.Ballot |
||||
|
|
||||
|
bal.Type = "ballot0" |
||||
|
|
||||
|
pidBytes := make([]byte, 32) |
||||
|
rand.Read(pidBytes) |
||||
|
bal.PID = base64.StdEncoding.EncodeToString(pidBytes) |
||||
|
|
||||
|
nullifier := make([]byte, 32) |
||||
|
rand.Read(nullifier) |
||||
|
bal.Nullifier = nullifier |
||||
|
|
||||
|
vote := make([]byte, 32) |
||||
|
rand.Read(vote) |
||||
|
bal.Vote = vote |
||||
|
|
||||
|
franchise := make([]byte, 32) |
||||
|
rand.Read(franchise) |
||||
|
bal.Franchise = franchise |
||||
|
|
||||
|
|
||||
|
b, err := json.Marshal(bal) |
||||
|
if err != nil { |
||||
|
fmt.Println(err) |
||||
|
return "error" |
||||
|
} |
||||
|
//todo: add encryption, pow
|
||||
|
return string(b) |
||||
|
} |
||||
|
|
||||
|
func makeEnvelope(ballot string) string { |
||||
|
var env types.Envelope |
||||
|
|
||||
|
env.Type = "envelope0" |
||||
|
|
||||
|
env.Nonce = rand.Uint64() |
||||
|
|
||||
|
kp := make([]byte, 4) |
||||
|
rand.Read(kp) |
||||
|
env.KeyProof = kp |
||||
|
|
||||
|
env.Ballot = []byte(ballot) |
||||
|
|
||||
|
env.Timestamp = time.Now() |
||||
|
|
||||
|
e, err := json.Marshal(env) |
||||
|
if err != nil { |
||||
|
fmt.Println(err) |
||||
|
return "error" |
||||
|
} |
||||
|
//todo: add encryption, pow
|
||||
|
return string(e) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
func main() { |
||||
|
interval := os.Args[1] |
||||
|
i, _ := strconv.Atoi(interval) |
||||
|
timer := time.NewTicker(time.Millisecond * time.Duration(i)) |
||||
|
rand.Seed(time.Now().UnixNano()) |
||||
|
url := "http://localhost:8090/submit" |
||||
|
fmt.Println("URL:>", url) |
||||
|
|
||||
|
for { |
||||
|
select { |
||||
|
case <- timer.C: |
||||
|
fmt.Println(makeEnvelope(makeBallot())) |
||||
|
var jsonStr = []byte(makeEnvelope(makeBallot())) |
||||
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) |
||||
|
req.Header.Set("X-Custom-Header", "myvalue") |
||||
|
req.Header.Set("Content-Type", "application/json") |
||||
|
|
||||
|
client := &http.Client{} |
||||
|
resp, err := client.Do(req) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
defer resp.Body.Close() |
||||
|
fmt.Println("response Status:", resp.Status) |
||||
|
fmt.Println("response Headers:", resp.Header) |
||||
|
body, _ := ioutil.ReadAll(resp.Body) |
||||
|
fmt.Println("response Body:", string(body)) |
||||
|
default: |
||||
|
continue |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,71 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"time" |
||||
|
"encoding/gob" |
||||
|
"bytes" |
||||
|
"github.com/vocdoni/dvote-relay/batch" |
||||
|
"github.com/vocdoni/dvote-relay/net" |
||||
|
"github.com/vocdoni/dvote-relay/db" |
||||
|
"github.com/vocdoni/dvote-relay/data" |
||||
|
) |
||||
|
|
||||
|
var dbPath = "~/.dvote/relay.db" |
||||
|
var batchSeconds = 10 //seconds
|
||||
|
var batchSize = 3 //packets
|
||||
|
|
||||
|
var err error |
||||
|
var batchTimer *time.Ticker |
||||
|
var batchSignal chan bool |
||||
|
var signal bool |
||||
|
|
||||
|
|
||||
|
func main() { |
||||
|
|
||||
|
db, err := db.NewLevelDbStorage(dbPath, false) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
defer db.Close() |
||||
|
|
||||
|
batch.Setup(db) |
||||
|
|
||||
|
batchTimer = time.NewTicker(time.Second * time.Duration(batchSeconds)) |
||||
|
batchSignal = make(chan bool) |
||||
|
|
||||
|
batch.BatchSignal = batchSignal |
||||
|
batch.BatchSize = batchSize |
||||
|
|
||||
|
fmt.Println("Entering main loop") |
||||
|
go net.Listen("8090") |
||||
|
for { |
||||
|
select { |
||||
|
case <- batchTimer.C: |
||||
|
fmt.Println("Timer triggered") |
||||
|
// fmt.Println(batch.Create())
|
||||
|
//replace with chain link
|
||||
|
case signal := <-batchSignal: |
||||
|
if signal == true { |
||||
|
fmt.Println("Signal triggered") |
||||
|
ns, bs := batch.Fetch() |
||||
|
buf := &bytes.Buffer{} |
||||
|
gob.NewEncoder(buf).Encode(bs) |
||||
|
bb := buf.Bytes() |
||||
|
cid := data.Publish(bb) |
||||
|
data.Pin(cid) |
||||
|
fmt.Printf("Batch published at: %s \n", cid) |
||||
|
// add to ipfs
|
||||
|
// add to chain
|
||||
|
// announce to pubsub
|
||||
|
//fmt.Println("Nullifiers:")
|
||||
|
//fmt.Println(n)
|
||||
|
//fmt.Println("Batch:")
|
||||
|
//fmt.Println(b)
|
||||
|
batch.Compact(ns) |
||||
|
} |
||||
|
default: |
||||
|
continue |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,64 @@ |
|||||
|
package net |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"fmt" |
||||
|
"net/http" |
||||
|
"io" |
||||
|
"github.com/vocdoni/dvote-relay/batch" |
||||
|
"github.com/vocdoni/dvote-relay/types" |
||||
|
) |
||||
|
|
||||
|
|
||||
|
func parse(rw http.ResponseWriter, request *http.Request) { |
||||
|
decoder := json.NewDecoder(request.Body) |
||||
|
|
||||
|
var e types.Envelope |
||||
|
var b types.Ballot |
||||
|
|
||||
|
err := decoder.Decode(&e) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
|
||||
|
err = json.Unmarshal(e.Ballot, &b) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//check PoW
|
||||
|
//check key
|
||||
|
//decrypt
|
||||
|
//check franchise
|
||||
|
//construct packet
|
||||
|
|
||||
|
//this should should be randomized, or actually taken from input
|
||||
|
//b.PID = "1"
|
||||
|
//b.Nullifier = []byte{1,2,3}
|
||||
|
//b.Vote = []byte{4,5,6}
|
||||
|
//b.Franchise = []byte{7,8,9}
|
||||
|
|
||||
|
err = batch.Add(b) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
|
||||
|
j, err := json.Marshal(e) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
io.WriteString(rw, string(j)) |
||||
|
} |
||||
|
|
||||
|
func Listen(port string) { |
||||
|
http.HandleFunc("/submit", parse) |
||||
|
//add waitgroup
|
||||
|
func() { |
||||
|
fmt.Println("serving on " + port) |
||||
|
err := http.ListenAndServe(":" + port, nil) |
||||
|
if err != nil { |
||||
|
panic("ListenAndServe: " + err.Error()) |
||||
|
} |
||||
|
}() |
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
package net |
||||
|
|
||||
|
import ( |
||||
|
"testing" |
||||
|
"encoding/json" |
||||
|
"net/http" |
||||
|
"fmt" |
||||
|
"time" |
||||
|
"bytes" |
||||
|
"io/ioutil" |
||||
|
) |
||||
|
|
||||
|
//func generateSubmission() submission {
|
||||
|
|
||||
|
//}
|
||||
|
|
||||
|
func TestListen(t *testing.T) { |
||||
|
t.Log("Testing listener") |
||||
|
|
||||
|
testSubmission := submission { |
||||
|
"package", |
||||
|
[]byte("012345678"), |
||||
|
[]byte("012345678"), |
||||
|
[]byte("012345678"), |
||||
|
time.Now(), |
||||
|
} |
||||
|
|
||||
|
listen("8080") |
||||
|
|
||||
|
url := "http://localhost:8080/submit" |
||||
|
fmt.Println("URL:>", url) |
||||
|
|
||||
|
j, err := json.Marshal(testSubmission) |
||||
|
if err != nil { |
||||
|
t.Errorf("Bad test JSON: %s", err) |
||||
|
} |
||||
|
|
||||
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(j)) |
||||
|
req.Header.Set("Content-Type", "application/json") |
||||
|
|
||||
|
client := &http.Client{} |
||||
|
resp, err := client.Do(req) |
||||
|
if err != nil { |
||||
|
t.Errorf("Error in client: %s", err) |
||||
|
} |
||||
|
defer resp.Body.Close() |
||||
|
|
||||
|
fmt.Println("response Status:", resp.Status) |
||||
|
fmt.Println("response Headers:", resp.Header) |
||||
|
body, _ := ioutil.ReadAll(resp.Body) |
||||
|
fmt.Println("response Body:", string(body)) |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
package types |
||||
|
|
||||
|
import ( |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type Ballot struct { |
||||
|
Type string |
||||
|
PID string |
||||
|
Nullifier []byte |
||||
|
Vote []byte |
||||
|
Franchise []byte |
||||
|
} |
||||
|
|
||||
|
type Envelope struct { |
||||
|
Type string |
||||
|
Nonce uint64 |
||||
|
KeyProof []byte |
||||
|
Ballot []byte |
||||
|
Timestamp time.Time |
||||
|
} |
||||
|
|
||||
|
type Batch struct { |
||||
|
Type string |
||||
|
Nullifiers []string |
||||
|
URL string |
||||
|
TXID string |
||||
|
Nonce []byte |
||||
|
Signature string |
||||
|
} |
||||
|
|