Skip to content
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

Contract Event Emission #283

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ test-integration-xion-send-platform-fee: compile_integration_tests
test-integration-xion-abstract-account: compile_integration_tests
@XION_TEST_IMAGE=$(XION_TEST_IMAGE) ./integration_tests/integration_tests.test -test.failfast -test.v -test.run XionAbstractAccount

test-integration-xion-abstract-account-event: compile_integration_tests
@XION_TEST_IMAGE=$(XION_TEST_IMAGE) ./integration_tests/integration_tests.test -test.failfast -test.v -test.run XionClientEvent

test-integration-xion-min-default: compile_integration_tests
@XION_TEST_IMAGE=$(XION_TEST_IMAGE) ./integration_tests/integration_tests.test -test.failfast -test.v -test.run TestXionMinimumFeeDefault

Expand Down
290 changes: 289 additions & 1 deletion integration_tests/abstract_account_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package integration_tests

import (
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
Expand Down Expand Up @@ -29,6 +30,9 @@ import (
"github.com/stretchr/testify/require"

wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
cometClient "github.com/cometbft/cometbft/rpc/client"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
cometRpcCoreTypes "github.com/cometbft/cometbft/rpc/core/types"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
aatypes "github.com/larry0x/abstract-account/x/abstractaccount/types"
Expand Down Expand Up @@ -580,7 +584,7 @@ func TestXionAbstractAccount(t *testing.T) {
aaContractAddr,
path.Join(xion.GetNode().HomeDir(), removeFilePath[len(removeFilePath)-1]),
"--chain-id", xion.Config().ChainID,
"--authenticator-id", "1",
"--nuthenticator-id", "1",
)
require.NoError(t, err)

Expand Down Expand Up @@ -617,3 +621,287 @@ func GetAAContractAddress(t *testing.T, txDetails map[string]interface{}) string

return addr
}

func TestXionClientEvent(t *testing.T) {
if testing.Short() {
t.Skip("skipping in short mode")
}

t.Parallel()
td := BuildXionChain(t, "0.0uxion", ModifyInterChainGenesis(ModifyInterChainGenesisFn{ModifyGenesisShortProposals}, [][]string{{votingPeriod, maxDepositPeriod}}))
xion, ctx := td.xionChain, td.ctx

config := types.GetConfig()
config.SetBech32PrefixForAccount("xion", "xionpub")

// Create and Fund User Wallets
t.Log("creating and funding user accounts")
fundAmount := math.NewInt(10_000_000)
xionUser, err := ibctest.GetAndFundTestUserWithMnemonic(ctx, "default", deployerMnemonic, fundAmount, xion)
require.NoError(t, err)
currentHeight, _ := xion.Height(ctx)
err = testutil.WaitForBlocks(ctx, int(currentHeight)+8, xion)
require.NoError(t, err)
t.Logf("created xion user %s", xionUser.FormattedAddress())

xionUserBalInitial, err := xion.GetBalance(ctx, xionUser.FormattedAddress(), xion.Config().Denom)
require.NoError(t, err)
require.Equal(t, fundAmount, xionUserBalInitial)

// Create a Secondary Key For Rotation
recipientKeyName := "recipient-key"
err = xion.CreateKey(ctx, recipientKeyName)
require.NoError(t, err)
receipientKeyAddressBytes, err := xion.GetAddress(ctx, recipientKeyName)
require.NoError(t, err)
recipientKeyAddress, err := types.Bech32ifyAddressBytes(xion.Config().Bech32Prefix, receipientKeyAddressBytes)
require.NoError(t, err)

// Get Public Key For Funded Account
account, err := ExecBin(t, ctx, xion.GetNode(),
"keys", "show",
xionUser.KeyName(),
"--keyring-backend", keyring.BackendTest,
"-p",
)
require.NoError(t, err)
t.Log("Funded Account:")
for k, v := range account {
t.Logf("[%s]: %v", k, v)
}

fp, err := os.Getwd()
require.NoError(t, err)

// Store Wasm Contract
codeID, err := xion.StoreContract(ctx, xionUser.FormattedAddress(), path.Join(fp,
"integration_tests", "testdata", "contracts", "account-wasm-updatable-event-aarch64.wasm"))
require.NoError(t, err)

// retrieve the hash
codeResp, err := ExecQuery(t, ctx, xion.GetNode(),
"wasm", "code-info", codeID)
require.NoError(t, err)
t.Logf("code response: %s", codeResp)

depositedFunds := fmt.Sprintf("%d%s", 100000, xion.Config().Denom)

registeredTxHash, err := ExecTx(t, ctx, xion.GetNode(),
xionUser.KeyName(),
"xion", "register",
codeID,
xionUser.KeyName(),
"--funds", depositedFunds,
"--salt", "0",
"--authenticator", "Secp256K1",
"--chain-id", xion.Config().ChainID,
)
require.NoError(t, err)
t.Logf("tx hash: %s", registeredTxHash)

txDetails, err := ExecQuery(t, ctx, xion.GetNode(), "tx", registeredTxHash)
require.NoError(t, err)
t.Logf("TxDetails: %s", txDetails)
aaContractAddr := GetAAContractAddress(t, txDetails)

contractBalance, err := xion.GetBalance(ctx, aaContractAddr, xion.Config().Denom)
require.NoError(t, err)
require.Equal(t, math.NewInt(100000), contractBalance)

contractState, err := ExecQuery(t, ctx, xion.GetNode(), "wasm", "contract-state", "smart", aaContractAddr, `{"authenticator_by_i_d":{ "id": 0 }}`)
require.NoError(t, err)

pubkey64, ok := contractState["data"].(string)
require.True(t, ok)
pubkeyRawJSON, err := base64.StdEncoding.DecodeString(pubkey64)
require.NoError(t, err)
var pubKeyMap jsonAuthenticator
json.Unmarshal(pubkeyRawJSON, &pubKeyMap)
require.Equal(t, account["key"], pubKeyMap["Secp256K1"]["pubkey"])

// Generate Msg Send without signatures
jsonMsg := RawJSONMsgSend(t, aaContractAddr, recipientKeyAddress, xion.Config().Denom)
require.NoError(t, err)
require.True(t, json.Valid(jsonMsg))

sendFile, err := os.CreateTemp("", "*-msg-bank-send.json")
require.NoError(t, err)
defer os.Remove(sendFile.Name())

_, err = sendFile.Write(jsonMsg)
require.NoError(t, err)

err = UploadFileToContainer(t, ctx, xion.GetNode(), sendFile)
require.NoError(t, err)

// Sign and broadcast a transaction
sendFilePath := strings.Split(sendFile.Name(), "/")
_, err = ExecTx(t, ctx, xion.GetNode(),
xionUser.KeyName(),
"xion", "sign",
xionUser.KeyName(),
aaContractAddr,
path.Join(xion.GetNode().HomeDir(), sendFilePath[len(sendFilePath)-1]),
"--chain-id", xion.Config().ChainID,
)
require.NoError(t, err)

err = testutil.WaitForBlocks(ctx, 1, xion)
require.NoError(t, err)

// Confirm the updated balance
balance, err := xion.GetBalance(ctx, recipientKeyAddress, xion.Config().Denom)
require.NoError(t, err)
require.Equal(t, math.NewInt(100000).Uint64(), balance.Uint64())

// Generate Key Rotation Msg
account, err = ExecBin(t, ctx, xion.GetNode(),
"keys", "show",
xionUser.KeyName(),
"--keyring-backend", keyring.BackendTest,
"-p",
)

// add secondary authenticator to account. in this case, the same key but in a different position
jsonExecMsgStr, err := GenerateTx(t, ctx, xion.GetNode(),
xionUser.KeyName(),
"xion", "add-authenticator", aaContractAddr,
"--authenticator-id", "1",
"--chain-id", xion.Config().ChainID,
)
require.NoError(t, err)
jsonExecMsg := []byte(jsonExecMsgStr)
require.True(t, json.Valid(jsonExecMsg))

rotateFile, err := os.CreateTemp("", "*-msg-exec-rotate-key.json")
require.NoError(t, err)
defer os.Remove(rotateFile.Name())

_, err = rotateFile.Write(jsonExecMsg)
require.NoError(t, err)

err = UploadFileToContainer(t, ctx, xion.GetNode(), rotateFile)
require.NoError(t, err)

rotateFilePath := strings.Split(rotateFile.Name(), "/")

_, err = ExecTx(t, ctx, xion.GetNode(),
xionUser.KeyName(),
"xion", "sign",
xionUser.KeyName(),
aaContractAddr,
path.Join(xion.GetNode().HomeDir(), rotateFilePath[len(rotateFilePath)-1]),
"--chain-id", xion.Config().ChainID,
)
require.NoError(t, err)
updatedContractState, err := ExecQuery(t, ctx, xion.GetNode(), "wasm", "contract-state", "smart", aaContractAddr, `{"authenticator_by_i_d":{ "id": 1 }}`)
require.NoError(t, err)

updatedPubKey, ok := updatedContractState["data"].(string)
require.True(t, ok)

updatedPubKeyRawJSON, err := base64.StdEncoding.DecodeString(updatedPubKey)
require.NoError(t, err)
var updatedPubKeyMap jsonAuthenticator

err = json.Unmarshal(updatedPubKeyRawJSON, &updatedPubKeyMap)
require.NoError(t, err)
require.Equal(t, account["key"], updatedPubKeyMap["Secp256K1"]["pubkey"])

cometWsClient, err := getCometClient(xion.GetHostRPCAddress()) // Note: add a close function
require.NoError(t, err)
fmt.Printf("%+v\n", xion.GetNode())

err = cometWsClient.Start()
require.NoError(t, err)

//eventStream, err := subscribeToEvent(t, ctx, cometTypes.EventTx, cometWsClient)
eventStream, err := subscribeToEvent(t, ctx, cometWsClient)

require.NoError(t, err)

doneChan := make(chan struct{})
go receiveEvents(t, doneChan, eventStream)

jsonExecMsgStr, err = GenerateTx(t, ctx, xion.GetNode(),
xionUser.KeyName(),
"xion", "emit", "arbitrary_data", aaContractAddr,
"--authenticator-id", "0",
"--chain-id", xion.Config().ChainID,
)

require.NoError(t, err)
jsonExecMsg = []byte(jsonExecMsgStr)
require.True(t, json.Valid(jsonExecMsg))

rotateFile, err = os.CreateTemp("", "*-msg-exec-emit.json")
require.NoError(t, err)
defer os.Remove(rotateFile.Name())

_, err = rotateFile.Write(jsonExecMsg)
require.NoError(t, err)

err = UploadFileToContainer(t, ctx, xion.GetNode(), rotateFile)
require.NoError(t, err)

rotateFilePath = strings.Split(rotateFile.Name(), "/")

_, err = ExecTx(t, ctx, xion.GetNode(),
xionUser.KeyName(),
"xion", "sign",
xionUser.KeyName(),
aaContractAddr,
path.Join(xion.GetNode().HomeDir(), rotateFilePath[len(rotateFilePath)-1]),
"--chain-id", xion.Config().ChainID,
)
require.NoError(t, err) // it's returning an error and it's not throwing
fmt.Println("we have thrown a transaction")

//wg.Wait()
<-doneChan
stopClient(ctx, cometWsClient)
}

func getCometClient(hostAddr string) (cometClient.Client, error) {
return rpchttp.New(hostAddr, "/websocket")
}

func subscribeToEvent(t *testing.T, ctx context.Context, cli cometClient.Client) (<-chan cometRpcCoreTypes.ResultEvent, error) {
return cli.Subscribe(ctx, "helpers", "message.module='wasm' AND message.action='/cosmwasm.wasm.v1.MsgExecuteContract'")
}

func receiveEvents(t *testing.T, done chan<- struct{}, eventStream <-chan cometRpcCoreTypes.ResultEvent) {
for {
select {
case event := <-eventStream:
fmt.Println("event intercepted")
contractAddress, ok := event.Events["wasm-account_emit._contract_address"]
if !ok {
fmt.Println("not desired event")
continue
}
arbData, ok := event.Events["wasm-account_emit.data"]
if !ok {
fmt.Println("not desired event")
continue
}
fmt.Println(contractAddress)
fmt.Println(arbData)
done <- struct{}{}
return
default:
continue
}
}
}

func stopClient(ctx context.Context, cli cometClient.Client) error {
if err := cli.UnsubscribeAll(ctx, "helpers"); err != nil {
return err
}

if err := cli.Stop(); err != nil {
return err
}
return nil
}
Binary file not shown.
53 changes: 53 additions & 0 deletions x/xion/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func NewTxCmd() *cobra.Command {
NewSignCmd(),
NewAddAuthenticatorCmd(),
NewRegisterCmd(),
NewEmitArbitraryDataCmd(),
)

return txCmd
Expand Down Expand Up @@ -514,6 +515,58 @@ func NewSignCmd() *cobra.Command {
return cmd
}

// NewEmitArbitraryDataCmd returns a CLI command handler for emitting some arbitrary data from the chain.
func NewEmitArbitraryDataCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "emit <arbitrary_data> <contract_address> --authenticator-id [uint8]",
Short: "Emit an arbitrary data from the chain",
Long: `Sends an arbitrary data to the contract's emit endpoint. The contract emits the arbitrary contract on-chain.`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
arbitraryData := args[0]

contractAddr := args[1]

data := map[string]interface{}{}
data["data"] = arbitraryData
msg := map[string]interface{}{}
msg["emit"] = data

jsonMsg, err := json.Marshal(msg)
if err != nil {
return err
}

rawMsg := wasmtypes.RawContractMessage{}
err = json.Unmarshal(jsonMsg, &rawMsg)
if err != nil {
return err
}

wasmMsg := &wasmtypes.MsgExecuteContract{
Sender: contractAddr,
Contract: contractAddr,
Msg: rawMsg,
Funds: nil,
}
if err := wasmMsg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), wasmMsg)
},
}

flags.AddTxFlagsToCmd(cmd)
cmd.Flags().Uint8(flagAuthenticatorID, 0, "Authenticator index locator")

return cmd
}

func getSignerOfTx(queryClient authtypes.QueryClient, address sdk.AccAddress) (*aatypes.AbstractAccount, error) {
res, err := queryClient.Account(context.Background(), &authtypes.QueryAccountRequest{Address: address.String()})
if err != nil {
Expand Down
Loading