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
Conversation
29a2dc7
to
040b197
Compare
client/client.go
Outdated
} | ||
|
||
// Wait for TreeSize to update. | ||
if err := c.waitForSTRUpdate(ctx, 2); err != nil { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return err
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
client/client_test.go
Outdated
@@ -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) | |||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. Add a TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
client/client_test.go
Outdated
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") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
client/client_test.go
Outdated
} | ||
} | ||
|
||
func TestAddSameLeaf(t *testing.T) { | ||
t.Skip("Submitting two leaves currently breaks") |
There was a problem hiding this comment.
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?
There was a problem hiding this 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?
client/client_test.go
Outdated
@@ -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) | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. Add a TODO?
8cd31e3
to
09ce4c9
Compare
Updated and ready for review. |
There was a problem hiding this 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:
- Please don't squash commits on ongoing reviews. It makes it hard to figure out what you changed.
- You have (had?) unit test failures on Travis, PTAL.
- 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.
|
There was a problem hiding this 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?
client/backoff/backoff.go
Outdated
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] |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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]".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this 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.
Added backoff test. |
There was a problem hiding this 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}, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
Looking at this commit after the fact, a couple of things:
|
Client AddLeaf:
Integration test improvements:
Contributes to #297