-
Notifications
You must be signed in to change notification settings - Fork 18
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: consensus messages #141
Changes from all commits
3ba107d
d33f49c
d188a3d
418b9b5
d9429a2
3a4370f
e74854f
7cb61d7
7760b3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package app | ||
|
||
import ( | ||
"testing" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" | ||
"github.com/gogo/protobuf/proto" | ||
prototypes "github.com/gogo/protobuf/types" | ||
"github.com/stretchr/testify/require" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
"github.com/tendermint/tendermint/proto/tendermint/types" | ||
) | ||
|
||
func TestBeginBlocker(t *testing.T) { | ||
app, valAccount := SetupWithOneValidator(t) | ||
ctx := app.NewUncachedContext(true, types.Header{ | ||
Height: 1, | ||
ChainID: "testchain_9000-1", | ||
}) | ||
|
||
bankSend := &banktypes.MsgSend{ | ||
FromAddress: valAccount.GetAddress().String(), | ||
ToAddress: valAccount.GetAddress().String(), | ||
Amount: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))), | ||
} | ||
msgBz, err := proto.Marshal(bankSend) | ||
require.NoError(t, err) | ||
|
||
goodMessage := &prototypes.Any{ | ||
TypeUrl: proto.MessageName(&banktypes.MsgSend{}), | ||
Value: msgBz, | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
consensusMsgs []*prototypes.Any | ||
expectError bool | ||
}{ | ||
{ | ||
name: "ValidConsensusMessage", | ||
consensusMsgs: []*prototypes.Any{ | ||
goodMessage, | ||
}, | ||
expectError: false, | ||
}, | ||
{ | ||
name: "InvalidUnpackMessage", | ||
consensusMsgs: []*prototypes.Any{ | ||
{ | ||
TypeUrl: "/path.to.InvalidMsg", | ||
Value: []byte("invalid unpack data"), | ||
}, | ||
}, | ||
expectError: true, | ||
}, | ||
{ | ||
name: "InvalidExecutionMessage", | ||
consensusMsgs: []*prototypes.Any{ | ||
{ | ||
TypeUrl: "/path.to.ExecErrorMsg", | ||
Value: []byte("execution error data"), | ||
}, | ||
}, | ||
expectError: true, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
req := abci.RequestBeginBlock{ | ||
Header: types.Header{ | ||
Height: 1, | ||
Time: ctx.BlockTime(), | ||
ChainID: "testchain_9000-1", | ||
}, | ||
LastCommitInfo: abci.LastCommitInfo{}, | ||
ByzantineValidators: []abci.Evidence{}, | ||
ConsensusMessages: tc.consensusMsgs, | ||
} | ||
|
||
res := app.BeginBlocker(ctx, req) | ||
require.NotNil(t, res) | ||
|
||
if tc.expectError { | ||
require.NotEmpty(t, res.ConsensusMessagesResponses) | ||
for _, response := range res.ConsensusMessagesResponses { | ||
_, isError := response.Response.(*abci.ConsensusMessageResponse_Error) | ||
require.True(t, isError, "Expected an error response but got a success") | ||
Comment on lines
+88
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'd also suggest verifying the returned error string There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @danwt does not like to check error strings There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea but it's still better than nothing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think in theory you can check grpc status code |
||
} | ||
} else { | ||
require.NotEmpty(t, res.ConsensusMessagesResponses) | ||
for _, response := range res.ConsensusMessagesResponses { | ||
_, isOk := response.Response.(*abci.ConsensusMessageResponse_Ok) | ||
require.True(t, isOk, "Expected a success response but got an error") | ||
} | ||
} | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
package app | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"testing" | ||
"time" | ||
|
||
appcodec "github.com/cosmos/cosmos-sdk/codec" | ||
codectypes "github.com/cosmos/cosmos-sdk/codec/types" | ||
"github.com/cosmos/cosmos-sdk/crypto/codec" | ||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" | ||
"github.com/cosmos/cosmos-sdk/simapp" | ||
"github.com/cosmos/cosmos-sdk/testutil/mock" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" | ||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" | ||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" | ||
"github.com/dymensionxyz/dymension-rdk/testutil/utils" | ||
rollappparamstypes "github.com/dymensionxyz/dymension-rdk/x/rollappparams/types" | ||
"github.com/tendermint/tendermint/crypto/encoding" | ||
"github.com/tendermint/tendermint/libs/log" | ||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" | ||
types2 "github.com/tendermint/tendermint/types" | ||
dbm "github.com/tendermint/tm-db" | ||
|
||
"github.com/stretchr/testify/require" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
) | ||
|
||
const TestChainID = "testchain_9000-1" | ||
|
||
func setup(withGenesis bool, invCheckPeriod uint) (*App, GenesisState) { | ||
db := dbm.NewMemDB() | ||
|
||
app := NewRollapp( | ||
log.NewNopLogger(), | ||
db, | ||
nil, | ||
true, | ||
map[int64]bool{}, | ||
DefaultNodeHome, | ||
invCheckPeriod, | ||
MakeEncodingConfig(), | ||
nil, | ||
simapp.EmptyAppOptions{}, | ||
nil, | ||
) | ||
|
||
if withGenesis { | ||
return app, NewDefaultGenesisState(app.appCodec) | ||
} | ||
|
||
return app, GenesisState{} | ||
} | ||
|
||
// SetupWithOneValidator initializes a new App. A Nop logger is set in App. | ||
func SetupWithOneValidator(t *testing.T) (*App, authtypes.AccountI) { | ||
t.Helper() | ||
|
||
privVal := mock.NewPV() | ||
pubKey, err := privVal.GetPubKey() | ||
require.NoError(t, err) | ||
|
||
// create validator set with single validator | ||
validator := types2.NewValidator(pubKey, 1) | ||
valSet := types2.NewValidatorSet([]*types2.Validator{validator}) | ||
|
||
// generate genesis account | ||
senderPrivKey := secp256k1.GenPrivKey() | ||
acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) | ||
balance := banktypes.Balance{ | ||
Address: acc.GetAddress().String(), | ||
Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), | ||
} | ||
fmt.Printf("address: %s\n", acc.GetAddress().String()) | ||
|
||
app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) | ||
|
||
return app, acc | ||
} | ||
|
||
// SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts | ||
// that also act as delegators. For simplicity, each validator is bonded with a delegation | ||
// of one consensus engine unit in the default token of the simapp from first genesis | ||
// account. A Nop logger is set in SimApp. | ||
func SetupWithGenesisValSet(t *testing.T, valSet *types2.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *App { | ||
t.Helper() | ||
|
||
app, genesisState := setup(true, 5) | ||
genesisState = genesisStateWithValSet(t, app, genesisState, valSet, genAccs, balances...) | ||
|
||
genesisState = setRollappVersion(app.appCodec, genesisState, "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0") | ||
|
||
stateBytes, err := json.MarshalIndent(genesisState, "", " ") | ||
require.NoError(t, err) | ||
|
||
proto, err := encoding.PubKeyToProto(valSet.Validators[0].PubKey) | ||
require.NoError(t, err) | ||
|
||
// init chain will set the validator set and initialize the genesis accounts | ||
app.InitChain( | ||
abci.RequestInitChain{ | ||
Validators: []abci.ValidatorUpdate{ | ||
{ | ||
PubKey: proto, | ||
Power: valSet.Validators[0].VotingPower, | ||
}, | ||
}, | ||
ConsensusParams: utils.DefaultConsensusParams, | ||
AppStateBytes: stateBytes, | ||
ChainId: TestChainID, | ||
}, | ||
) | ||
|
||
// commit genesis changes | ||
app.Commit() | ||
app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ | ||
ChainID: TestChainID, | ||
Height: app.LastBlockHeight() + 1, | ||
AppHash: app.LastCommitID().Hash, | ||
ValidatorsHash: valSet.Hash(), | ||
NextValidatorsHash: valSet.Hash(), | ||
}}) | ||
|
||
return app | ||
} | ||
|
||
func genesisStateWithValSet(t *testing.T, | ||
app *App, genesisState GenesisState, | ||
valSet *types2.ValidatorSet, genAccs []authtypes.GenesisAccount, | ||
balances ...banktypes.Balance, | ||
) GenesisState { | ||
// set genesis accounts | ||
authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) | ||
genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) | ||
|
||
validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) | ||
delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) | ||
|
||
bondAmt := sdk.DefaultPowerReduction | ||
|
||
for _, val := range valSet.Validators { | ||
pk, err := codec.FromTmPubKeyInterface(val.PubKey) | ||
require.NoError(t, err) | ||
pkAny, err := codectypes.NewAnyWithValue(pk) | ||
require.NoError(t, err) | ||
validator := stakingtypes.Validator{ | ||
OperatorAddress: sdk.ValAddress(val.Address).String(), | ||
ConsensusPubkey: pkAny, | ||
Jailed: false, | ||
Status: stakingtypes.Bonded, | ||
Tokens: bondAmt, | ||
DelegatorShares: sdk.OneDec(), | ||
Description: stakingtypes.Description{}, | ||
UnbondingHeight: int64(0), | ||
UnbondingTime: time.Unix(0, 0).UTC(), | ||
Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), | ||
MinSelfDelegation: sdk.ZeroInt(), | ||
} | ||
validators = append(validators, validator) | ||
delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) | ||
|
||
} | ||
// set validators and delegations | ||
stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) | ||
genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) | ||
|
||
totalSupply := sdk.NewCoins() | ||
for _, b := range balances { | ||
// add genesis acc tokens to total supply | ||
totalSupply = totalSupply.Add(b.Coins...) | ||
} | ||
|
||
for range delegations { | ||
// add delegated tokens to total supply | ||
totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) | ||
} | ||
|
||
// add bonded amount to bonded pool module account | ||
balances = append(balances, banktypes.Balance{ | ||
Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), | ||
Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, | ||
}) | ||
|
||
// update total supply | ||
bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) | ||
genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) | ||
|
||
return genesisState | ||
} | ||
|
||
func setRollappVersion(appCodec appcodec.Codec, genesisState GenesisState, version string) GenesisState { | ||
var rollappParamsGenesis rollappparamstypes.GenesisState | ||
if genesisState["rollappparams"] != nil { | ||
appCodec.MustUnmarshalJSON(genesisState["rollappparams"], &rollappParamsGenesis) | ||
} else { | ||
rollappParamsGenesis = rollappparamstypes.GenesisState{ | ||
Params: rollappparamstypes.Params{ | ||
Version: version, | ||
}, | ||
} | ||
} | ||
|
||
rollappParamsGenesis.Params.Version = version | ||
|
||
genesisState["rollappparams"] = appCodec.MustMarshalJSON(&rollappParamsGenesis) | ||
|
||
return genesisState | ||
} |
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 is this message allowed?