Skip to content

Commit

Permalink
Add query digest membership
Browse files Browse the repository at this point in the history
  • Loading branch information
iknite committed Dec 4, 2018
1 parent 7df07b5 commit aa43063
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 36 deletions.
57 changes: 56 additions & 1 deletion api/apihttp/apihttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func Add(balloon raftwal.RaftBalloonApi) http.HandlerFunc {
response.HistoryDigest,
response.HyperDigest,
response.Version,
event.Event,
response.EventDigest,
}

out, err := json.Marshal(snapshot)
Expand Down Expand Up @@ -176,6 +176,60 @@ func Membership(balloon raftwal.RaftBalloonApi) http.HandlerFunc {
}
}

// DigestMembership returns a membershipProof from the system
// The http post url is:
// POST /proofs/digest-membership
//
// Differs from Membership in that instead of sending the raw event we query
// with the keyDigest which is the digest of the event.
//
// The following statuses are expected:
// If everything is alright, the HTTP status is 201 and the body contains:
// {
// "key": "TG9yZW0gaXBzdW0gZGF0dW0gbm9uIGNvcnJ1cHR1bSBlc3QK",
// "keyDigest": "NDRkMmY3MjEzYjlhMTI4ZWRhZjQzNWFhNjcyMzUxMGE0YTRhOGY5OWEzOWNiYTVhN2FhMWI5OWEwYTlkYzE2NCAgLQo=",
// "isMember": "true",
// "proofs": ["<truncated for clarity in docs>"],
// "queryVersion": "1",
// "actualVersion": "2",
// }
func DigestMembership(balloon raftwal.RaftBalloonApi) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

// Make sure we can only be called with an HTTP POST request.
if r.Method != "POST" {
w.Header().Set("Allow", "POST")
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

var query protocol.MembershipDigest
err := json.NewDecoder(r.Body).Decode(&query)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

// Wait for the response
proof, err := balloon.QueryDigestMembership(query.KeyDigest, query.Version)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

out, err := json.Marshal(protocol.ToMembershipResult([]byte(nil), proof))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
w.Write(out)
return

}
}

// Incremental returns an incrementalProof from the system
// The http post url is:
// POST /proofs/incremental
Expand Down Expand Up @@ -250,6 +304,7 @@ func NewApiHttp(balloon raftwal.RaftBalloonApi) *http.ServeMux {
api.HandleFunc("/health-check", AuthHandlerMiddleware(HealthCheckHandler))
api.HandleFunc("/events", AuthHandlerMiddleware(Add(balloon)))
api.HandleFunc("/proofs/membership", AuthHandlerMiddleware(Membership(balloon)))
api.HandleFunc("/proofs/digest-membership", AuthHandlerMiddleware(DigestMembership(balloon)))
api.HandleFunc("/proofs/incremental", AuthHandlerMiddleware(Incremental(balloon)))

return api
Expand Down
72 changes: 66 additions & 6 deletions api/apihttp/apihttp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,21 @@ func (b fakeRaftBalloon) Join(nodeID, addr string) error {
return nil
}

func (b fakeRaftBalloon) QueryMembership(event []byte, version uint64) (*balloon.MembershipProof, error) {
mp := &balloon.MembershipProof{
func (b fakeRaftBalloon) QueryDigestMembership(keyDigest hashing.Digest, version uint64) (*balloon.MembershipProof, error) {
return &balloon.MembershipProof{
true,
visitor.NewFakeVerifiable(true),
visitor.NewFakeVerifiable(true),
1,
1,
2,
hashing.Digest{0x0},
keyDigest,
hashing.NewFakeXorHasher(),
}
return mp, nil
}, nil
}

func (b fakeRaftBalloon) QueryMembership(event []byte, version uint64) (*balloon.MembershipProof, error) {
return b.QueryDigestMembership(event, version)
}

func (b fakeRaftBalloon) QueryConsistency(start, end uint64) (*balloon.IncrementalProof, error) {
Expand Down Expand Up @@ -163,7 +166,64 @@ func TestMembership(t *testing.T) {
// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
handler := Membership(fakeRaftBalloon{})
expectedResult := &protocol.MembershipResult{Exists: true, Hyper: visitor.AuditPath{}, History: visitor.AuditPath{}, CurrentVersion: 0x1, QueryVersion: 0x1, ActualVersion: 0x2, KeyDigest: []uint8{0x0}, Key: []uint8{0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74}}
expectedResult := &protocol.MembershipResult{
Exists: true,
Hyper: visitor.AuditPath{},
History: visitor.AuditPath{},
CurrentVersion: 0x1,
QueryVersion: 0x1,
ActualVersion: 0x2,
KeyDigest: []uint8{0x0},
Key: key,
}

// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
handler.ServeHTTP(rr, req)

// Check the status code is what we expect.
if status := rr.Code; status != http.StatusOK {
t.Fatalf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}

// Check the body response
actualResult := new(protocol.MembershipResult)
json.Unmarshal([]byte(rr.Body.String()), actualResult)

assert.Equal(t, expectedResult, actualResult, "Incorrect proof")

}

func TestDigestMembership(t *testing.T) {

version := uint64(1)
hasher := hashing.NewSha256Hasher()
eventDigest := hasher.Do([]byte("this is a sample event"))

query, _ := json.Marshal(protocol.MembershipDigest{
eventDigest,
version,
})

req, err := http.NewRequest("POST", "/proofs/digest-membership", bytes.NewBuffer(query))
if err != nil {
t.Fatal(err)
}

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
handler := DigestMembership(fakeRaftBalloon{})
expectedResult := &protocol.MembershipResult{
Exists: true,
Hyper: visitor.AuditPath{},
History: visitor.AuditPath{},
CurrentVersion: 0x1,
QueryVersion: 0x1,
ActualVersion: 0x2,
KeyDigest: eventDigest,
Key: []byte(nil),
}

// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
Expand Down
21 changes: 16 additions & 5 deletions balloon/balloon.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func NewMembershipProof(
currentVersion, queryVersion, actualVersion uint64,
keyDigest hashing.Digest,
Hasher hashing.Hasher) *MembershipProof {

return &MembershipProof{
exists,
hyperProof,
Expand All @@ -109,15 +110,14 @@ func NewMembershipProof(
}
}

// Verify verifies a proof and answer from QueryMembership. Returns true if the
// DigestVerify verifies a proof and answer from QueryMembership. Returns true if the
// answer and proof are correct and consistent, otherwise false.
// Run by a client on input that should be verified.
func (p MembershipProof) Verify(event []byte, snapshot *Snapshot) bool {
func (p MembershipProof) DigestVerify(digest hashing.Digest, snapshot *Snapshot) bool {
if p.HyperProof == nil || p.HistoryProof == nil {
return false
}

digest := p.Hasher.Do(event)
hyperCorrect := p.HyperProof.Verify(digest, snapshot.HyperDigest)

if p.Exists {
Expand All @@ -130,6 +130,13 @@ func (p MembershipProof) Verify(event []byte, snapshot *Snapshot) bool {
return hyperCorrect
}

// Verify verifies a proof and answer from QueryMembership. Returns true if the
// answer and proof are correct and consistent, otherwise false.
// Run by a client on input that should be verified.
func (p MembershipProof) Verify(event []byte, snapshot *Snapshot) bool {
return p.DigestVerify(p.Hasher.Do(event), snapshot)
}

type IncrementalProof struct {
Start, End uint64
AuditPath visitor.AuditPath
Expand Down Expand Up @@ -223,7 +230,7 @@ func (b *Balloon) Add(event []byte) (*Snapshot, []*storage.Mutation, error) {
return snapshot, mutations, nil
}

func (b Balloon) QueryMembership(event []byte, version uint64) (*MembershipProof, error) {
func (b Balloon) QueryDigestMembership(keyDigest hashing.Digest, version uint64) (*MembershipProof, error) {
stats := metrics.Balloon
stats.AddFloat("QueryMembership", 1)
var proof MembershipProof
Expand All @@ -233,7 +240,7 @@ func (b Balloon) QueryMembership(event []byte, version uint64) (*MembershipProof
var historyProof *history.MembershipProof

proof.Hasher = b.hasherF()
proof.KeyDigest = proof.Hasher.Do(event)
proof.KeyDigest = keyDigest
proof.QueryVersion = version
proof.CurrentVersion = b.version - 1

Expand Down Expand Up @@ -272,6 +279,10 @@ func (b Balloon) QueryMembership(event []byte, version uint64) (*MembershipProof
return &proof, nil
}

func (b Balloon) QueryMembership(event []byte, version uint64) (*MembershipProof, error) {
return b.QueryDigestMembership(b.hasher.Do(event), version)
}

func (b Balloon) QueryConsistency(start, end uint64) (*IncrementalProof, error) {
stats := metrics.Balloon
stats.AddFloat("QueryConsistency", 1)
Expand Down
37 changes: 35 additions & 2 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,26 @@ func (c HttpClient) Membership(key []byte, version uint64) (*protocol.Membership

}

// Membership will ask for a Proof to the server.
func (c HttpClient) MembershipDigest(keyDigest hashing.Digest, version uint64) (*protocol.MembershipResult, error) {

query, _ := json.Marshal(&protocol.MembershipDigest{
keyDigest,
version,
})

body, err := c.doReq("POST", "/proofs/digest-membership", query)
if err != nil {
return nil, err
}

var proof *protocol.MembershipResult
json.Unmarshal(body, &proof)

return proof, nil

}

// Incremental will ask for an IncrementalProof to the server.
func (c HttpClient) Incremental(start, end uint64) (*protocol.IncrementalResponse, error) {

Expand Down Expand Up @@ -165,10 +185,9 @@ func uint2bytes(i uint64) []byte {

// Verify will compute the Proof given in Membership and the snapshot from the
// add and returns a proof of existence.

func (c HttpClient) Verify(result *protocol.MembershipResult, snap *protocol.Snapshot, hasherF func() hashing.Hasher) bool {

proof := protocol.ToBalloonProof([]byte(c.apiKey), result, hasherF)
proof := protocol.ToBalloonProof(result, hasherF)

return proof.Verify(snap.EventDigest, &balloon.Snapshot{
snap.EventDigest,
Expand All @@ -179,6 +198,20 @@ func (c HttpClient) Verify(result *protocol.MembershipResult, snap *protocol.Sna

}

// Verify will compute the Proof given in Membership and the snapshot from the
// add and returns a proof of existence.
func (c HttpClient) DigestVerify(result *protocol.MembershipResult, snap *protocol.Snapshot, hasherF func() hashing.Hasher) bool {

proof := protocol.ToBalloonProof(result, hasherF)

return proof.DigestVerify(snap.EventDigest, &balloon.Snapshot{
snap.EventDigest,
snap.HistoryDigest,
snap.HyperDigest,
snap.Version,
})

}
func (c HttpClient) VerifyIncremental(result *protocol.IncrementalResponse, startSnapshot, endSnapshot *protocol.Snapshot, hasher hashing.Hasher) bool {

proof := protocol.ToIncrementalProof(result, hasher)
Expand Down
24 changes: 24 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,30 @@ func TestMembership(t *testing.T) {

}

func TestDigestMembership(t *testing.T) {
tearDown := setup()
defer tearDown()

event := "Hello world!"
version := uint64(0)
fakeResult := &protocol.MembershipResult{
Key: []byte(event),
KeyDigest: []byte("digest"),
Exists: true,
Hyper: make(visitor.AuditPath),
History: make(visitor.AuditPath),
CurrentVersion: version,
QueryVersion: version,
ActualVersion: version,
}
resultJSON, _ := json.Marshal(fakeResult)
mux.HandleFunc("/proofs/digest-membership", okHandler(resultJSON))

result, err := client.MembershipDigest([]byte("digest"), version)
assert.NoError(t, err)
assert.Equal(t, fakeResult, result, "The results should match")
}

func TestMembershipWithServerFailure(t *testing.T) {
tearDown := setup()
defer tearDown()
Expand Down
2 changes: 1 addition & 1 deletion cmd/client_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func newAddCommand(ctx *clientContext) *cobra.Command {

log.Infof(`
Received snapshot with values:
EventDigest: %s
EventDigest: %x
HyperDigest: %x
HistoryDigest: %x
Version: %d
Expand Down
Loading

0 comments on commit aa43063

Please sign in to comment.