Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client with (partially) verified QueueLeaf #320

Merged
merged 6 commits into from Feb 13, 2017
Merged

Conversation

gdbelvin
Copy link
Contributor

@gdbelvin gdbelvin commented Jan 25, 2017

Client AddLeaf:

  • Fetches current Signed Tree Root
  • Queues the leaf
  • Waits for an STR update
  • TODO: Verify STR
  • Fetch and verify inclusion proof

Integration test improvements:

  • LogEnv also creates a signer
  • Graceful server shutdown
  • Adjust VerifyMerkleTree to accept a LeafHash rather than LeafData.

Contributes to #297

client/client.go Outdated
}

// Wait for TreeSize to update.
if err := c.waitForSTRUpdate(ctx, 2); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to pass in the first tree size, which must be read before queueLeaf.

Suppose the new leaf gets integrated really quickly. The tree size could change before the first call to waitForSTRUpdate(), which makes you block needlessly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't block needlessly because it uses current STR in the LogClient as it's base line rather than the first fetch. I've added an UpdateSTR call before queue leaf

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm now passing the current tree size

client/client.go Outdated

// Wait for TreeSize to update.
if err := c.waitForSTRUpdate(ctx, 2); err != nil {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return err

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

client/client.go Outdated
}

// Get proof by hash.
err := c.getInclusionProof(ctx, leaf.MerkleLeafHash, c.STR.TreeSize)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

Either do "if err != ...; err != nil { return err }" and "return nil" at line 64, or remove the if and "return err" (as you do now)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now using if err:= foo; ... { format

client/client.go Outdated

"github.com/google/trillian"
"github.com/jpillora/backoff"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll push back on jpillora's library due to the lack of an explicit license. Let's talk about this in person if you like.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like he has an MIT license in the README. Opened a ticket for him to create a LICENSE file in his repo.

client/client.go Outdated
// waitForSTRUpdate repeatedly fetches the STR until the TreeSize changes
// or until a maximum number of attempts (10) has been tried.
func (c *LogClient) waitForSTRUpdate(ctx context.Context, maxRetries int) error {
startTreeSize := c.STR.TreeSize
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated above, I think we need the STR from immediately before queueLeaf. If AddLeaf is called on a new client, the STR will be zeroed, which will cause this to detect a change even if the actual STR didn't change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. Added UpdateSTR before queue leaf

client/client.go Outdated
if err := c.UpdateSTR(ctx); err != nil {
return err
}
if i >= maxRetries {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be factored into the stop condition of the loop.

We've got 2 issues here:

  • The last attempt will always cause an error, regardless of a successful update
  • The loop does maxRetries+1 attempts, not maxRetries

Please add tests for the scenarios above before changing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated algorithm to be correct.

client/client.go Outdated
LeafHash: hash,
TreeSize: treeSize,
}
_, err := c.client.GetInclusionProofByHash(ctx, req)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Scope err in the if.

if _, err := ...; err != nil { return err }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -33,18 +34,38 @@ func TestAddLeaf(t *testing.T) {
if err := mysql.CreateTree(logID, env.DB); err != nil {
t.Errorf("Failed to create log: %v", err)
}

client := New(logID, env.ClientConn)

if err := client.AddLeaf(ctx, []byte("foo")); err != nil {
t.Errorf("Failed to add Leaf: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify that the leaf was really added in the log? An empty AddLeaf() function would pass this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a GetLeaf don't we?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. Add a TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

client := New(logID, env.ClientConn)

if err := client.AddLeaf(ctx, []byte("foo")); err != nil {
t.Errorf("Failed to add Leaf: %v", err)
}
}

func TestAddSameLeaf(t *testing.T) {
func TestUpdateSTR(t *testing.T) {
t.Skip("Need to add a leaf first")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the skip? We have AddLeaf(), let's test it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
}

func TestAddSameLeaf(t *testing.T) {
t.Skip("Submitting two leaves currently breaks")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the skip here too?

Copy link
Contributor

@codingllama codingllama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes.

It seems that we still have a few open comments, plus Travis is broken. Could you verify?

@@ -33,18 +34,38 @@ func TestAddLeaf(t *testing.T) {
if err := mysql.CreateTree(logID, env.DB); err != nil {
t.Errorf("Failed to create log: %v", err)
}

client := New(logID, env.ClientConn)

if err := client.AddLeaf(ctx, []byte("foo")); err != nil {
t.Errorf("Failed to add Leaf: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. Add a TODO?

@gdbelvin gdbelvin mentioned this pull request Feb 1, 2017
9 tasks
@gdbelvin gdbelvin force-pushed the client2 branch 4 times, most recently from 8cd31e3 to 09ce4c9 Compare February 9, 2017 16:16
@gdbelvin gdbelvin changed the title Client with getProofByHash Client with (partially) verified QueueLeaf Feb 9, 2017
@gdbelvin
Copy link
Contributor Author

gdbelvin commented Feb 9, 2017

Updated and ready for review.

Copy link
Contributor

@codingllama codingllama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a few requests:

  1. Please don't squash commits on ongoing reviews. It makes it hard to figure out what you changed.
  2. You have (had?) unit test failures on Travis, PTAL.
  3. As we discussed offline, could you remove the dependency to jpillora's library? I think it buys too little to justify yet another dependency (plus licensing and so on).

Meanwhile I'll have another go at it and see if I have any further comments.

@gdbelvin
Copy link
Contributor Author

  • Sorry for the squash. There was a large rebase which cased this.
  • Travis errors were in the past. These have been fixed.
  • Created a backoff library.

Copy link
Contributor

@codingllama codingllama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the comments.

Could you add a test for Backoff, please?

maxNanos := float64(b.Max.Nanoseconds())
nanos := math.Min(minNanos*math.Pow(b.Factor, float64(b.x)), maxNanos)
if b.Jitter {
// Generate a number in the range [b.Min, b.Max]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the range should be [b.Min,nanos), otherwise you may end up with a <= 0 number. Alternatively, you could just have "nanos = r".

TBH I don't exactly how you want Jitter to work, so I'm not entirely sure which approach is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

)

// Backoff specifies the parameters of the backoff algorithm.
type Backoff struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand the documentation with a simple usage example, plus a brief explanation of what Jitter does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

x int
}

// Duration returns the time to wait on duration x.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add:

"Every time Duration() is called the returned value will exponentially increase, up to Backoff.Max. If Jitter is true, instead of increasing exponentially, a random Duration will be picked in the interval [insert interval here]".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

@codingllama codingllama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test for Backoff; other than that I'm happy to approve.

@gdbelvin
Copy link
Contributor Author

Added backoff test.

Copy link
Contributor

@codingllama codingllama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the remaining comment. Other than that, as long as Travis is happy, LGTM.

{b, 2, time.Duration(2) * time.Second, time.Duration(4) * time.Second},
{b, 3, time.Duration(4) * time.Second, time.Duration(8) * time.Second},
{b, 4, time.Duration(8) * time.Second, time.Duration(16) * time.Second},
{b, 8, time.Duration(100) * time.Second, time.Duration(200) * time.Second},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we limit Jitter to Max? That would be my working assumption.

If we don't, please document it on Backoff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about capping Jitter at Max, but I didn't want the max to cap things at a static value when jitter is on lets clients all clients wait the same amount of time.
The current algorithm uses a geometrically increasing delay [A, B, C, D] = [ca^0, ca^1, c^a*2..]. Jitter selects a value from next higher interval. To keep max constant, jitter would have to select from the next lower interval, which would cause problems for x=0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I'm happy with the documentation change.

Client AddLeaf:
- Fetches current Signed Tree Root
- Queues the leaf
- Waits for an STR update
- TODO: Verify STR
- Fetch and verify inclusion proof

Integration test improvements:
- LogEnv also creates a signer
- Created backoff library
- Rebased and removed signinterval
@daviddrysdale
Copy link
Contributor

Looking at this commit after the fact, a couple of things:

  • The commit message / summary could be more helpful. The summary implies it's all about client verification, but there are other changes that either only get mentioned in the body of the commit message (adding a signer), or are not mentioned at all (modifying storage to use gRPC errors). Please separate distinct chunks into separate commits (they can still be in the same PR, but you'll need to get used to git rebase -i etc.)
  • Adding a dependency from storage/mysql to grpc just to avoid a bit of error mapping seems like a bad idea. I think we should back that out.

@gdbelvin gdbelvin deleted the client2 branch July 4, 2017 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants