diff --git a/service/README.md b/service/README.md index e8c0c39..b8def01 100644 --- a/service/README.md +++ b/service/README.md @@ -19,6 +19,7 @@ processHttp.Listen(1500, "http", pubK) ``` curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow"}' http://localhost:1500/addClaim + {"error":false,"response":""} ``` @@ -27,6 +28,7 @@ curl -d '{"censusID":"GoT_Favorite", "claimData":"Jon Snow", "timeStamp":"1547814675", "signature":"a117c4ce12b29090884112ffe57e664f007e7ef142a1679996e2d34fd2b852fe76966e47932f1e9d3a54610d0f361383afe2d9aab096e15d136c236abb0a0d0e"}' http://localhost:1500/addClaim + {"error":false,"response":""} ``` @@ -34,6 +36,7 @@ The signature message is a concatenation of the following strings: `censusID, cl ``` curl -d '{"censusID":"GoT_Favorite","claimData":"Tyrion"}' http://localhost:1500/addClaim + {"error":false,"response":""} ``` @@ -41,6 +44,7 @@ curl -d '{"censusID":"GoT_Favorite","claimData":"Tyrion"}' http://localhost:1500 ``` curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow"}' http://localhost:1500/genProof + {"error":false,"response":"0x000000000000000000000000000000000000000000000000000000000000000352f3ca2aaf635ec2ae4452f6a65be7bca72678287a8bb08ad4babfcccd76c2fef1aac7675261bf6d12c746fb7907beea6d1f1635af93ba931eec0c6a747ecc37"} ``` @@ -48,10 +52,35 @@ curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow"}' http://localhost:15 ``` curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow", "proofData": "0x0000000000000000000000000000000000000000000000000000000000000000000123"}' http://localhost:1500/checkProof + {"error":false,"response":"invalid"} ``` ``` curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow", "proofData": "0x000000000000000000000000000000000000000000000000000000000000000352f3ca2aaf635ec2ae4452f6a65be7bca72678287a8bb08ad4babfcccd76c2fef1aac7675261bf6d12c746fb7907beea6d1f1635af93ba931eec0c6a747ecc37"}' http://localhost:1500/checkProof + {"error":false,"response":"valid"} ``` + +#### make snapshot + +Snapshots are static and unmutable copies of a specific census + +``` +curl -d '{"censusID":"GoT_Favorite"}' http://localhost:1500/snapshot + +{"error":false,"response":"snaphost.GoT_Favorite.1548169813"} +``` + +The name for the snapshot is "snaphost.GoT_Favorite.1548169813" + +Now you can use it as censusID for checkProof, genProof and dump. But addClaim is not longer allowed. + +#### dump + +Dump contents of a specific censusID (values) + +``` +curl -d '{"censusID":"GoT_Favorite"}' http://localhost:1500/dump +{"error":false,"response":"[\"Tyrion\",\"Jon Snow\"]"} +``` \ No newline at end of file diff --git a/service/processHttp.go b/service/processHttp.go index ef3dfea..4361efe 100644 --- a/service/processHttp.go +++ b/service/processHttp.go @@ -107,7 +107,6 @@ func claimHandler(w http.ResponseWriter, req *http.Request, op string) { if op == "add" { msg := fmt.Sprintf("%s%s%s", c.CensusID, c.ClaimData, c.TimeStamp) - log.Printf("Msg to check: %s", msg) if auth := checkAuth(c.TimeStamp, c.Signature, msg); auth { err = T.AddClaim([]byte(c.ClaimData)) } else { @@ -124,6 +123,38 @@ func claimHandler(w http.ResponseWriter, req *http.Request, op string) { resp.Response = T.GetRoot() } + if op == "dump" { + values, err := T.Dump() + if err != nil { + resp.Error = true + resp.Response = fmt.Sprint(err) + } else { + jValues, err := json.Marshal(values) + if err != nil { + resp.Error = true + resp.Response = fmt.Sprint(err) + } else { + resp.Response = string(jValues) + } + } + } + + if op == "snapshot" { + msg := fmt.Sprintf("%s%s%s", c.CensusID, c.ClaimData, c.TimeStamp) + if auth := checkAuth(c.TimeStamp, c.Signature, msg); auth { + snapshotNamespace, err := T.Snapshot() + if err != nil { + resp.Error = true + resp.Response = fmt.Sprint(err) + } else { + resp.Response = snapshotNamespace + } + } else { + resp.Error = true + resp.Response = "invalid authentication" + } + } + if op == "check" { if len(c.ProofData) < 1 { resp.Error = true @@ -172,6 +203,12 @@ func Listen(port int, proto string, pubKey string) { http.HandleFunc("/getRoot", func(w http.ResponseWriter, r *http.Request) { claimHandler(w, r, "root") }) + http.HandleFunc("/snapshot", func(w http.ResponseWriter, r *http.Request) { + claimHandler(w, r, "snapshot") + }) + http.HandleFunc("/dump", func(w http.ResponseWriter, r *http.Request) { + claimHandler(w, r, "dump") + }) if len(pubKey) > 1 { log.Printf("Enabling signature authentication with %s\n", pubKey) diff --git a/tree/README.md b/tree/README.md index 7a75955..8904de6 100644 --- a/tree/README.md +++ b/tree/README.md @@ -19,4 +19,4 @@ Example of usage: #### To-Do -+ Add export/import methods +Avoid duplicates on dump/snapshot \ No newline at end of file diff --git a/tree/tree.go b/tree/tree.go index 74263a3..618f86e 100644 --- a/tree/tree.go +++ b/tree/tree.go @@ -1,36 +1,45 @@ package tree import ( - "github.com/vocdoni/go-iden3/db" - "github.com/vocdoni/go-iden3/merkletree" - mkcore "github.com/vocdoni/go-iden3/core" - common3 "github.com/vocdoni/go-iden3/common" - "os/user" + "errors" + "fmt" + "os/user" + "strings" + "time" + + common3 "github.com/vocdoni/go-iden3/common" + mkcore "github.com/vocdoni/go-iden3/core" + "github.com/vocdoni/go-iden3/db" + "github.com/vocdoni/go-iden3/merkletree" ) type Tree struct { - Namespace string - Storage string - Tree *merkletree.MerkleTree + Namespace string + Storage string + Tree *merkletree.MerkleTree + DbStorage *db.LevelDbStorage } func (t *Tree) Init() error { - if len(t.Storage) < 1 { - usr, err := user.Current() - if err == nil { - t.Storage = usr.HomeDir + "/.dvote/Tree" - } else { t.Storage = "./dvoteTree" } - } - mtdb, err := db.NewLevelDbStorage(t.Storage, false) + if len(t.Storage) < 1 { + usr, err := user.Current() + if err == nil { + t.Storage = usr.HomeDir + "/.dvote/Tree" + } else { + t.Storage = "./dvoteTree" + } + } + mtdb, err := db.NewLevelDbStorage(t.Storage, false) if err != nil { - return err + return err } mt, err := merkletree.New(mtdb, 140) if err != nil { - return err + return err } - t.Tree = mt - return nil + t.DbStorage = mtdb + t.Tree = mt + return nil } func (t *Tree) Close() { @@ -38,29 +47,72 @@ func (t *Tree) Close() { } func (t *Tree) AddClaim(data []byte) error { - claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil) - return t.Tree.Add(claim) + isSnapshot := strings.Contains(t.Namespace, "snapshot.") + if isSnapshot { + return errors.New("No new claims can be added to a Snapshot") + } + claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil) + return t.Tree.Add(claim) } func (t *Tree) GenProof(data []byte) (string, error) { - claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil) - mp, err := t.Tree.GenerateProof(claim.Hi()) - if err!=nil { - return "", err - } - mpHex := common3.BytesToHex(mp) - return mpHex, nil + claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil) + mp, err := t.Tree.GenerateProof(claim.Hi()) + if err != nil { + return "", err + } + mpHex := common3.BytesToHex(mp) + return mpHex, nil } func (t *Tree) CheckProof(data []byte, mpHex string) (bool, error) { - mp, err := common3.HexToBytes(mpHex) - if err != nil { - return false, err - } - claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil) - return merkletree.CheckProof(t.Tree.Root(), mp, claim.Hi(), claim.Ht(), t.Tree.NumLevels()), nil + mp, err := common3.HexToBytes(mpHex) + if err != nil { + return false, err + } + claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil) + return merkletree.CheckProof(t.Tree.Root(), mp, claim.Hi(), claim.Ht(), t.Tree.NumLevels()), nil +} + +func (t *Tree) GetRoot() string { + return t.Tree.Root().Hex() +} + +/* Dump, Export and Snapshot functions are a bit tricky. + Since go-iden3 does not provide the necessary tools. Low level operations must be performed. + Once go-iden3 API is mature enough, these functions must be adapted. + + To explore: Values are stored twice in the BD? +*/ +func (t *Tree) Dump() ([]string, error) { + var response []string + substorage := t.DbStorage.WithPrefix([]byte(t.Namespace)) + nsHash := merkletree.HashBytes([]byte(t.Namespace)) + substorage.Iterate(func(key, value []byte) { + nsValue := value[5:37] + if fmt.Sprint(nsHash) == fmt.Sprint(nsValue) { + response = append(response, string(value[69:])) + } + }) + return response, nil } -func (t *Tree) GetRoot() (string) { - return t.Tree.Root().Hex() +func (t *Tree) Snapshot() (string, error) { + substorage := t.DbStorage.WithPrefix([]byte(t.Namespace)) + nsHash := merkletree.HashBytes([]byte(t.Namespace)) + currentTime := int64(time.Now().Unix()) + snapshotNamespace := fmt.Sprintf("snapshot.%s.%d", t.Namespace, currentTime) + substorage.Iterate(func(key, value []byte) { + nsValue := value[5:37] + if fmt.Sprint(nsHash) == fmt.Sprint(nsValue) { + data := value[69:] + //fmt.Printf(" Adding value: %s\n", data) + claim := mkcore.NewGenericClaim(snapshotNamespace, "default", data, nil) + err := t.Tree.Add(claim) + if err != nil { + fmt.Println(err) + } + } + }) + return snapshotNamespace, nil }