Skip to content

Commit

Permalink
Merge pull request #399 from PeggyJV/bolten/2.0.0-rc3
Browse files Browse the repository at this point in the history
Test and fix community spend proposal
  • Loading branch information
EricBolten authored Apr 22, 2022
2 parents 838c0d2 + 5c26884 commit 0bfefb9
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 38 deletions.
142 changes: 140 additions & 2 deletions integration_tests/happy_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/ethereum/go-ethereum/common"
"github.com/peggyjv/gravity-bridge/module/v2/x/gravity/types"
)
Expand All @@ -20,7 +22,7 @@ func (s *IntegrationTestSuite) TestHappyPath() {
s.Require().NoError(err, "error getting allowance of gravity contract spending on behalf of first validator")
s.Require().Equal(UInt256Max(), allowance.BigInt(), "spending allowance not set correctly, got: %s", allowance.String())

balance, err := s.getEthBalanceOf(common.HexToAddress(s.chain.validators[0].ethereumKey.address))
balance, err := s.getEthTokenBalanceOf(common.HexToAddress(s.chain.validators[0].ethereumKey.address), testERC20contract)
s.Require().NoError(err, "error getting first validator balance")
s.Require().Equal(sdk.NewUint(10000).BigInt(), balance.BigInt(), "balance was %s, expected 10000", balance.String())

Expand Down Expand Up @@ -100,7 +102,6 @@ func (s *IntegrationTestSuite) TestHappyPath() {
}, 105*time.Second, 10*time.Second, "balance never found on cosmos")

s.T().Logf("sending to ethereum")

sendToEthereumMsg := types.NewMsgSendToEthereum(
s.chain.validators[1].keyInfo.GetAddress(),
s.chain.validators[1].ethereumKey.address,
Expand Down Expand Up @@ -129,5 +130,142 @@ func (s *IntegrationTestSuite) TestHappyPath() {
return true
}, 105*time.Second, 10*time.Second, "unable to send to ethereum")

s.T().Logf("funding community pool")
orch := s.chain.orchestrators[0]
clientCtx, err := s.chain.clientContext("tcp://localhost:26657", orch.keyring, "orch", orch.keyInfo.GetAddress())
s.Require().NoError(err)

fundCommunityPoolMsg := distrtypes.NewMsgFundCommunityPool(
sdk.NewCoins(sdk.NewCoin(testDenom, sdk.NewInt(1000000000))),
orch.keyInfo.GetAddress(),
)

s.Require().Eventuallyf(func() bool {
response, err := s.chain.sendMsgs(*clientCtx, fundCommunityPoolMsg)
if err != nil {
s.T().Logf("error: %s", err)
return false
}
if response.Code != 0 {
if response.Code != 32 {
s.T().Log(response)
}
return false
}
return true
}, 105*time.Second, 10*time.Second, "unable to fund community pool")

distrQueryClient := distrtypes.NewQueryClient(clientCtx)
poolRes, err := distrQueryClient.CommunityPool(context.Background(),
&distrtypes.QueryCommunityPoolRequest{},
)
s.Require().NoError(err, "error retrieving community pool")
s.Require().True(poolRes.Pool.AmountOf(testDenom).GT(sdk.NewDec(1000000000)))

s.T().Logf("deploying testgb as an ERC20")
gbQueryClient := types.NewQueryClient(clientCtx)
paramsRes, err := gbQueryClient.DenomToERC20Params(context.Background(),
&types.DenomToERC20ParamsRequest{
Denom: testDenom,
})
s.Require().NoError(err, "error retrieving ERC20 params for testgb denom")

err = s.deployERC20(paramsRes.BaseDenom, paramsRes.Erc20Name, paramsRes.Erc20Symbol, uint8(paramsRes.Erc20Decimals))
s.Require().NoError(err, "error deploying testgb as an ERC20")

s.Require().Eventuallyf(func() bool {
erc20Res, err := gbQueryClient.DenomToERC20(context.Background(),
&types.DenomToERC20Request{
Denom: testDenom,
},
)
if err != nil {
s.T().Logf("erc20 not deployed yet, waiting")
return false
}

s.Require().True(erc20Res.CosmosOriginated)
return true
}, 180*time.Second, 10*time.Second, "unable to verify ERC20 deployment")

s.T().Logf("create governance proposal to fund an ethereum address")
orch = s.chain.orchestrators[0]
clientCtx, err = s.chain.clientContext("tcp://localhost:26657", orch.keyring, "orch", orch.keyInfo.GetAddress())
s.Require().NoError(err)

proposal := types.CommunityPoolEthereumSpendProposal{
Title: "community pool spend ethereum",
Description: "community pool spend ethereum",
Recipient: s.chain.validators[2].ethereumKey.address,
Amount: sdk.NewCoin(testDenom, sdk.NewInt(900000000)),
BridgeFee: sdk.NewCoin(testDenom, sdk.NewInt(1000000)),
}

proposalMsg, err := govtypes.NewMsgSubmitProposal(
&proposal,
sdk.Coins{
{
Denom: testDenom,
Amount: sdk.NewInt(2),
},
},
orch.keyInfo.GetAddress(),
)
s.Require().NoError(err, "unable to create governance proposal")

s.T().Log("submit proposal spending community pool funds")
submitProposalResponse, err := s.chain.sendMsgs(*clientCtx, proposalMsg)
s.Require().NoError(err)
s.Require().Zero(submitProposalResponse.Code, "raw log: %s", submitProposalResponse.RawLog)

s.T().Log("check proposal was submitted correctly")
govQueryClient := govtypes.NewQueryClient(clientCtx)
proposalsQueryResponse, err := govQueryClient.Proposals(context.Background(), &govtypes.QueryProposalsRequest{})
s.Require().NoError(err)
s.Require().NotEmpty(proposalsQueryResponse.Proposals)
s.Require().Equal(uint64(1), proposalsQueryResponse.Proposals[0].ProposalId, "not proposal id 1")
s.Require().Equal(govtypes.StatusVotingPeriod, proposalsQueryResponse.Proposals[0].Status, "proposal not in voting period")

s.T().Log("vote for community spend proposal")
for _, val := range s.chain.validators {
kr, err := val.keyring()
s.Require().NoError(err)
clientCtx, err := s.chain.clientContext("tcp://localhost:26657", &kr, "val", val.keyInfo.GetAddress())
s.Require().NoError(err)

voteMsg := govtypes.NewMsgVote(val.keyInfo.GetAddress(), 1, govtypes.OptionYes)
voteResponse, err := s.chain.sendMsgs(*clientCtx, voteMsg)
s.Require().NoError(err)
s.Require().Zero(voteResponse.Code, "vote error: %s", voteResponse.RawLog)
}

s.T().Log("wait for community spend proposal to be approved")
s.Require().Eventuallyf(func() bool {
proposalQueryResponse, err := govQueryClient.Proposal(context.Background(), &govtypes.QueryProposalRequest{ProposalId: 1})
s.Require().NoError(err)
return govtypes.StatusPassed == proposalQueryResponse.Proposal.Status
}, time.Second*30, time.Second*5, "proposal was never accepted")

erc20Res, err := gbQueryClient.DenomToERC20(context.Background(),
&types.DenomToERC20Request{
Denom: testDenom,
},
)
s.Require().NoError(err, "error querying ERC20 for testgb denom")
erc20Contract := common.HexToAddress(erc20Res.Erc20)

s.T().Log("waiting for community funds to reach destination")
s.Require().Eventuallyf(func() bool {
balance, err := s.getEthTokenBalanceOf(common.HexToAddress(s.chain.validators[2].ethereumKey.address), erc20Contract)
s.Require().NoError(err, "error getting destination balance")

if balance.LT(sdk.NewInt(900000000)) {
s.T().Logf("funds not received yet, dest balance: %s", balance.String())
return false
}

s.Require().Equal(balance.BigInt(), sdk.NewInt(900000000).BigInt(), "balance was %s, expected 900000000", balance.String())
return true
}, time.Second*180, time.Second*10, "community funds did not reach destination")
})
}
32 changes: 15 additions & 17 deletions integration_tests/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/ory/dockertest/v3"
Expand Down Expand Up @@ -277,6 +278,16 @@ func (s *IntegrationTestSuite) initGenesis() {
s.Require().NoError(err)
appGenState[banktypes.ModuleName] = bz

var govGenState govtypes.GenesisState
s.Require().NoError(cdc.UnmarshalJSON(appGenState[govtypes.ModuleName], &govGenState))

// set short voting period to allow gov proposals in tests
govGenState.VotingParams.VotingPeriod = time.Second * 20
govGenState.DepositParams.MinDeposit = sdk.Coins{{Denom: testDenom, Amount: sdk.OneInt()}}
bz, err = cdc.MarshalJSON(&govGenState)
s.Require().NoError(err)
appGenState[govtypes.ModuleName] = bz

// set crisis denom
var crisisGenState crisistypes.GenesisState
s.Require().NoError(cdc.UnmarshalJSON(appGenState[crisistypes.ModuleName], &crisisGenState))
Expand Down Expand Up @@ -716,20 +727,7 @@ func (s *IntegrationTestSuite) TestBasicChain() {
}

func (s *IntegrationTestSuite) deployERC20(denom string, name string, symbol string, decimals uint8) error {
ethClient, err := ethclient.Dial(fmt.Sprintf("http://%s", s.ethResource.GetHostPort("8545/tcp")))
if err != nil {
return err
}

data := PackDeployERC20(denom, name, symbol, decimals)

_, err = ethClient.CallContract(context.Background(), ethereum.CallMsg{
From: common.HexToAddress(s.chain.validators[0].ethereumKey.address),
To: &gravityContract,
Gas: 0,
Data: data,
}, nil)
return err
return s.SendEthTransaction(s.chain.validators[0], gravityContract, PackDeployERC20(denom, name, symbol, decimals))
}

func (s *IntegrationTestSuite) approveERC20() error {
Expand All @@ -740,7 +738,7 @@ func (s *IntegrationTestSuite) sendToCosmos(destination sdk.AccAddress, amount s
return s.SendEthTransaction(s.chain.validators[0], gravityContract, PackSendToCosmos(testERC20contract, destination, amount))
}

func (s *IntegrationTestSuite) getEthBalanceOf(account common.Address) (*sdk.Int, error) {
func (s *IntegrationTestSuite) getEthTokenBalanceOf(account common.Address, erc20contract common.Address) (*sdk.Int, error) {
ethClient, err := ethclient.Dial(fmt.Sprintf("http://%s", s.ethResource.GetHostPort("8545/tcp")))
if err != nil {
return nil, err
Expand All @@ -750,7 +748,7 @@ func (s *IntegrationTestSuite) getEthBalanceOf(account common.Address) (*sdk.Int

response, err := ethClient.CallContract(context.Background(), ethereum.CallMsg{
From: common.HexToAddress(s.chain.validators[0].ethereumKey.address),
To: &testERC20contract,
To: &erc20contract,
Gas: 0,
Data: data,
}, nil)
Expand Down Expand Up @@ -810,7 +808,7 @@ func (s *IntegrationTestSuite) SendEthTransaction(validator *validator, toAddres
}

value := big.NewInt(0)
gasLimit := uint64(500000)
gasLimit := uint64(1000000)
gasPrice, err := ethClient.SuggestGasPrice(context.Background())
if err != nil {
return err
Expand Down
36 changes: 18 additions & 18 deletions module/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,24 +371,6 @@ func NewGravityApp(
scopedIBCKeeper,
)

govRouter := govtypes.NewRouter()
govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler).
AddRoute(paramsproposal.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)).
AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.upgradeKeeper)).
AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.ibcKeeper.ClientKeeper)).
AddRoute(gravitytypes.RouterKey, gravity.NewCommunityPoolEthereumSpendProposalHandler(app.gravityKeeper))

app.govKeeper = govkeeper.NewKeeper(
appCodec,
keys[govtypes.StoreKey],
app.GetSubspace(govtypes.ModuleName),
app.accountKeeper,
app.bankKeeper,
&stakingKeeper,
govRouter,
)

app.transferKeeper = ibctransferkeeper.NewKeeper(
appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName),
app.ibcKeeper.ChannelKeeper, &app.ibcKeeper.PortKeeper,
Expand Down Expand Up @@ -422,6 +404,24 @@ func NewGravityApp(
app.ModuleAccountAddressesToNames([]string{distrtypes.ModuleName}),
)

govRouter := govtypes.NewRouter()
govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler).
AddRoute(paramsproposal.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)).
AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.upgradeKeeper)).
AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.ibcKeeper.ClientKeeper)).
AddRoute(gravitytypes.RouterKey, gravity.NewCommunityPoolEthereumSpendProposalHandler(app.gravityKeeper))

app.govKeeper = govkeeper.NewKeeper(
appCodec,
keys[govtypes.StoreKey],
app.GetSubspace(govtypes.ModuleName),
app.accountKeeper,
app.bankKeeper,
&stakingKeeper,
govRouter,
)

app.setupUpgradeStoreLoaders()

var skipGenesisInvariants = cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants))
Expand Down
3 changes: 2 additions & 1 deletion module/x/gravity/keeper/proposal_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ func (k Keeper) HandleCommunityPoolEthereumSpendProposal(ctx sdk.Context, p *typ
// NOTE the community pool isn't a module account, however its coins
// are held in the distribution module account. Thus the community pool
// must be reduced separately from the createSendToEthereum calls
newPool, negative := feePool.CommunityPool.SafeSub(sdk.NewDecCoinsFromCoins(p.Amount, p.BridgeFee))
totalToSpend := p.Amount.Add(p.BridgeFee)
newPool, negative := feePool.CommunityPool.SafeSub(sdk.NewDecCoinsFromCoins(totalToSpend))
if negative {
return distributiontypes.ErrBadDistribution
}
Expand Down
7 changes: 7 additions & 0 deletions orchestrator/relayer/src/logic_call_relaying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ use ethereum_gravity::one_eth_f32;
use ethereum_gravity::{
logic_call::send_eth_logic_call, types::EthClient, utils::get_logic_call_nonce,
};
use ethers::prelude::ContractError;
use ethers::prelude::signer::SignerMiddlewareError;
use ethers::types::Address as EthAddress;
use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient;
use gravity_utils::error::GravityError;
use gravity_utils::ethereum::{bytes_to_hex_str, downcast_to_f32};
use gravity_utils::types::{LogicCallConfirmResponse, Valset};
use gravity_utils::{message_signatures::encode_logic_call_confirm_hashed, types::LogicCall};
Expand Down Expand Up @@ -38,6 +41,10 @@ pub async fn relay_logic_calls(
let mut oldest_signatures: Option<Vec<LogicCallConfirmResponse>> = None;
for call in latest_calls {
if logic_call_skips.should_skip(&call) {
warn!(
"Skipping LogicCall {}/{}, will be skipped until on-chain timeout at eth height {} or process restart",
bytes_to_hex_str(&call.invalidation_id), call.invalidation_nonce, call.timeout
);
continue;
}

Expand Down

0 comments on commit 0bfefb9

Please sign in to comment.