-
Notifications
You must be signed in to change notification settings - Fork 2
/
verifier.go
155 lines (139 loc) · 4.36 KB
/
verifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"syscall/js"
"time"
"github.com/codenotary/immudb/embedded/store"
"github.com/codenotary/immudb/pkg/api/schema"
)
func main() {
c := make(chan struct{}, 0)
println("Go WebAssembly initialized")
js.Global().Set("VerifyConsistency", js.FuncOf(VerifyConsistency))
<-c
}
// State ...
type State struct {
TXID uint64 `json:"tx_id"`
TXHash []byte `json:"tx_hash"`
}
// Equals ...
func (s *State) Equals(ss *State) bool {
if s == nil || ss == nil {
return false
}
return s.TXID == ss.TXID && bytes.Compare(s.TXHash, ss.TXHash) == 0
}
// VerifyConsistency ...
func VerifyConsistency(this js.Value, args []js.Value) interface{} {
serverURL := args[0].String()
go func() {
client := http.Client{Timeout: 5 * time.Second}
// get local state
var localState *State
localStateJS := js.Global().Get("localStorage").Call("getItem", "immuvotingState")
if !js.Null().Equal(localStateJS) {
localStateStr := localStateJS.String()
println("local state:", localStateStr)
localState = &State{}
if err := json.Unmarshal([]byte(localStateStr), localState); err != nil {
println("error JSON-unmarshaling local state", localStateStr, ": ", err.Error())
return
}
}
// get server state
stateURL := serverURL + "/state"
req, err := http.NewRequest(http.MethodGet, stateURL, nil)
if err != nil {
println(
"error creating new HTTP GET %s request to fetch server state:",
stateURL, err.Error())
return
}
var serverState State
if _, err := httpDo(&client, req, &serverState, "server state:"); err != nil {
println(err.Error())
return
}
var verified bool
if localState != nil {
// get verifiable transaction
vTXURL := fmt.Sprintf(
"%s/verifiable-tx?server_tx=%d&local_tx=%d",
serverURL, serverState.TXID, localState.TXID)
req, err = http.NewRequest(http.MethodGet, vTXURL, nil)
if err != nil {
println(
"error creating new HTTP GET", vTXURL,
"request to fetch verifiable TX:", err.Error())
return
}
var vTX schema.VerifiableTx
if httpStatus, err := httpDo(&client, req, &vTX, ""); err != nil {
errMsg := err.Error()
if httpStatus == http.StatusNotFound {
errMsg = fmt.Sprintf(
"verification error: one of the 2 tx IDs was not found on server: %v", err)
}
println(errMsg)
return
}
// do the verification
proof := schema.DualProofFrom(vTX.DualProof)
localTXHash := schema.DigestFrom(localState.TXHash)
serverTXHash := schema.DigestFrom(serverState.TXHash)
verified = store.VerifyDualProof(
proof, localState.TXID, serverState.TXID, localTXHash, serverTXHash)
println("verified:", verified)
now := time.Now().Format(time.RFC3339)
resultDOMElem := js.Global().Get("document").Call("getElementById", "tampering-result")
if !verified {
resultDOMElem.Set("innerHTML", "<span class=\"audit-failed\">Tampered!</span> @ "+now)
} else {
resultDOMElem.Set("innerHTML", "<span class=\"audit-ok\">OK</span> @ "+now)
}
}
// override the local state with the fresh server state
if (verified && !localState.Equals(&serverState)) || localState == nil {
serverStateBs, err := json.Marshal(&serverState)
if err != nil {
println(
"error JSON-marshaling server state", fmt.Sprintf("%+v", serverState),
"before perisiting it to local storage:", err.Error())
return
}
js.Global().Get("localStorage").Call("setItem", "immuvotingState", string(serverStateBs))
}
}()
return nil
}
func httpDo(client *http.Client, req *http.Request, out interface{}, printRawPayload string) (int, error) {
resp, err := client.Do(req)
if err != nil {
return 0, fmt.Errorf("error executing HTTP request %s: %s", req.URL, err)
}
defer resp.Body.Close()
httpStatus := resp.StatusCode
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return httpStatus, fmt.Errorf(
"error reading bytes from %s response: %s", req.URL, err)
}
if httpStatus < 200 || httpStatus > 299 {
return httpStatus, fmt.Errorf(
"%s responded with non-200 range code %d", req.URL, httpStatus)
}
if len(printRawPayload) > 0 {
print(printRawPayload, fmt.Sprintf("%s", bodyBytes))
}
if err := json.Unmarshal(bodyBytes, out); err != nil {
return httpStatus, fmt.Errorf(
"error JSON-unmarshaling response bytes \"%s\" from %s: %s",
bodyBytes, req.URL, err)
}
return httpStatus, nil
}