mirror of
https://github.com/arnaucube/go-dvote.git
synced 2026-02-28 05:26:46 +01:00
Merge branch 'feature/new_merkletree'
Signed-off-by: p4u <p4u@dabax.net>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
Reference implementation of a voting census service running on the Vocdoni platform
|
||||
|
||||
#### Compile
|
||||
## Compile
|
||||
|
||||
In a GO ready environment:
|
||||
|
||||
@@ -11,35 +11,67 @@ go get -u github.com/vocdoni/dvote-census/...
|
||||
go build -o censusHttpService github.com/vocdoni/dvote-census/cmd/censushttp
|
||||
```
|
||||
|
||||
#### Usage
|
||||
## Usage
|
||||
|
||||
`./censusHttpService <port> [publicKey]`
|
||||
`./censusHttpService <port> <censusId>[:pubKey] [<censusId>[:pubKey] ...]`
|
||||
|
||||
Example:
|
||||
Example
|
||||
|
||||
```
|
||||
./censusHttpService 1500
|
||||
2018/12/17 09:54:20 Starting process HTTP service on port 1500
|
||||
2018/12/17 09:54:20 Starting server in http mode
|
||||
./censusHttpService 1500 Got_Favorite
|
||||
2019/02/12 10:20:16 Starting process HTTP service on port 1500 for namespace GoT_Favorite
|
||||
2019/02/12 10:20:16 Starting server in http mode
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
A HTTP jSON endpoint is available with the following possible fields: `censusId`, `claimData`, `rootHash` and `proofData`.
|
||||
|
||||
If `pubKey` has been configured for a specific `censusId`, then two more methods are available (`timeStamp` and `signature`) to provide authentication.
|
||||
|
||||
The next table shows the available methods and its relation with the fields.
|
||||
|
||||
| method | censusId | claimData | rootHash | proofData | protected? | description |
|
||||
|------------|-----------|-----------|----------|-----------|------------|------------|
|
||||
| `addCLaim` | mandatory | mandatory | none | none | yes | adds a new claim to the merkle tree |
|
||||
| `getRoot` | mandatory | none | none | none | no | get the current merkletree root hash
|
||||
| `genProof` | mandatory | mandatory | optional | none | no | generate the merkle proof for a given claim
|
||||
| `checkProof` | mandatory | mandatory | optional | mandatory | no | check a claim and its merkle proof
|
||||
| `getIdx` | mandatory | mandatory | optional | none | no | get the merkletree data index of a given claim
|
||||
| `dump` | mandatory | none | optional | none | yes | list the contents of the census for a given hash
|
||||
|
||||
|
||||
## Signature
|
||||
|
||||
The signature provides authentication by signing a concatenation of the following strings (even if empty) without spaces: `censusId rootHash claimData timeStamp`.
|
||||
|
||||
The `timeStamp` when received on the server side must not differ more than 10 seconds from the current UNIX time.
|
||||
|
||||
## Examples
|
||||
|
||||
#### add claims
|
||||
|
||||
Add two new claims, one for `Jon Snow` and another for `Tyrion`.
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow"}' http://localhost:1500/addClaim
|
||||
|
||||
{"error":false,"response":""}
|
||||
```
|
||||
|
||||
If public key authentication enabled, `signature` field is required using Nancl signature.
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite","claimData":"Tyrion"}' http://localhost:1500/addClaim
|
||||
|
||||
The signed message is expected to be a concatenation of the following fields: `censusID, claimData, timeStamp`
|
||||
{"error":false,"response":""}
|
||||
```
|
||||
|
||||
In case signature is enabled:
|
||||
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite",
|
||||
curl -d '{
|
||||
"censusID":"GoT_Favorite",
|
||||
"claimData":"Jon Snow",
|
||||
"timeStamp":"1547814675",
|
||||
"signature":"a117c4ce12b29090884112ffe57e664f007e7ef142a1679996e2d34fd2b852fe76966e47932f1e9d3a54610d0f361383afe2d9aab096e15d136c236abb0a0d0e"}' http://localhost:1500/addClaim
|
||||
"timeStamp":"1547814675",
|
||||
"signature":"a117c4ce12b29090884112ffe57e664f007e7ef142a1679996e2d34fd2b852fe76966e47932f1e9d3a54610d0f361383afe2d9aab096e15d136c236abb0a0d0e" }' http://localhost:1500/addClaim
|
||||
|
||||
{"error":false,"response":""}
|
||||
```
|
||||
@@ -47,88 +79,50 @@ curl -d '{"censusID":"GoT_Favorite",
|
||||
|
||||
#### generate proof
|
||||
|
||||
Generate a merkle proof for the claim `Jon Snow`
|
||||
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow"}' http://localhost:1500/genProof
|
||||
|
||||
{"error":false,"response":"0x000000000000000000000000000000000000000000000000000000000000000352f3ca2aaf635ec2ae4452f6a65be7bca72678287a8bb08ad4babfcccd76c2fef1aac7675261bf6d12c746fb7907beea6d1f1635af93ba931eec0c6a747ecc37"}
|
||||
{"error":false,"response":"0x000200000000000000000000000000000000000000000000000000000000000212f8134039730791388a9bd0460f9fbd0757327212a64b3a2b0f0841ce561ee3"}
|
||||
```
|
||||
|
||||
If `rootHash` is specified, the proof will be calculated for the given root hash.
|
||||
|
||||
#### get root
|
||||
|
||||
The previous merkle proof is valid only for the current root hash. Let's get it
|
||||
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite"}' http://localhost:1500/getRoot
|
||||
|
||||
{"error":false,"response":"0x2f0ddde5cb995eae23dc3b75a5c0333f1cc89b73f3a00b0fe71996fb90fef04b"}
|
||||
```
|
||||
|
||||
|
||||
#### check proof
|
||||
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow", "proofData": "0x0000000000000000000000000000000000000000000000000000000000000000000123"}' http://localhost:1500/checkProof
|
||||
|
||||
{"error":false,"response":"invalid"}
|
||||
```
|
||||
Now let's check if the proof is valid
|
||||
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow", "proofData": "0x000000000000000000000000000000000000000000000000000000000000000352f3ca2aaf635ec2ae4452f6a65be7bca72678287a8bb08ad4babfcccd76c2fef1aac7675261bf6d12c746fb7907beea6d1f1635af93ba931eec0c6a747ecc37"}' http://localhost:1500/checkProof
|
||||
curl -d '{
|
||||
"censusID":"GoT_Favorite","claimData":"Jon Snow",
|
||||
"rootHash":"0x2f0ddde5cb995eae23dc3b75a5c0333f1cc89b73f3a00b0fe71996fb90fef04b",
|
||||
"proofData":"0x000200000000000000000000000000000000000000000000000000000000000212f8134039730791388a9bd0460f9fbd0757327212a64b3a2b0f0841ce561ee3"}' 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":"0x8647692e073a10980d821764c65ca3fddbc606bb936f9812a7a806bfa97df152"}
|
||||
```
|
||||
|
||||
The name for the snapshot is "0x8647692e073a10980d821764c65ca3fddbc606bb936f9812a7a806bfa97df152" which represents the Root Hash.
|
||||
|
||||
Now you can use it as censusID for checkProof, getRoot, genProof and dump. But addClaim is not longer allowed.
|
||||
If `rootHash` is not specified, the current root hash is used.
|
||||
|
||||
#### dump
|
||||
|
||||
Dump contents of a specific censusID (values)
|
||||
Dump contents of a specific censusId (values)
|
||||
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite"}' http://localhost:1500/dump
|
||||
|
||||
{"error":false,"response":"[\"Tyrion\",\"Jon Snow\"]"}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
##### add claims
|
||||
|
||||
```
|
||||
curl -d '{"processID":"GoT_Favorite","claimData":"Jon Snow"}' http://localhost:1500/addClaim
|
||||
{"error":false,"response":""}
|
||||
```
|
||||
|
||||
```
|
||||
curl -d '{"processID":"GoT_Favorite","claimData":"Tyrion"}' http://localhost:1500/addClaim
|
||||
{"error":false,"response":""}
|
||||
```
|
||||
|
||||
##### generate proof
|
||||
|
||||
```
|
||||
curl -d '{"censusID":"GoT_Favorite","claimData":"Jon Snow"}' http://localhost:1500/genProof
|
||||
{"error":false,"response":"0x000000000000000000000000000000000000000000000000000000000000000352f3ca2aaf635ec2ae4452f6a65be7bca72678287a8bb08ad4babfcccd76c2fef1aac7675261bf6d12c746fb7907beea6d1f1635af93ba931eec0c6a747ecc37"}
|
||||
```
|
||||
|
||||
##### check proof
|
||||
|
||||
```
|
||||
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"}
|
||||
```
|
||||
|
||||
If `rootHash` is specified, dump will return the values for the merkle tree with the given root hash.
|
||||
@@ -4,13 +4,15 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
censusmanager "github.com/vocdoni/dvote-census/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal("Usage: " + os.Args[0] + " <port> [pubKey]")
|
||||
log.Fatal("Usage: " + os.Args[0] +
|
||||
" <port> <namespace>[:pubKey] [<namespace>[:pubKey]]...")
|
||||
os.Exit(2)
|
||||
}
|
||||
port, err := strconv.Atoi(os.Args[1])
|
||||
@@ -18,13 +20,18 @@ func main() {
|
||||
log.Fatal(err)
|
||||
os.Exit(2)
|
||||
}
|
||||
pubK := ""
|
||||
if len(os.Args) > 2 {
|
||||
log.Print("Public key authentication enabled")
|
||||
pubK = os.Args[2]
|
||||
for i := 2; i < len(os.Args); i++ {
|
||||
s := strings.Split(os.Args[i], ":")
|
||||
ns := s[0]
|
||||
pubK := ""
|
||||
if len(s) > 1 {
|
||||
pubK = s[1]
|
||||
log.Printf("Public Key authentication enabled on namespace %s\n", ns)
|
||||
}
|
||||
censusmanager.AddNamespace(ns, pubK)
|
||||
log.Printf("Starting process HTTP service on port %d for namespace %s\n",
|
||||
port, ns)
|
||||
}
|
||||
log.Print("Starting process HTTP service on port " + os.Args[1])
|
||||
censusmanager.T.Init()
|
||||
censusmanager.Listen(port, "http", pubK)
|
||||
censusmanager.Listen(port, "http")
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
## dvot-census http service library
|
||||
|
||||
#### How to use it
|
||||
|
||||
```
|
||||
processHttp.T.Init()
|
||||
processHttp.Listen(1500, "http", "")
|
||||
```
|
||||
|
||||
To enable authentication (using pubKey signature):
|
||||
|
||||
```
|
||||
pubK := "39f54ce5293520b689f6658ea7f3401f4ff931fa3d90dea21ff901cdf82bb8aa"
|
||||
processHttp.Listen(1500, "http", pubK)
|
||||
```
|
||||
@@ -6,22 +6,21 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/vocdoni/dvote-census/tree"
|
||||
"github.com/vocdoni/dvote-relay/crypto/signature"
|
||||
tree "github.com/vocdoni/dvote-census/tree"
|
||||
signature "github.com/vocdoni/dvote-relay/crypto/signature"
|
||||
)
|
||||
|
||||
const authTimeWindow = 10 // Time window (seconds) in which TimeStamp will be accepted if auth enabled
|
||||
|
||||
var authPubKey string
|
||||
|
||||
var T tree.Tree // MerkleTree dvote-census library
|
||||
var S signature.SignKeys // Signature dvote-relay library
|
||||
const hashSize = 32
|
||||
const authTimeWindow = 10 // Time window (seconds) in which TimeStamp will be accepted if auth enabled
|
||||
var MkTrees map[string]*tree.Tree // MerkleTree dvote-census library
|
||||
var Signatures map[string]string
|
||||
var Signature signature.SignKeys // Signature dvote-relay library
|
||||
|
||||
type Claim struct {
|
||||
CensusID string `json:"censusID"` // References to MerkleTree namespace
|
||||
CensusID string `json:"censusId"` // References to MerkleTree namespace
|
||||
RootHash string `json:"rootHash"` // References to MerkleTree rootHash
|
||||
ClaimData string `json:"claimData"` // Data to add to the MerkleTree
|
||||
ProofData string `json:"proofData"` // MerkleProof to check
|
||||
TimeStamp string `json:"timeStamp"` // Unix TimeStamp in seconds
|
||||
@@ -33,6 +32,20 @@ type Result struct {
|
||||
Response string `json:"response"`
|
||||
}
|
||||
|
||||
func AddNamespace(name, pubKey string) {
|
||||
if len(MkTrees) == 0 {
|
||||
MkTrees = make(map[string]*tree.Tree)
|
||||
}
|
||||
if len(Signatures) == 0 {
|
||||
Signatures = make(map[string]string)
|
||||
}
|
||||
|
||||
mkTree := tree.Tree{}
|
||||
mkTree.Init(name)
|
||||
MkTrees[name] = &mkTree
|
||||
Signatures[name] = pubKey
|
||||
}
|
||||
|
||||
func reply(resp *Result, w http.ResponseWriter) {
|
||||
err := json.NewEncoder(w).Encode(resp)
|
||||
if err != nil {
|
||||
@@ -50,8 +63,8 @@ func checkRequest(w http.ResponseWriter, req *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func checkAuth(timestamp, signature, message string) bool {
|
||||
if len(authPubKey) < 1 {
|
||||
func checkAuth(timestamp, signature, pubKey, message string) bool {
|
||||
if len(pubKey) < 1 {
|
||||
return true
|
||||
}
|
||||
currentTime := int64(time.Now().Unix())
|
||||
@@ -62,7 +75,7 @@ func checkAuth(timestamp, signature, message string) bool {
|
||||
}
|
||||
if timeStampRemote < currentTime+authTimeWindow &&
|
||||
timeStampRemote > currentTime-authTimeWindow {
|
||||
v, err := S.Verify(message, signature, authPubKey)
|
||||
v, err := Signature.Verify(message, signature, pubKey)
|
||||
if err != nil {
|
||||
log.Printf("Verification error: %s\n", err)
|
||||
}
|
||||
@@ -74,6 +87,7 @@ func checkAuth(timestamp, signature, message string) bool {
|
||||
func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
|
||||
var c Claim
|
||||
var resp Result
|
||||
|
||||
if ok := checkRequest(w, req); !ok {
|
||||
return
|
||||
}
|
||||
@@ -85,36 +99,25 @@ func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
|
||||
}
|
||||
|
||||
// Process data
|
||||
log.Printf("Received censusID:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n",
|
||||
c.CensusID, c.ClaimData, c.ProofData, c.TimeStamp, c.Signature)
|
||||
log.Printf("censusId:{%s} rootHash:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n",
|
||||
c.CensusID, c.RootHash, c.ClaimData, c.ProofData, c.TimeStamp, c.Signature)
|
||||
authString := fmt.Sprintf("%s%s%s%s", c.CensusID, c.RootHash, c.ClaimData, c.TimeStamp)
|
||||
resp.Error = false
|
||||
resp.Response = ""
|
||||
|
||||
censusFound := false
|
||||
if len(c.CensusID) > 0 {
|
||||
T.Namespace = c.CensusID
|
||||
} else {
|
||||
resp.Error = true
|
||||
resp.Response = "censusID is not valid"
|
||||
reply(&resp, w)
|
||||
return
|
||||
_, censusFound = MkTrees[c.CensusID]
|
||||
}
|
||||
|
||||
if len(c.ClaimData) < 0 {
|
||||
if !censusFound {
|
||||
resp.Error = true
|
||||
resp.Response = "data not valid"
|
||||
resp.Response = "censusId not valid or not found"
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
|
||||
if op == "add" {
|
||||
msg := fmt.Sprintf("%s%s%s", c.CensusID, c.ClaimData, c.TimeStamp)
|
||||
if auth := checkAuth(c.TimeStamp, c.Signature, msg); auth {
|
||||
if strings.HasPrefix(c.CensusID, "0x") {
|
||||
resp.Error = true
|
||||
resp.Response = "add claim to snapshot is not allowed"
|
||||
} else {
|
||||
err = T.AddClaim([]byte(c.ClaimData))
|
||||
}
|
||||
if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], authString); auth {
|
||||
err = MkTrees[c.CensusID].AddClaim([]byte(c.ClaimData))
|
||||
} else {
|
||||
resp.Error = true
|
||||
resp.Response = "invalid authentication"
|
||||
@@ -122,61 +125,107 @@ func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
|
||||
}
|
||||
|
||||
if op == "gen" {
|
||||
resp.Response, err = T.GenProof([]byte(c.ClaimData))
|
||||
var t *tree.Tree
|
||||
var err error
|
||||
if len(c.RootHash) > 1 { //if rootHash specified
|
||||
t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
|
||||
if err != nil {
|
||||
log.Printf("Snapshot error: %s", err.Error())
|
||||
resp.Error = true
|
||||
resp.Response = "invalid root hash"
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
} else { //if rootHash not specified use current tree
|
||||
t = MkTrees[c.CensusID]
|
||||
}
|
||||
resp.Response, err = t.GenProof([]byte(c.ClaimData))
|
||||
if err != nil {
|
||||
resp.Error = true
|
||||
resp.Response = err.Error()
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if op == "root" {
|
||||
resp.Response = T.GetRoot()
|
||||
resp.Response = MkTrees[c.CensusID].GetRoot()
|
||||
}
|
||||
|
||||
if op == "idx" {
|
||||
|
||||
}
|
||||
|
||||
if op == "dump" {
|
||||
values, err := T.Dump()
|
||||
var t *tree.Tree
|
||||
if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], authString); !auth {
|
||||
resp.Error = true
|
||||
resp.Response = "invalid authentication"
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
|
||||
if len(c.RootHash) > 1 { //if rootHash specified
|
||||
t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
|
||||
if err != nil {
|
||||
log.Printf("Snapshot error: %s", err.Error())
|
||||
resp.Error = true
|
||||
resp.Response = "invalid root hash"
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
} else { //if rootHash not specified use current merkletree
|
||||
t = MkTrees[c.CensusID]
|
||||
}
|
||||
|
||||
//dump the claim data and return it
|
||||
values, err := t.Dump()
|
||||
if err != nil {
|
||||
resp.Error = true
|
||||
resp.Response = fmt.Sprint(err)
|
||||
resp.Response = err.Error()
|
||||
} else {
|
||||
jValues, err := json.Marshal(values)
|
||||
if err != nil {
|
||||
resp.Error = true
|
||||
resp.Response = fmt.Sprint(err)
|
||||
resp.Response = err.Error()
|
||||
} else {
|
||||
resp.Response = string(jValues)
|
||||
resp.Response = fmt.Sprintf("%s", 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 {
|
||||
if strings.HasPrefix(c.CensusID, "0x") {
|
||||
resp.Error = true
|
||||
resp.Response = "snapshot an snapshot makes no sense"
|
||||
} else {
|
||||
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
|
||||
resp.Response = "proofData not provided"
|
||||
} else {
|
||||
validProof, _ := T.CheckProof([]byte(c.ClaimData), c.ProofData)
|
||||
if validProof {
|
||||
resp.Response = "valid"
|
||||
} else {
|
||||
resp.Response = "invalid"
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
var t *tree.Tree
|
||||
if len(c.RootHash) > 1 { //if rootHash specified
|
||||
t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
|
||||
if err != nil {
|
||||
log.Printf("Snapshot error: %s", err.Error())
|
||||
resp.Error = true
|
||||
resp.Response = "invalid root hash"
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
} else { //if rootHash not specified use current merkletree
|
||||
t = MkTrees[c.CensusID]
|
||||
}
|
||||
|
||||
validProof, err := t.CheckProof([]byte(c.ClaimData), c.ProofData)
|
||||
if err != nil {
|
||||
resp.Error = true
|
||||
resp.Response = err.Error()
|
||||
reply(&resp, w)
|
||||
return
|
||||
}
|
||||
if validProof {
|
||||
resp.Response = "valid"
|
||||
} else {
|
||||
resp.Response = "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +238,7 @@ func addCorsHeaders(w *http.ResponseWriter, req *http.Request) {
|
||||
(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
}
|
||||
|
||||
func Listen(port int, proto string, pubKey string) {
|
||||
func Listen(port int, proto string) {
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", port),
|
||||
ReadHeaderTimeout: 4 * time.Second,
|
||||
@@ -234,15 +283,6 @@ func Listen(port int, proto string, pubKey string) {
|
||||
http.Error(w, "Not found", http.StatusNotFound)
|
||||
}
|
||||
})
|
||||
http.HandleFunc("/snapshot", func(w http.ResponseWriter, r *http.Request) {
|
||||
addCorsHeaders(&w, r)
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
claimHandler(w, r, "snapshot")
|
||||
} else if r.Method != http.MethodOptions {
|
||||
http.Error(w, "Not found", http.StatusNotFound)
|
||||
}
|
||||
})
|
||||
http.HandleFunc("/dump", func(w http.ResponseWriter, r *http.Request) {
|
||||
addCorsHeaders(&w, r)
|
||||
|
||||
@@ -253,13 +293,6 @@ func Listen(port int, proto string, pubKey string) {
|
||||
}
|
||||
})
|
||||
|
||||
if len(pubKey) > 1 {
|
||||
log.Printf("Enabling signature authentication with %s\n", pubKey)
|
||||
authPubKey = pubKey
|
||||
} else {
|
||||
authPubKey = ""
|
||||
}
|
||||
|
||||
if proto == "https" {
|
||||
log.Print("Starting server in https mode")
|
||||
if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
|
||||
|
||||
128
tree/tree.go
128
tree/tree.go
@@ -1,38 +1,40 @@
|
||||
package tree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/user"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
common3 "github.com/iden3/go-iden3/common"
|
||||
mkcore "github.com/iden3/go-iden3/core"
|
||||
db "github.com/iden3/go-iden3/db"
|
||||
merkletree "github.com/iden3/go-iden3/merkletree"
|
||||
)
|
||||
|
||||
type Tree struct {
|
||||
Namespace string
|
||||
Storage string
|
||||
Tree *merkletree.MerkleTree
|
||||
DbStorage *db.LevelDbStorage
|
||||
}
|
||||
|
||||
func (t *Tree) Init() error {
|
||||
func (t *Tree) Init(namespace string) error {
|
||||
if len(t.Storage) < 1 {
|
||||
if len(namespace) < 1 {
|
||||
return errors.New("namespace not valid")
|
||||
}
|
||||
usr, err := user.Current()
|
||||
if err == nil {
|
||||
t.Storage = usr.HomeDir + "/.dvote/Tree"
|
||||
t.Storage = usr.HomeDir + "/.dvote/census/" + namespace
|
||||
} else {
|
||||
t.Storage = "./dvoteTree"
|
||||
t.Storage = "./dvoteTree/" + namespace
|
||||
}
|
||||
}
|
||||
mtdb, err := db.NewLevelDbStorage(t.Storage, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mt, err := merkletree.New(mtdb, 140)
|
||||
mt, err := merkletree.NewMerkleTree(mtdb, 140)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -45,74 +47,92 @@ func (t *Tree) Close() {
|
||||
defer t.Tree.Storage().Close()
|
||||
}
|
||||
|
||||
func (t *Tree) AddClaim(data []byte) error {
|
||||
isSnapshot := strings.HasPrefix(t.Namespace, "0x")
|
||||
if isSnapshot {
|
||||
return errors.New("No new claims can be added to a Snapshot")
|
||||
func (t *Tree) GetClaim(data []byte) (*mkcore.ClaimBasic, error) {
|
||||
if len(data) > 496/8 {
|
||||
return nil, errors.New("claim data too large")
|
||||
}
|
||||
claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil)
|
||||
return t.Tree.Add(claim)
|
||||
for i := len(data); i <= 496/8; i++ {
|
||||
data = append(data, '\x00')
|
||||
}
|
||||
var indexSlot [400 / 8]byte
|
||||
var dataSlot [496 / 8]byte
|
||||
copy(indexSlot[:], data[:400/8])
|
||||
copy(dataSlot[:], data[:496/8])
|
||||
e := mkcore.NewClaimBasic(indexSlot, dataSlot)
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (t *Tree) AddClaim(data []byte) error {
|
||||
e, err := t.GetClaim(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.Tree.Add(e.Entry())
|
||||
}
|
||||
|
||||
func (t *Tree) GenProof(data []byte) (string, error) {
|
||||
claim := mkcore.NewGenericClaim(t.Namespace, "default", data, nil)
|
||||
mp, err := t.Tree.GenerateProof(claim.Hi())
|
||||
e, err := t.GetClaim(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mpHex := common3.BytesToHex(mp)
|
||||
mp, err := t.Tree.GenerateProof(e.Entry().HIndex())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mpHex := common3.HexEncode(mp.Bytes())
|
||||
return mpHex, nil
|
||||
}
|
||||
|
||||
func (t *Tree) CheckProof(data []byte, mpHex string) (bool, error) {
|
||||
mp, err := common3.HexToBytes(mpHex)
|
||||
mpBytes, err := common3.HexDecode(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 := merkletree.NewProofFromBytes(mpBytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
e, err := t.GetClaim(data)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return merkletree.VerifyProof(t.Tree.RootKey(), mp,
|
||||
e.Entry().HIndex(), e.Entry().HValue()), nil
|
||||
}
|
||||
|
||||
func (t *Tree) GetRoot() string {
|
||||
return t.Tree.Root().Hex()
|
||||
return common3.HexEncode(t.Tree.RootKey().Bytes())
|
||||
}
|
||||
|
||||
/* 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.
|
||||
func (t *Tree) GetIndex(data []byte) (string, error) {
|
||||
e, err := t.GetClaim(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
index, err := t.Tree.GetDataByIndex(e.Entry().HIndex())
|
||||
return index.String(), err
|
||||
}
|
||||
|
||||
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:]))
|
||||
err := t.Tree.Walk(nil, func(n *merkletree.Node) {
|
||||
if n.Type == merkletree.NodeTypeLeaf {
|
||||
data := bytes.Trim(n.Value()[65:], "\x00")
|
||||
response = append(response, fmt.Sprintf("%s", data))
|
||||
}
|
||||
})
|
||||
return response, nil
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (t *Tree) Snapshot() (string, error) {
|
||||
substorage := t.DbStorage.WithPrefix([]byte(t.Namespace))
|
||||
nsHash := merkletree.HashBytes([]byte(t.Namespace))
|
||||
snapshotNamespace := t.GetRoot()
|
||||
fmt.Printf("Snapshoting %s\n", snapshotNamespace)
|
||||
t.Namespace = snapshotNamespace
|
||||
substorage.Iterate(func(key, value []byte) {
|
||||
nsValue := value[5:37]
|
||||
if fmt.Sprint(nsHash) == fmt.Sprint(nsValue) {
|
||||
fmt.Printf("%x\n", value)
|
||||
data := value[69:]
|
||||
//claim := mkcore.NewGenericClaim(snapshotNamespace, "default", data, nil)
|
||||
err := t.AddClaim(data)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
return snapshotNamespace, nil
|
||||
func (t *Tree) Snapshot(root string) (*Tree, error) {
|
||||
var rootHash merkletree.Hash
|
||||
snapshotTree := new(Tree)
|
||||
rootBytes, err := common3.HexDecode(root)
|
||||
if err != nil {
|
||||
return snapshotTree, err
|
||||
}
|
||||
copy(rootHash[:32], rootBytes)
|
||||
mt, err := t.Tree.Snapshot(&rootHash)
|
||||
snapshotTree.Tree = mt
|
||||
return snapshotTree, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user