-
Notifications
You must be signed in to change notification settings - Fork 170
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
feat: checkpointing/implement BLS signer #104
Conversation
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.
Getting there for sure!
I don't know if this can be tested during the SDK simulation tests.
About unit tests, there is a MockClient in the SDK that returns errors at broadcast attemts, so it's not exactly good for us, but a similar client could be used for testing that broadcasting happens at all during block processing. You could pass a client context to the keeper, and in tests it would be a mock client. Similar things happen in other CLI command tests, but most of them don't use a Client
, they just set various properties of the context.
How to test if you pass the right client context in the real program flow? Maybe we can just rely on integration tests for that part. The unit tests ensure that the right thing gets signed and the right broadcast method is used, and the integration test ensures that the configuration is correct, that eventually the checkpoint is created on the ledger.
x/checkpointing/keeper/bls_signer.go
Outdated
fs := pflag.NewFlagSet("checkpointing", pflag.ContinueOnError) | ||
err = fs.Set(flags.FlagFrom, k.GetParams(ctx).String()) |
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.
What is this bit doing exactly?
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.
To be honest, I'm not sure. I thought this is a necessary ingredient to compose a transaction, but I'm not sure I'm doing it right. See here.
x/checkpointing/keeper/bls_signer.go
Outdated
if err != nil { | ||
return err | ||
} | ||
err = tx.GenerateOrBroadcastTxCLI(client.Context{}, fs, msg) |
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.
Don't you have to set some fields on the client context, such as BroadcastMode
and Client
?
Basically everything that happens in the CLI to initialize the context. Maybe this can be injected into the keeper.
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 sharing the link. I was not sure what to put in the context. Let me run some tests
Thanks, @aakoshh and @vitsalis for the discussion. I have updated the draft PR and it's ready for review. I'm still struggling with injecting the client.Context to the checkpointing keeper. In the unit test, I used some utilities to customize a client.Context and sent it to the keeper But I still think we should inject the client.Context when initializing the checkpointing keeper. I left that as a TODO as I haven't figured out how to do that. For example, the client.Context requires a keyring. How do we get the keyring when starting the checkpointing keeper? |
The same way we do when we run a client CLI command? It has to be in the home directory, like a wallet, doesn't it? The operator has to make sure that the node has the files available. And it should fail during startup, not later during epoch switches. |
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.
Nice work with the mocks!
Perhaps it's possible to inject the client context to the app, rather than the config, from the root. See comments.
@@ -120,6 +120,16 @@ $(BUILDDIR)/: | |||
|
|||
.PHONY: build build-linux | |||
|
|||
mockgen_cmd=go run github.com/golang/mock/mockgen@v1.6.0 | |||
|
|||
mocks: $(MOCKS_DIR) |
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 instead of a one-time task that you run manually, the actual output files could be targets that depend on the inputs, so that if the input changes, then the mock is automatically regenerated. And tests could depend on the mock files to force them to be generated.
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.
That would be cool. But I'm not sure how to do that in Makefile. Could you please show me an example?
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 is a good post about Make: https://www.olioapps.com/blog/the-lost-art-of-the-makefile/
It would look something like this:
mockgen_cmd=go run github.com/golang/mock/mockgen@v1.6.0 -package mocks
testutil/mocks/checkpointing_expected_keepers.go: x/checkpointing/types/expected_keepers.go $(MOCKS_DIR)
$(mockgen_cmd) -source=$< -destination $@
testutil/mocks/bls_signer.go: x/checkpointing/keeper/bls_signer.go $(MOCKS_DIR)
$(mockgen_cmd) -source=$< -destination $@
MOCK_FILES = \
bls_signer.go \
checkpointing_expected_keepers.go
MOCK_PATHS := $(patsubst %, testutil/mocks/%, $(MOCK_FILES))
run-tests: $(MOCK_PATHS)
...
The only difference is the regeneration if the source changes.
app/app.go
Outdated
pvKeyFile := nodeCfg.PrivValidatorKeyFile() | ||
if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { | ||
panic(err) | ||
} | ||
pvStateFile := nodeCfg.PrivValidatorStateFile() | ||
if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { | ||
panic(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.
Does this allow non-validator nodes to run, a.k.a. "full nodes"?
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.
Yes...It's wired that I cannot get the flagWithTendermint
from newApp because it is not accessible. Maybe we need to add a new flag for this. I added a TODO.
44b9e5d
to
3ba03c8
Compare
Thanks, @aakoshh for your advice! I managed to inject client context into the checkpointing keeper at |
return nil, err | ||
} | ||
pvStateFile := nodeCfg.PrivValidatorStateFile() | ||
err = tmos.EnsureDir(filepath.Dir(pvStateFile), 0777) |
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 returning if err != nil
?
nodeCfg := tmconfig.DefaultConfig() | ||
pvKeyFile := nodeCfg.PrivValidatorKeyFile() | ||
err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0777) | ||
if err != nil { | ||
return nil, err | ||
} | ||
pvStateFile := nodeCfg.PrivValidatorStateFile() | ||
err = tmos.EnsureDir(filepath.Dir(pvStateFile), 0777) | ||
if err != nil { | ||
return nil, 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.
The same checks seem to happen in InitClientContext
, so maybe we don't have to do it here?
if err != nil { | ||
return nil, err | ||
} | ||
privSigner, _ := InitClientContext(client.Context{}, keyring.BackendMemory) |
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 remind me what the PV keyfile and state file are, versus an in-memory keyring? Why is one backed by files and the other by memory?
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.
PV keyfile and state file are used in Tendermint for consensus. Please see here. We wrap PV to include BLS keys. Keyring is to store account keys. Keyring have alternative backends but we use in-memory backend only for testing.
privSigner, _ := InitClientContext(client.Context{}, keyring.BackendMemory) | ||
privSigner.WrappedPV.Clean(pvKeyFile, pvStateFile) |
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.
Is this not going to remove something of value by accident?
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 it will, not in testing. This method is only used to remove the files generated during testing. Otherwise, these key/state files will be generated everywhere after running tests.
signer.EXPECT().GetAddress().Return(addr1) | ||
signer.EXPECT().SignMsgWithBls(gomock.Eq(signBytes)).Return(bls12381.Sign(blsPrivKey1, signBytes), nil) | ||
err := ckptkeeper.SendBlsSig(ctx, epochNum, lch) | ||
require.NoError(t, 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.
So what't the client in this test, what does it do for broadcast? Is it possible to assert that the right thing got broadcasted?
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 pointing this out. I used BroadcastAsync
mode in utils.go InitClientContext
. See here. Basically, it does not need to wait for any confirmation of the transaction. We can switch to alternative modes to get more information about the transactions that are sent. I just thought Async mode is good for unit test. We can make it configurable for integration 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.
This is a tough one, well done! It looks good, but I guess the only way to see if it works is to run the node and check the logs.
* slashing tx validation * fix * fix * fix * fix * fix check * fix tests and checks * fix * fix checks * fix data types * fix * update e2e tests * fix comments * fix comments * fix comments * fix comments * change to slashing rate * fix comments and checks * fix checks * hardcode slashing rate to 100% to pass tests * fix docstring * more comments * add slashing rate check in checktransactions * change staker addr to change addr * pass change addr in msgcreatedelegation * change address in msgbtcundelegate * error handling for change addr * verbose errors * fix fee checks and validation for slashing tx on rounded slashing rate * more comments * fix docstring * more comments in fee checks * remove staking output info in msgundelegate * remove receiver func Validate * fix tests and include change address in MsgCreateBTCDelegation request * fix tests and pass change address in MsgBTCUndelegate * fix naming and remove dead code * fix e2e tests - pass change addr in types.MsgCreateBTCDelegation * fix imports * fix import issue * fix imports * fix imports * fix * fix args in tx * fix imports * change address in undelegation and delegation creation * keep Validate func * add more comments * fix proto comments * fix slashing rate validation * fix validation * more comments * fix checks in validation and don't depend on change address * remove change address proto * remove change address usages * fix receiver func * remove todos * fix args * fix func sigs * fix imports * fix defs * fix imports * fix formatting * change to ceil(round up) * lock time 0 * fix checks * move fee checks * fix doc string * convert to btcutil.amount * dust relay tx fee * fix dust fn * fix mod * add todo * fix docstring * changes in build logic and fix tests * rename vars * fix tests * fix slashing rate in tests to avoid change amount being too low * improve slashing rate validity and fix tests * improvise tests * fix comments * fix spacing * fix slashing rate in tests * docstrings * docstring * more comments * docstring * move btcstaking errors to btcstaking/ dir * move validation fn inside btcstaking/ dir * enforce checks if slashing/change address are same * imrpove precision check * fix slashing rate * fix tests
This is a draft PR for implementing BLS signer. This PR aims to prepare a
MsgAddBlsSig
and send it to Tendermint at the end of each epoch. I'm not sure if it works. More importantly, I'm not sure how to test if the is actually sent to Tendermint. Is there a light-weight way to do this?