-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Random transaction generator #171
Conversation
The purpose of this feature is to give the proposers something to propose while we flush out p2p. |
Hi @rawfalafel the tx generator data isn't too important because for phase 1 of sharding, proposers will not care about the contents or "validity" of transactions and instead of just serialize them into blobs and package them into collation bodies. If you are interested in extending past this simple PR into a more robust transaction generator, take a look at #17 which has been explored by @prestonvanloon a while back. The robust transaction generator would be more of a CLI tool or perhaps an additional service that can be registered to a sharding node that can be configurable and would be creating actual transactions from some sort of genesis state that we would configure in our own blockchain instance. For this PR, however, feel free to keep it simple so we can get it merged soon. The item I outlined in the previous section is more of a bigger project that is outside of this scope. |
Thanks for the clarification @prestonvanloon @rauljordan! This PR is ready to review. The logic is incredibly simple; send a random transaction through the event feed every 5 seconds, and instruct the proposer to subscribe to the feed and propose a collation using that transaction. I tested the goroutine by spinning up my own node. |
rand.Read(data) | ||
txs = append(txs, types.NewTransaction(0, common.HexToAddress("0x0"), | ||
nil, 0, nil, data)) | ||
for { |
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 function body here needs test coverage but I didn't see any examples for testing side effects in the sharding package. Any ideas?
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.
Can you elaborate more? What makes this difficult to 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.
I haven't looked at every test in the sharding
package but most of them assert the return value of a function. For this method I'd like to stub createCollation
and check that it gets called when a transaction gets sent to the transaction feed but I don't see any mocking libraries being used in any of the test files.
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.
There's still no test coverage for this PR. Open to ideas on how to add test coverage? Although having no test coverage (for now) is fine if you consider how much the behavior here is going to change in the near future.
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.
Yeah, I'm not sure. We are facing the same issue in other places.
I suppose you could call p.cancel
after a short timeout, but that feels like a hacky solution.
Since this is temporary code, I'm ok without testing this for loop, but this is a pattern we are already having issues within non-temporary code. I will spend some more time thinking about how to test this today.
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.
Oh wait this is the non-temporary code.
sharding/proposer/service.go
Outdated
txs = append(txs, types.NewTransaction(0, common.HexToAddress("0x0"), | ||
nil, 0, nil, data)) | ||
for { | ||
timeout := time.NewTimer(10 * 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.
Please use context for timeouts
https://golang.org/pkg/context/#WithTimeout
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.
+1
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.
Ah, okay. Bear with me because I'm still new to Go. Are you saying call context.WithTimeout
here and pass the new context down to createCollation
, or to simply replace time.NewTimer
with this context method and discard the context.
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.
Something like this:
for {
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
defer cancel()
select {
case tx := <-requests:
log.Info(fmt.Sprintf("Received transaction: %x", tx.Hash()))
if err := p.createCollation(ctx, []*types.Transaction{tx}); err != nil {
log.Error(fmt.Sprintf("Create collation failed: %v", err))
}
case <-ctx.Done():
log.Error("Subscriber timed out")
case err := <-p.txpoolSub.Err():
log.Error(fmt.Sprintf("Subscriber failed: %v", err))
cancel()
return
}
}
rand.Read(data) | ||
txs = append(txs, types.NewTransaction(0, common.HexToAddress("0x0"), | ||
nil, 0, nil, data)) | ||
for { |
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.
Can you elaborate more? What makes this difficult to test?
for range p.ticker.C { | ||
tx := createTestTransaction() | ||
nsent := p.transactionsFeed.Send(tx) | ||
log.Info(fmt.Sprintf("Sent transaction %x to %d subscribers", tx.Hash(), nsent)) |
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.
tx.Hash().Hex()
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.
Using the %x
formatter seems more logical to me, although I'd be splitting hairs.
sharding/txpool/service.go
Outdated
"github.com/ethereum/go-ethereum/log" | ||
"github.com/ethereum/go-ethereum/sharding" | ||
) | ||
|
||
// ShardTXPool handles a transaction pool for a sharded system. | ||
type ShardTXPool struct { | ||
p2p sharding.ShardP2P | ||
p2p sharding.ShardP2P |
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 decided on not using interfaces for things we most likely won't have a second implementation for. See #177 - just use *p2p.Server.
sharding/proposer/service.go
Outdated
func (p *Proposer) proposeCollations() { | ||
requests := make(chan *types.Transaction) |
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.
Make sure you also defer close(requests)
sharding/proposer/service.go
Outdated
@@ -23,6 +23,7 @@ type Proposer struct { | |||
client *mainchain.SMCClient | |||
shardp2p sharding.ShardP2P |
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.
See 2 comments below about discontinuing interfaces for things with a single implementation. Just use *p2p.Server and *txpool.ShardTXPool - #177.
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.
Just a few feedbacks. Thanks for doing this @rawfalafel!
sharding/proposer/service.go
Outdated
return nil | ||
} | ||
|
||
// proposeCollations listens to the transaction feed submits a collation when one is found | ||
func (p *Proposer) proposeCollations() { |
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.
Since this is similar to simulateNotaryRequests()
for Notary. Would it be better to place proposeCollations()
under /proposer/utils.go
? And maybe name it to simulateProposeCollation
?
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.
Not against it, but I guess this method doesn't know that the transactions it's being fed are simulated -- ShardTxPool
does.
sharding/proposer/service.go
Outdated
txs = append(txs, types.NewTransaction(0, common.HexToAddress("0x0"), | ||
nil, 0, nil, data)) | ||
for { | ||
timeout := time.NewTimer(10 * 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.
+1
sharding/proposer/service.go
Outdated
select { | ||
case tx := <-requests: | ||
if err := p.createCollation([]*types.Transaction{tx}); err != nil { | ||
log.Error(fmt.Sprintf("Create collation failed: %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.
I don't think we should be capitalizing first letter of error messages
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.
These error logs (not error objects) seem to be capitalized consistently throughout the entire package.
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.
Ah. I meant to not capitalize for nested error. But this is fine
sharding/proposer/service.go
Outdated
if err := p.createCollation([]*types.Transaction{tx}); err != nil { | ||
log.Error(fmt.Sprintf("Create collation failed: %v", err)) | ||
} | ||
log.Info(fmt.Sprintf("Received transaction: %x", tx.Hash())) |
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.
Maybe this line should be before createCollation
?
sharding/txpool/service.go
Outdated
p2p sharding.ShardP2P | ||
p2p sharding.ShardP2P | ||
transactionsFeed *event.Feed | ||
ticker *time.Ticker | ||
} | ||
|
||
// NewShardTXPool creates a new observer instance. |
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 comment is not right
sharding/txpool/service.go
Outdated
return p.transactionsFeed | ||
} | ||
|
||
// sendTestTransaction sends a transaction with random bytes over a 5 second interval |
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.
Period end function comments
sharding/proposer/proposer.go
Outdated
@@ -15,6 +15,7 @@ import ( | |||
// and body. Header consists of shardID, ChunkRoot, period, | |||
// proposer addr and signatures. Body contains serialized blob | |||
// of a collations transactions. | |||
|
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 this newline
This PR is ready for another look! |
sharding/proposer/service.go
Outdated
p.txpoolSub = p.txpool.TransactionsFeed().Subscribe(requests) | ||
|
||
for { | ||
ctx, cancel := context.WithTimeout(p.ctx, 10*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.
Why do you need to define a new context here? Let's just use p.ctx.Done() in the select block. Also, this will cancel the goroutine right after the select block...we instead want this to run forever.
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 derived a new context here to mimic the behavior here. If a single loop takes more than 10 seconds, whatever process is running gets canceled and the loop restarts. On the other hand if the parent context gets canceled we want the goroutine to be exit entirely.
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 looks good to me.
sharding/proposer/service.go
Outdated
log.Error(fmt.Sprintf("Create collation failed: %v", err)) | ||
} | ||
case <-ctx.Done(): | ||
log.Info("Context canceled") |
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 a return
statement after this line to stop the goroutine
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.
+1 to return statement.
Remove the logging, it's not necessary (Or make it a debug level)
sharding/proposer/service.go
Outdated
txs = append(txs, types.NewTransaction(0, common.HexToAddress("0x0"), | ||
nil, 0, nil, data)) | ||
cancel() | ||
return |
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.
Eliminate these two lines. Cancel will be called only if the service stops in the Stop()
function.
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 called the derived context's cancel to let any goroutine that might have spawned within the for loop to abandon their work. The return doesn't belong here though.
I'm going to take another look at how contexts work tomorrow morning before making changes. |
// Get current block number. | ||
blockNumber, err := p.client.ChainReader().BlockByNumber(context.Background(), nil) | ||
blockNumber, err := p.client.ChainReader().BlockByNumber(ctx, 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.
why not use p.ctx
instead of passing ctx
down?
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.
So that BlockByNumber
could be given the opportunity to exit immediately if it hit ctx
's 10 second timeout. But I removed the derived context now so this is passing down p.ctx
again.
sharding/proposer/service.go
Outdated
log.Error(fmt.Sprintf("Create collation failed: %v", err)) | ||
} | ||
case <-ctx.Done(): | ||
log.Info("Context canceled") |
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 log.Info
can be more descriptive
I poked around at the behavior of the context derived by This is ready for another look! |
Sharding: proof of custody performance improvements
…more testable and add more unit tests
I pushed up a commit that adds tests to the proposer service by refactoring methods in the package. I found that because of the limitations to mocking Since adding tests led to unwanted scope creep, I'm down with reverting the last commit and merging this in without test coverage. |
Hey @rawfalafel let's revert and do the test coverage in a separate PR |
reverted |
…nctions more testable and add more unit tests" This reverts commit e7608d4.
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.
LGTM
sharding/proposer/proposer.go
Outdated
) | ||
|
||
func getPeriod(reader mainchain.Reader, periodLength *big.Int) (*big.Int, error) { |
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 move it on the SMC client level so getPeriod
can be used for notary as well? if not, please add a TODO to make it a good first issue
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.
pointing out, just in case, but this is from the reverted code
sharding/proposer/proposer.go
Outdated
return new(big.Int).Div(blockNumber.Number(), periodLength), nil | ||
} | ||
|
||
func submitCollationHeader(txs []*types.Transaction, period *big.Int, shardID *big.Int, account *accounts.Account, |
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.
can you add a quick function comment here for submitCollationHeader
?
sharding: random transaction generator
sharding: random transaction generator Former-commit-id: 69b7da1
This PR, which resolves #153, is ready for review. See my comments below for more details.
👋
this PR isn't ready for review yet but I need help defining the requirements for this PR.What kind of transactions should get sent through the feed? This branch just throws a random byte array into atypes.Transaction
object. Should the generator be smarter in order to target specific code paths on the other side of the event feed?Also, should the generator be configurable? Would be nice if we could send transactions fromgeth console
by having it read transactions from a JSON file. @rauljordan also mentioned making the intervals more random.#125, #161, and possibly others could take advantage of a transaction generator but the requirements aren't clear to me, so I'm leaning towards keeping this simple and extensible for the next dev.