217 pop participant election (#244)

* implemented SelectPopParticipants
* added check if enough participants to initiate PoP
* make use of lib for broadcasting tx

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
Lorenz Herzberger 2023-12-21 12:29:11 +01:00 committed by GitHub
parent c09aab8b38
commit 0cab7d5878
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 253 additions and 6 deletions

View File

@ -103,3 +103,6 @@ issues:
linters:
- dupl
- paralleltest
- path: tests/.*/*\.go
linters:
- paralleltest

View File

@ -568,10 +568,12 @@ func New(
keys[daomoduletypes.ChallengeKey],
keys[daomoduletypes.MintRequestHashKey],
keys[daomoduletypes.MintRequestAddressKey],
keys[authtypes.StoreKey],
app.GetSubspace(daomoduletypes.ModuleName),
app.BankKeeper,
app.AccountKeeper,
app.MachineKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
daoModule := daomodule.NewAppModule(appCodec, app.DaoKeeper, app.AccountKeeper, app.BankKeeper)

View File

@ -9,7 +9,11 @@ import (
)
func TestE2ETestSuite(t *testing.T) {
t.Parallel()
cfg := network.DefaultConfig()
suite.Run(t, NewE2ETestSuite(cfg))
}
func TestPopE2ETestSuite(t *testing.T) {
cfg := network.DefaultConfig()
suite.Run(t, NewPopSelectionE2ETestSuite(cfg))
}

View File

@ -0,0 +1,130 @@
package dao
import (
"strconv"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/planetmint/planetmint-go/config"
"github.com/planetmint/planetmint-go/lib"
clitestutil "github.com/planetmint/planetmint-go/testutil/cli"
"github.com/planetmint/planetmint-go/testutil/network"
"github.com/planetmint/planetmint-go/testutil/sample"
daocli "github.com/planetmint/planetmint-go/x/dao/client/cli"
machinetypes "github.com/planetmint/planetmint-go/x/machine/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
var machines = []struct {
name string
mnemonic string
address string
}{
{
name: "R2D2",
mnemonic: "number judge garbage lock village slush business upset suspect green wrestle puzzle foil tragic drum stereo ticket teach upper bone inject monkey deny portion",
address: "plmnt1kp93kns6hs2066d8qw0uz84fw3vlthewt2ck6p",
},
{
name: "C3PO",
mnemonic: "letter plate husband impulse grid lake panel seminar try powder virtual run spice siege mutual enhance ripple country two boring have convince symptom fuel",
address: "plmnt15wrx9eqegjtlvvx80huau7rkn3f44rdj969xrx",
},
}
type PopSelectionE2ETestSuite struct {
suite.Suite
cfg network.Config
network *network.Network
}
func NewPopSelectionE2ETestSuite(cfg network.Config) *PopSelectionE2ETestSuite {
return &PopSelectionE2ETestSuite{cfg: cfg}
}
func (s *PopSelectionE2ETestSuite) SetupSuite() {
s.T().Log("setting up e2e test suite")
cfg := config.GetConfig()
cfg.FeeDenom = "stake"
s.network = network.New(s.T(), s.cfg)
// create 2 machines accounts
for i, machine := range machines {
s.attestMachine(machine.name, machine.mnemonic, i)
}
}
// TearDownSuite clean up after testing
func (s *PopSelectionE2ETestSuite) TearDownSuite() {
s.T().Log("tearing down e2e test suite")
}
func (s *PopSelectionE2ETestSuite) TestPopSelection() {
val := s.network.Validators[0]
// set PopEpochs to 1 in Order to trigger some participant selections
cfg := config.GetConfig()
cfg.PopEpochs = 1
// wait for some blocks so challenges get stored
s.Require().NoError(s.network.WaitForNextBlock())
s.Require().NoError(s.network.WaitForNextBlock())
// check if machines are selected as challanger/challengee
height, _ := s.network.LatestHeight()
queryHeight := height - 1
out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, daocli.CmdGetChallenge(), []string{
strconv.FormatInt(queryHeight, 10),
})
s.Require().NoError(err)
assert.Contains(s.T(), out.String(), machines[0].address)
assert.Contains(s.T(), out.String(), machines[1].address)
}
func (s *PopSelectionE2ETestSuite) attestMachine(name string, mnemonic string, num int) {
val := s.network.Validators[0]
kb := val.ClientCtx.Keyring
account, err := kb.NewAccount(name, mnemonic, keyring.DefaultBIP39Passphrase, sample.DefaultDerivationPath, hd.Secp256k1)
s.Require().NoError(err)
addr, _ := account.GetAddress()
// sending funds to machine to initialize account on chain
coin := sdk.NewCoins(sdk.NewInt64Coin("stake", 1000))
sendMsg := banktypes.NewMsgSend(val.Address, addr, coin)
_, err = lib.BroadcastTxWithFileLock(val.Address, sendMsg)
s.Require().NoError(err)
s.Require().NoError(s.network.WaitForNextBlock())
// register Ta
prvKey, pubKey := sample.KeyPair(num)
ta := sample.TrustAnchor(pubKey)
registerMsg := machinetypes.NewMsgRegisterTrustAnchor(val.Address.String(), &ta)
_, err = lib.BroadcastTxWithFileLock(val.Address, registerMsg)
s.Require().NoError(err)
s.Require().NoError(s.network.WaitForNextBlock())
// name and address of private key with which to sign
clientCtx := val.ClientCtx.
WithFromAddress(addr).
WithFromName(name)
libConfig := lib.GetConfig()
libConfig.SetClientCtx(clientCtx)
machine := sample.Machine(name, pubKey, prvKey, addr.String())
attestMsg := machinetypes.NewMsgAttestMachine(addr.String(), &machine)
_, err = lib.BroadcastTxWithFileLock(addr, attestMsg)
s.Require().NoError(err)
s.Require().NoError(s.network.WaitForNextBlock())
// reset clientCtx to validator ctx
libConfig.SetClientCtx(val.ClientCtx)
}

View File

@ -64,9 +64,11 @@ func DaoKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
challengeStoreKey,
mintRequestHashStoreKey,
mintRequestAddressStoreKey,
nil, // TODO: mount and add store/key
paramsSubspace,
bk,
nil,
nil,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

View File

@ -24,12 +24,13 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
hexProposerAddress := hex.EncodeToString(proposerAddress)
if isPopHeight(req.Header.GetHeight()) {
// select PoP participants
challenger := ""
challengee := ""
challenger, challengee := k.SelectPopParticipants(ctx)
// Issue PoP
util.SendInitPoP(ctx, hexProposerAddress, challenger, challengee, currentBlockHeight)
// TODO send MQTT message to challenger && challengee
if challenger != "" && challengee != "" {
// Issue PoP
util.SendInitPoP(ctx, hexProposerAddress, challenger, challengee, currentBlockHeight)
// TODO send MQTT message to challenger && challengee
}
}
if isReIssuanceHeight(currentBlockHeight) {

View File

@ -4,8 +4,10 @@ import (
"fmt"
"cosmossdk.io/math"
db "github.com/cometbft/cometbft-db"
"github.com/cometbft/cometbft/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
@ -25,10 +27,12 @@ type (
challengeKey storetypes.StoreKey
mintRequestHashKey storetypes.StoreKey
mintRequestAddressKey storetypes.StoreKey
accountKeeperKey storetypes.StoreKey
paramstore paramtypes.Subspace
bankKeeper types.BankKeeper
accountKeeper types.AccountKeeper
machineKeeper types.MachineKeeper
authority string
}
)
@ -40,10 +44,12 @@ func NewKeeper(
challengeKey storetypes.StoreKey,
mintRequestHashKey storetypes.StoreKey,
mintRequestAddressKey storetypes.StoreKey,
accountKeeperKey storetypes.StoreKey,
ps paramtypes.Subspace,
bankKeeper types.BankKeeper,
accountKeeper types.AccountKeeper,
machineKeeper types.MachineKeeper,
authority string,
) *Keeper {
// set KeyTable if it has not already been set
@ -58,10 +64,12 @@ func NewKeeper(
challengeKey: challengeKey,
mintRequestHashKey: mintRequestHashKey,
mintRequestAddressKey: mintRequestAddressKey,
accountKeeperKey: accountKeeperKey,
paramstore: ps,
bankKeeper: bankKeeper,
accountKeeper: accountKeeper,
machineKeeper: machineKeeper,
authority: authority,
}
}
@ -143,3 +151,56 @@ func (k Keeper) processBalances(ctx sdk.Context, balances map[string]math.Int, t
}
return nil
}
func (k Keeper) SelectPopParticipants(ctx sdk.Context) (challenger string, challengee string) {
cfg := config.GetConfig()
var startAccountNumber uint64
lastPopHeight := ctx.BlockHeight() - int64(cfg.PopEpochs)
lastPop, found := k.LookupChallenge(ctx, lastPopHeight)
if lastPopHeight > 0 && found {
lastAccountAddr := sdk.MustAccAddressFromBech32(lastPop.Challengee)
lastAccount := k.accountKeeper.GetAccount(ctx, lastAccountAddr)
startAccountNumber = lastAccount.GetAccountNumber() + 1
}
var participants []sdk.AccAddress
k.iterateAccountsForMachines(ctx, startAccountNumber, &participants, true)
if len(participants) != 2 {
k.iterateAccountsForMachines(ctx, startAccountNumber, &participants, false)
}
// Not enough participants
if len(participants) != 2 {
return
}
challenger = participants[0].String()
challengee = participants[1].String()
return
}
func (k Keeper) iterateAccountsForMachines(ctx sdk.Context, start uint64, participants *[]sdk.AccAddress, iterateFromStart bool) {
store := ctx.KVStore(k.accountKeeperKey)
accountStore := prefix.NewStore(store, authtypes.AccountNumberStoreKeyPrefix)
var iterator db.Iterator
if iterateFromStart {
iterator = accountStore.Iterator(sdk.Uint64ToBigEndian(start), nil)
} else {
iterator = accountStore.Iterator(nil, sdk.Uint64ToBigEndian(start))
}
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
participant := sdk.AccAddress(iterator.Value())
_, found := k.machineKeeper.GetMachineIndexByAddress(ctx, participant.String())
if found {
*participants = append(*participants, participant)
}
if len(*participants) == 2 {
return
}
}
}

View File

@ -10,6 +10,7 @@ import (
types "github.com/cosmos/cosmos-sdk/types"
types0 "github.com/cosmos/cosmos-sdk/x/auth/types"
gomock "github.com/golang/mock/gomock"
types1 "github.com/planetmint/planetmint-go/x/machine/types"
)
// MockAccountKeeper is a mock of AccountKeeper interface.
@ -195,3 +196,41 @@ func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gom
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr)
}
// MockMachineKeeper is a mock of MachineKeeper interface.
type MockMachineKeeper struct {
ctrl *gomock.Controller
recorder *MockMachineKeeperMockRecorder
}
// MockMachineKeeperMockRecorder is the mock recorder for MockMachineKeeper.
type MockMachineKeeperMockRecorder struct {
mock *MockMachineKeeper
}
// NewMockMachineKeeper creates a new mock instance.
func NewMockMachineKeeper(ctrl *gomock.Controller) *MockMachineKeeper {
mock := &MockMachineKeeper{ctrl: ctrl}
mock.recorder = &MockMachineKeeperMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockMachineKeeper) EXPECT() *MockMachineKeeperMockRecorder {
return m.recorder
}
// GetMachineIndexByAddress mocks base method.
func (m *MockMachineKeeper) GetMachineIndexByAddress(ctx types.Context, address string) (types1.MachineIndex, bool) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetMachineIndexByAddress", ctx, address)
ret0, _ := ret[0].(types1.MachineIndex)
ret1, _ := ret[1].(bool)
return ret0, ret1
}
// GetMachineIndexByAddress indicates an expected call of GetMachineIndexByAddress.
func (mr *MockMachineKeeperMockRecorder) GetMachineIndexByAddress(ctx, address interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMachineIndexByAddress", reflect.TypeOf((*MockMachineKeeper)(nil).GetMachineIndexByAddress), ctx, address)
}

View File

@ -3,6 +3,7 @@ package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/types"
machinetypes "github.com/planetmint/planetmint-go/x/machine/types"
)
// AccountKeeper defines the expected account keeper used for simulations (noalias)
@ -24,3 +25,7 @@ type BankKeeper interface {
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
// Methods imported from bank should be defined here
}
type MachineKeeper interface {
GetMachineIndexByAddress(ctx sdk.Context, address string) (val machinetypes.MachineIndex, found bool)
}