diff --git a/precompiles/confidentialtransfers/ct.go b/precompiles/confidentialtransfers/ct.go index b2480a69a..ba96e8f47 100644 --- a/precompiles/confidentialtransfers/ct.go +++ b/precompiles/confidentialtransfers/ct.go @@ -237,13 +237,17 @@ func (p PrecompileExecutor) getTransferMessageFromArgs(ctx sdk.Context, caller c return nil, err } + return BuildTransferMsgFromArgs(fromAddr.String(), toAddress.String(), args) +} + +func BuildTransferMsgFromArgs(fromAddress string, toAddress string, args []interface{}) (*cttypes.MsgTransfer, error) { denom, ok := args[1].(string) if !ok || denom == "" { return nil, errors.New("invalid denom") } var fromAmountLo cttypes.Ciphertext - err = fromAmountLo.Unmarshal(args[2].([]byte)) + err := fromAmountLo.Unmarshal(args[2].([]byte)) if err != nil { return nil, err } @@ -284,8 +288,8 @@ func (p PrecompileExecutor) getTransferMessageFromArgs(ctx sdk.Context, caller c } return &cttypes.MsgTransfer{ - FromAddress: fromAddr.String(), - ToAddress: toAddress.String(), + FromAddress: fromAddress, + ToAddress: toAddress, Denom: denom, FromAmountLo: &fromAmountLo, FromAmountHi: &fromAmountHi, @@ -348,16 +352,27 @@ func (p PrecompileExecutor) getAssociatedAddressesBySeiAddress(ctx sdk.Context, return seiAddr, evmAddr, nil } -func (p PrecompileExecutor) getAuditorsFromArg(ctx sdk.Context, arg interface{}) (auditorsArray []*cttypes.Auditor, rerr error) { +// GetCtAuditors parses the auditors array from the arguments, it returns anonymous struct similar to types.CtAuditor. +// To achieve this, we need to define (twice as return type and in method body) an anonymous struct similar to +// types.CtAuditor because the ABI returns an anonymous struct. +func GetCtAuditors(arg interface{}) (ctAuditors []struct { + AuditorAddress string `json:"auditorAddress"` + EncryptedTransferAmountLo []byte `json:"encryptedTransferAmountLo"` + EncryptedTransferAmountHi []byte `json:"encryptedTransferAmountHi"` + TransferAmountLoValidityProof []byte `json:"transferAmountLoValidityProof"` + TransferAmountHiValidityProof []byte `json:"transferAmountHiValidityProof"` + TransferAmountLoEqualityProof []byte `json:"transferAmountLoEqualityProof"` + TransferAmountHiEqualityProof []byte `json:"transferAmountHiEqualityProof"` +}, e error) { defer func() { if err := recover(); err != nil { - auditorsArray = nil - rerr = fmt.Errorf("error parsing auditors array: %s", err) + ctAuditors = nil + e = fmt.Errorf("error parsing auditors array: %s", err) return } }() // we need to define an anonymous struct similar to types.CtAuditor because the ABI returns an anonymous struct - evmAuditors := arg.([]struct { + ctAuditors = arg.([]struct { AuditorAddress string `json:"auditorAddress"` EncryptedTransferAmountLo []byte `json:"encryptedTransferAmountLo"` EncryptedTransferAmountHi []byte `json:"encryptedTransferAmountHi"` @@ -367,64 +382,91 @@ func (p PrecompileExecutor) getAuditorsFromArg(ctx sdk.Context, arg interface{}) TransferAmountHiEqualityProof []byte `json:"transferAmountHiEqualityProof"` }) - if len(evmAuditors) == 0 { + if len(ctAuditors) == 0 { + return nil, errors.New("auditors array cannot be empty") + } + return ctAuditors, nil +} + +func (p PrecompileExecutor) getAuditorsFromArg(ctx sdk.Context, arg interface{}) (auditorsArray []*cttypes.Auditor, rerr error) { + defer func() { + if err := recover(); err != nil { + auditorsArray = nil + rerr = fmt.Errorf("error processing autitors: %s", err) + return + } + }() + ctAuditors, err := GetCtAuditors(arg) + if err != nil { + return nil, err + } + + if len(ctAuditors) == 0 { return nil, errors.New("auditors array cannot be empty") } auditors := make([]*cttypes.Auditor, 0) - for _, auditor := range evmAuditors { + for _, auditor := range ctAuditors { auditorAddr, err := p.getValidSeiAddressFromString(ctx, auditor.AuditorAddress) if err != nil { return nil, err } - var encryptedTransferAmountLo cttypes.Ciphertext - err = encryptedTransferAmountLo.Unmarshal(auditor.EncryptedTransferAmountLo) + a, err := GetAuditorFromCtAuditor(auditorAddr.String(), auditor) if err != nil { return nil, err } + auditors = append(auditors, a) + } + return auditors, nil +} - var encryptedTransferAmountHi cttypes.Ciphertext - err = encryptedTransferAmountHi.Unmarshal(auditor.EncryptedTransferAmountHi) - if err != nil { - return nil, err - } +// GetAuditorFromCtAuditor translates auditor payload from the format that Solidity understands to the internal format +func GetAuditorFromCtAuditor(address string, ctAuditor cttypes.CtAuditor) (*cttypes.Auditor, error) { + var encryptedTransferAmountLo cttypes.Ciphertext + err := encryptedTransferAmountLo.Unmarshal(ctAuditor.EncryptedTransferAmountLo) + if err != nil { + return nil, err + } - var transferAmountLoValidityProof cttypes.CiphertextValidityProof - err = transferAmountLoValidityProof.Unmarshal(auditor.TransferAmountLoValidityProof) - if err != nil { - return nil, err - } - var transferAmountHiValidityProof cttypes.CiphertextValidityProof - err = transferAmountHiValidityProof.Unmarshal(auditor.TransferAmountHiValidityProof) - if err != nil { - return nil, err - } + var encryptedTransferAmountHi cttypes.Ciphertext + err = encryptedTransferAmountHi.Unmarshal(ctAuditor.EncryptedTransferAmountHi) + if err != nil { + return nil, err + } - var transferAmountLoEqualityProof cttypes.CiphertextCiphertextEqualityProof - err = transferAmountLoEqualityProof.Unmarshal(auditor.TransferAmountLoEqualityProof) - if err != nil { - return nil, err - } + var transferAmountLoValidityProof cttypes.CiphertextValidityProof + err = transferAmountLoValidityProof.Unmarshal(ctAuditor.TransferAmountLoValidityProof) + if err != nil { + return nil, err + } + var transferAmountHiValidityProof cttypes.CiphertextValidityProof + err = transferAmountHiValidityProof.Unmarshal(ctAuditor.TransferAmountHiValidityProof) + if err != nil { + return nil, err + } - var transferAmountHiEqualityProof cttypes.CiphertextCiphertextEqualityProof - err = transferAmountHiEqualityProof.Unmarshal(auditor.TransferAmountHiEqualityProof) - if err != nil { - return nil, err - } + var transferAmountLoEqualityProof cttypes.CiphertextCiphertextEqualityProof + err = transferAmountLoEqualityProof.Unmarshal(ctAuditor.TransferAmountLoEqualityProof) + if err != nil { + return nil, err + } - a := &cttypes.Auditor{ - AuditorAddress: auditorAddr.String(), - EncryptedTransferAmountLo: &encryptedTransferAmountLo, - EncryptedTransferAmountHi: &encryptedTransferAmountHi, - TransferAmountLoValidityProof: &transferAmountLoValidityProof, - TransferAmountHiValidityProof: &transferAmountHiValidityProof, - TransferAmountLoEqualityProof: &transferAmountLoEqualityProof, - TransferAmountHiEqualityProof: &transferAmountHiEqualityProof, - } - auditors = append(auditors, a) + var transferAmountHiEqualityProof cttypes.CiphertextCiphertextEqualityProof + err = transferAmountHiEqualityProof.Unmarshal(ctAuditor.TransferAmountHiEqualityProof) + if err != nil { + return nil, err } - return auditors, nil + + return &cttypes.Auditor{ + AuditorAddress: address, + EncryptedTransferAmountLo: &encryptedTransferAmountLo, + EncryptedTransferAmountHi: &encryptedTransferAmountHi, + TransferAmountLoValidityProof: &transferAmountLoValidityProof, + TransferAmountHiValidityProof: &transferAmountHiValidityProof, + TransferAmountLoEqualityProof: &transferAmountLoEqualityProof, + TransferAmountHiEqualityProof: &transferAmountHiEqualityProof, + }, nil } func (p PrecompileExecutor) initializeAccount(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) (ret []byte, remainingGas uint64, rerr error) { @@ -453,54 +495,64 @@ func (p PrecompileExecutor) initializeAccount(ctx sdk.Context, method *abi.Metho return } + msg, err := BuildInitializeAccountMsgFromArgs(seiAddr.String(), args) + if err != nil { + rerr = err + return + } + + _, err = p.ctKeeper.InitializeAccount(sdk.WrapSDKContext(ctx), msg) + if err != nil { + rerr = err + return + } + ret, rerr = method.Outputs.Pack(true) + remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + return +} + +func BuildInitializeAccountMsgFromArgs(address string, args []interface{}) (*cttypes.MsgInitializeAccount, error) { denom := args[1].(string) if denom == "" { - rerr = errors.New("invalid denom") - return + return nil, errors.New("invalid denom") } publicKey, ok := args[2].([]byte) if !ok { - rerr = errors.New("invalid public key") - return + return nil, errors.New("invalid public key") } decryptableBalance := args[3].(string) if decryptableBalance == "" { - rerr = errors.New("invalid decryptable balance") - return + return nil, errors.New("invalid decryptable balance") } var pendingBalanceLo cttypes.Ciphertext - err = pendingBalanceLo.Unmarshal(args[4].([]byte)) + err := pendingBalanceLo.Unmarshal(args[4].([]byte)) if err != nil { - rerr = err - return + return nil, err } var pendingBalanceHi cttypes.Ciphertext err = pendingBalanceHi.Unmarshal(args[5].([]byte)) if err != nil { - rerr = err - return + return nil, err } var availableBalance cttypes.Ciphertext err = availableBalance.Unmarshal(args[6].([]byte)) if err != nil { - rerr = err - return + return nil, err } var initializeAccountProofs cttypes.InitializeAccountMsgProofs err = initializeAccountProofs.Unmarshal(args[7].([]byte)) if err != nil { - rerr = err - return + return nil, err } - msg := &cttypes.MsgInitializeAccount{ - FromAddress: seiAddr.String(), + return &cttypes.MsgInitializeAccount{ + FromAddress: address, Denom: denom, PublicKey: publicKey, DecryptableBalance: decryptableBalance, @@ -508,16 +560,7 @@ func (p PrecompileExecutor) initializeAccount(ctx sdk.Context, method *abi.Metho PendingBalanceHi: &pendingBalanceHi, AvailableBalance: &availableBalance, Proofs: &initializeAccountProofs, - } - - _, err = p.ctKeeper.InitializeAccount(sdk.WrapSDKContext(ctx), msg) - if err != nil { - rerr = err - return - } - ret, rerr = method.Outputs.Pack(true) - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - return + }, nil } func (p PrecompileExecutor) deposit(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) (ret []byte, remainingGas uint64, rerr error) { @@ -541,33 +584,38 @@ func (p PrecompileExecutor) deposit(ctx sdk.Context, method *abi.Method, caller return } + msg, err := BuildDepositMsgFromArgs(seiAddr.String(), args) + if err != nil { + rerr = err + return + } + _, err = p.ctKeeper.Deposit(sdk.WrapSDKContext(ctx), msg) + if err != nil { + rerr = err + return + } + ret, rerr = method.Outputs.Pack(true) + remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + return +} + +func BuildDepositMsgFromArgs(address string, args []interface{}) (*cttypes.MsgDeposit, error) { denom := args[0].(string) if denom == "" { - rerr = errors.New("invalid denom") - return + return nil, errors.New("invalid denom") } // for usei denom amount should be treated as 6 decimal instead of 19 decimal amount, ok := args[1].(uint64) if !ok { - rerr = errors.New("invalid amount") - return + return nil, errors.New("invalid amount") } - msg := &cttypes.MsgDeposit{ - FromAddress: seiAddr.String(), + return &cttypes.MsgDeposit{ + FromAddress: address, Denom: denom, Amount: amount, - } - - _, err = p.ctKeeper.Deposit(sdk.WrapSDKContext(ctx), msg) - if err != nil { - rerr = err - return - } - ret, rerr = method.Outputs.Pack(true) - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - return + }, nil } func (p PrecompileExecutor) applyPendingBalance(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) (ret []byte, remainingGas uint64, rerr error) { @@ -591,48 +639,52 @@ func (p PrecompileExecutor) applyPendingBalance(ctx sdk.Context, method *abi.Met return } + msg, err := BuildApplyPendingBalanceMsgFromArgs(fromAddr.String(), args) + if err != nil { + rerr = err + return + } + + _, err = p.ctKeeper.ApplyPendingBalance(sdk.WrapSDKContext(ctx), msg) + if err != nil { + rerr = err + return + } + + ret, rerr = method.Outputs.Pack(true) + remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + return +} + +func BuildApplyPendingBalanceMsgFromArgs(address string, args []interface{}) (*cttypes.MsgApplyPendingBalance, error) { denom := args[0].(string) if denom == "" { - rerr = errors.New("invalid denom") - return + return nil, errors.New("invalid denom") } decryptableBalance := args[1].(string) if decryptableBalance == "" { - rerr = errors.New("invalid decryptable balance") - return + return nil, errors.New("invalid decryptable balance") } pendingBalanceCreditCounter, ok := args[2].(uint32) if !ok { - rerr = errors.New("invalid pendingBalanceCreditCounter") - return + return nil, errors.New("invalid pendingBalanceCreditCounter") } var availableBalance cttypes.Ciphertext - err = availableBalance.Unmarshal(args[3].([]byte)) + err := availableBalance.Unmarshal(args[3].([]byte)) if err != nil { - rerr = err - return + return nil, err } - msg := &cttypes.MsgApplyPendingBalance{ - Address: fromAddr.String(), + return &cttypes.MsgApplyPendingBalance{ + Address: address, Denom: denom, NewDecryptableAvailableBalance: decryptableBalance, CurrentPendingBalanceCounter: pendingBalanceCreditCounter, CurrentAvailableBalance: &availableBalance, - } - - _, err = p.ctKeeper.ApplyPendingBalance(sdk.WrapSDKContext(ctx), msg) - if err != nil { - rerr = err - return - } - - ret, rerr = method.Outputs.Pack(true) - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - return + }, nil } func (p PrecompileExecutor) withdraw(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) (ret []byte, remainingGas uint64, rerr error) { @@ -656,56 +708,59 @@ func (p PrecompileExecutor) withdraw(ctx sdk.Context, method *abi.Method, caller return } - denom := args[0].(string) - if denom == "" { - rerr = errors.New("invalid denom") + msg, err := BuildWithdrawMsgFromArgs(fromAddr.String(), args) + if err != nil { + rerr = err + return + } + _, err = p.ctKeeper.Withdraw(sdk.WrapSDKContext(ctx), msg) + if err != nil { + rerr = err return } + ret, rerr = method.Outputs.Pack(true) + remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + return +} + +func BuildWithdrawMsgFromArgs(address string, args []interface{}) (*cttypes.MsgWithdraw, error) { + denom, ok := args[0].(string) + if !ok || denom == "" { + return nil, errors.New("invalid denom") + } + amount, ok := args[1].(*big.Int) if !ok { - rerr = errors.New("invalid amount") - return + return nil, errors.New("invalid amount") } decryptableBalance := args[2].(string) if decryptableBalance == "" { - rerr = errors.New("invalid decryptable balance") - return + return nil, errors.New("invalid decryptable balance") } var remainingBalanceCommitment cttypes.Ciphertext - err = remainingBalanceCommitment.Unmarshal(args[3].([]byte)) + err := remainingBalanceCommitment.Unmarshal(args[3].([]byte)) if err != nil { - rerr = errors.New("invalid remainingBalanceCommitment") - return + return nil, errors.New("invalid remainingBalanceCommitment") } var withdrawProofs cttypes.WithdrawMsgProofs err = withdrawProofs.Unmarshal(args[4].([]byte)) if err != nil { - rerr = err - return + return nil, err } - msg := &cttypes.MsgWithdraw{ - FromAddress: fromAddr.String(), + return &cttypes.MsgWithdraw{ + FromAddress: address, Denom: denom, Amount: amount.String(), DecryptableBalance: decryptableBalance, RemainingBalanceCommitment: &remainingBalanceCommitment, Proofs: &withdrawProofs, - } - - _, err = p.ctKeeper.Withdraw(sdk.WrapSDKContext(ctx), msg) - if err != nil { - rerr = err - return - } + }, nil - ret, rerr = method.Outputs.Pack(true) - remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) - return } func (p PrecompileExecutor) closeAccount(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) (ret []byte, remainingGas uint64, rerr error) { @@ -729,25 +784,12 @@ func (p PrecompileExecutor) closeAccount(ctx sdk.Context, method *abi.Method, ca return } - denom := args[0].(string) - if denom == "" { - rerr = errors.New("invalid denom") - return - } - - var closeAccountProofs cttypes.CloseAccountMsgProofs - err = closeAccountProofs.Unmarshal(args[1].([]byte)) + msg, err := BuildCloseAccountMsgFromArgs(fromAddr.String(), args) if err != nil { rerr = err return } - msg := &cttypes.MsgCloseAccount{ - Address: fromAddr.String(), - Denom: denom, - Proofs: &closeAccountProofs, - } - _, err = p.ctKeeper.CloseAccount(sdk.WrapSDKContext(ctx), msg) if err != nil { rerr = err @@ -758,6 +800,25 @@ func (p PrecompileExecutor) closeAccount(ctx sdk.Context, method *abi.Method, ca return } +func BuildCloseAccountMsgFromArgs(address string, args []interface{}) (*cttypes.MsgCloseAccount, error) { + denom, ok := args[0].(string) + if !ok || denom == "" { + return nil, errors.New("invalid denom") + } + + var closeAccountProofs cttypes.CloseAccountMsgProofs + err := closeAccountProofs.Unmarshal(args[1].([]byte)) + if err != nil { + return nil, err + } + + return &cttypes.MsgCloseAccount{ + Address: address, + Denom: denom, + Proofs: &closeAccountProofs, + }, nil +} + type CtAccount struct { PublicKey []byte PendingBalanceLo []byte diff --git a/x/confidentialtransfers/client/cli/query.go b/x/confidentialtransfers/client/cli/query.go index 3ee15f260..f3f76e10b 100644 --- a/x/confidentialtransfers/client/cli/query.go +++ b/x/confidentialtransfers/client/cli/query.go @@ -1,10 +1,14 @@ package cli import ( - "context" "crypto/ecdsa" - "encoding/hex" + "encoding/json" + "errors" "fmt" + "io" + "net/http" + "reflect" + "strings" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -12,16 +16,24 @@ import ( cdctypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + "github.com/ethereum/go-ethereum/common" "github.com/gogo/protobuf/proto" + pre "github.com/sei-protocol/sei-chain/precompiles/common" + ctpre "github.com/sei-protocol/sei-chain/precompiles/confidentialtransfers" "github.com/sei-protocol/sei-chain/x/confidentialtransfers/types" "github.com/sei-protocol/sei-chain/x/confidentialtransfers/utils" + evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" + ethtxtypes "github.com/sei-protocol/sei-chain/x/evm/types/ethtx" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" "github.com/spf13/cobra" + tmtypes "github.com/tendermint/tendermint/abci/types" ) const ( decryptAvailableBalanceFlag = "decrypt-available-balance" decryptorFlag = "decryptor" + flagRPC = "evm-rpc" ) // GetQueryCmd returns the cli query commands for the minting module. @@ -213,6 +225,7 @@ func GetCmdQueryTx() *cobra.Command { flags.AddQueryFlagsToCmd(cmd) cmd.Flags().String(decryptorFlag, "", "Name or address of private key to decrypt the account") cmd.Flags().Bool(decryptAvailableBalanceFlag, false, "Set this to attempt to decrypt the available balance") + cmd.Flags().String(flagRPC, "", "EVM RPC endpoint e.g. http://localhost:8545") return cmd } @@ -225,6 +238,18 @@ func queryDecryptedTx(cmd *cobra.Command, args []string) error { // Get the transaction hash txHashHex := args[0] + evmRpc, err := cmd.Flags().GetString(flagRPC) + if err != nil { + return err + } + + if evmRpc != "" { + txHashHex, err = getTxHashByEvmHash(evmRpc, txHashHex) + if err != nil { + return err + } + } + decryptorAccount, err := cmd.Flags().GetString(decryptorFlag) if err != nil { return err @@ -251,27 +276,17 @@ func queryDecryptedTx(cmd *cobra.Command, args []string) error { } } - // Decode the transaction hash from hex to bytes - txHash, err := hex.DecodeString(txHashHex) + txResponse, err := authtx.QueryTx(clientCtx, txHashHex) if err != nil { - return fmt.Errorf("failed to decode transaction hash: %w", err) - } - - // Connect to the gRPC client - node, err := clientCtx.GetNode() - if err != nil { - return fmt.Errorf("failed to connect to node: %w", err) + return err } - - // Query the transaction using Tendermint's Tx endpoint - res, err := node.Tx(context.Background(), txHash, false) - if err != nil { - return fmt.Errorf("failed to fetch transaction: %w", err) + if txResponse.Tx == nil { + return fmt.Errorf("transaction not found") } // Decode the transaction var rawTx tx.Tx - if err := clientCtx.Codec.Unmarshal(res.Tx, &rawTx); err != nil { + if err := clientCtx.Codec.Unmarshal(txResponse.Tx.Value, &rawTx); err != nil { return fmt.Errorf("failed to unmarshal transaction: %w", err) } @@ -283,7 +298,7 @@ func queryDecryptedTx(cmd *cobra.Command, args []string) error { } msgPrinted := false for _, msg := range rawTx.Body.Messages { - result, foundMsg, err := handleDecryptableMessage(clientCtx.Codec, msg, decryptor, privateKey, decryptAvailableBalance, fromAddr.String()) + result, foundMsg, err := handleDecryptableMessage(clientCtx.Codec, msg, txResponse.Events, decryptor, privateKey, decryptAvailableBalance, fromAddr.String(), evmRpc) if !foundMsg { continue } else { @@ -309,10 +324,12 @@ func queryDecryptedTx(cmd *cobra.Command, args []string) error { func handleDecryptableMessage( cdc codec.Codec, msgAny *cdctypes.Any, + events []tmtypes.Event, decryptor *elgamal.TwistedElGamal, privKey *ecdsa.PrivateKey, decryptAvailableBalance bool, - address string) (msg proto.Message, foundDecryptableMsg bool, error error) { + address string, + evmRpc string) (msg proto.Message, foundDecryptableMsg bool, error error) { // Try to unmarshal the message as one of the known types var sdkmsg sdk.Msg err := cdc.UnpackAny(msgAny, &sdkmsg) @@ -320,6 +337,15 @@ func handleDecryptableMessage( return nil, false, nil } + // If the message is of MsgEVMTransaction type, convert it to a corresponding confidential transfer message + // e.g. MsgTransfer + if isEvmMsg(sdkmsg) { + sdkmsg, err = convertEvmMsgToCtMsg(sdkmsg, events, evmRpc) + if err != nil { + return nil, false, err + } + } + var result proto.Message switch message := sdkmsg.(type) { case *types.MsgInitializeAccount: @@ -340,3 +366,216 @@ func handleDecryptableMessage( return result, true, err } + +type RpcResponse struct { + JSONRPC string `json:"jsonrpc"` + ID string `json:"id"` + Result string `json:"result"` +} + +func getTxHashByEvmHash(evmRpc string, ethHash string) (string, error) { + body := fmt.Sprintf("{\"jsonrpc\": \"2.0\",\"method\": \"sei_getCosmosTx\",\"params\":[\"%s\"],\"id\":\"cosmos_tx\"}", ethHash) + return executeRpcCall(evmRpc, body) +} + +func getSeiAddress(evmRpc string, evmAddress string) (string, error) { + body := fmt.Sprintf("{\"jsonrpc\": \"2.0\",\"method\": \"sei_getSeiAddress\",\"params\":[\"%s\"],\"id\":\"1\"}", evmAddress) + return executeRpcCall(evmRpc, body) +} + +func executeRpcCall(evmRpc string, requestBody string) (string, error) { + req, err := http.NewRequest(http.MethodGet, evmRpc, strings.NewReader(requestBody)) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + resBody, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + var response RpcResponse + err = json.Unmarshal(resBody, &response) + if err != nil { + return "", err + } + return response.Result, nil +} + +func isEvmMsg(msg sdk.Msg) bool { + return reflect.TypeOf(msg) == reflect.TypeOf(&evmtypes.MsgEVMTransaction{}) +} + +func convertEvmMsgToCtMsg(sdkmsg sdk.Msg, events []tmtypes.Event, evmRpc string) (sdk.Msg, error) { + message := sdkmsg.(*evmtypes.MsgEVMTransaction) + var dyanmicFeeTx ethtxtypes.DynamicFeeTx + data := message.GetData() + err := proto.Unmarshal(data.Value, &dyanmicFeeTx) + if err != nil { + return nil, err + } + + methodID, err := pre.ExtractMethodID(dyanmicFeeTx.Data) + if err != nil { + return nil, err + } + ctPrecompile, _ := ctpre.NewPrecompile(nil, nil, nil) + method, err := ctPrecompile.ABI.MethodById(methodID) + if err != nil { + return nil, err + } + // In Ethereum transactions, the first 4 bytes of the Data field typically represent the method ID + argsBz := dyanmicFeeTx.Data[4:] + args, err := method.Inputs.Unpack(argsBz) + if err != nil { + return nil, err + } + + getAddressFromEvent := func(eventType, attrKey string) (string, error) { + for _, event := range events { + if event.Type == eventType { + for _, attr := range event.Attributes { + if string(attr.Key) == attrKey { + return string(attr.Value), nil + } + } + } + } + return "", fmt.Errorf("address not found for event type %s", eventType) + } + + switch method.Name { + case ctpre.ApplyPendingBalanceMethod: + address, err := getAddressFromEvent(types.EventTypeApplyPendingBalance, types.AttributeAddress) + if err != nil { + return nil, err + } + msg, err := ctpre.BuildApplyPendingBalanceMsgFromArgs(address, args) + if err != nil { + return nil, err + } + return msg, nil + case ctpre.DepositMethod: + address, err := getAddressFromEvent(types.EventTypeDeposit, types.AttributeAddress) + if err != nil { + return nil, err + } + msg, err := ctpre.BuildDepositMsgFromArgs(address, args) + if err != nil { + return nil, err + } + return msg, nil + case ctpre.InitializeAccountMethod: + address, err := getAddressFromEvent(types.EventTypeInitializeAccount, types.AttributeAddress) + if err != nil { + return nil, err + } + msg, err := ctpre.BuildInitializeAccountMsgFromArgs(address, args) + if err != nil { + return nil, err + } + return msg, nil + case ctpre.TransferMethod: + fromAddress, err := getAddressFromEvent(types.TypeMsgTransfer, types.AttributeSender) + if err != nil { + return nil, err + } + toAddress, err := getAddressFromEvent(types.TypeMsgTransfer, types.AttributeRecipient) + if err != nil { + return nil, err + } + msg, err := ctpre.BuildTransferMsgFromArgs(fromAddress, toAddress, args) + if err != nil { + return nil, err + } + return msg, nil + case ctpre.TransferWithAuditorsMethod: + fromAddress, err := getAddressFromEvent(types.EventTypeTransfer, types.AttributeSender) + if err != nil { + return nil, err + } + toAddress, err := getAddressFromEvent(types.EventTypeTransfer, types.AttributeRecipient) + if err != nil { + return nil, err + } + msg, err := ctpre.BuildTransferMsgFromArgs(fromAddress, toAddress, args) + if err != nil { + return nil, err + } + + auditors, err := getAuditorsFromArg(evmRpc, args[9]) + if err != nil { + return nil, err + } + msg.Auditors = auditors + return msg, nil + case ctpre.WithdrawMethod: + address, err := getAddressFromEvent(types.EventTypeWithdraw, types.AttributeAddress) + if err != nil { + return nil, err + } + msg, err := ctpre.BuildWithdrawMsgFromArgs(address, args) + if err != nil { + return nil, err + } + return msg, nil + case ctpre.CloseAccountMethod: + address, err := getAddressFromEvent(types.EventTypeCloseAccount, types.AttributeAddress) + if err != nil { + return nil, err + } + msg, err := ctpre.BuildCloseAccountMsgFromArgs(address, args) + if err != nil { + return nil, err + } + return msg, nil + default: + return nil, fmt.Errorf("unknown method %s", method.Name) + } +} + +func getAuditorsFromArg(evmRpc string, arg interface{}) ([]*types.Auditor, error) { + ctAuditors, err := ctpre.GetCtAuditors(arg) + if err != nil { + return nil, err + } + + if len(ctAuditors) == 0 { + return nil, errors.New("auditors array cannot be empty") + } + + auditors := make([]*types.Auditor, 0) + for _, auditor := range ctAuditors { + auditorAddr, err := getValidSeiAddressFromString(evmRpc, auditor.AuditorAddress) + if err != nil { + return nil, err + } + + a, err := ctpre.GetAuditorFromCtAuditor(auditorAddr, auditor) + if err != nil { + return nil, err + } + auditors = append(auditors, a) + } + return auditors, nil +} + +func getValidSeiAddressFromString(evmRpc string, addr string) (string, error) { + if common.IsHexAddress(addr) { + evmAddr := common.HexToAddress(addr) + res, err := getSeiAddress(evmRpc, evmAddr.String()) + if err != nil { + return "", err + } + return res, nil + } + if seiAddress, err := sdk.AccAddressFromBech32(addr); err != nil { + return "", fmt.Errorf("invalid address %s: %w", addr, err) + } else { + return seiAddress.String(), nil + } +} diff --git a/x/confidentialtransfers/types/transfer.go b/x/confidentialtransfers/types/transfer.go index 9c215cc7a..6fa450ff5 100644 --- a/x/confidentialtransfers/types/transfer.go +++ b/x/confidentialtransfers/types/transfer.go @@ -49,6 +49,7 @@ type TransferAuditor struct { TransferAmountHiEqualityProof *zkproofs.CiphertextCiphertextEqualityProof `json:"transfer_amount_hi_equality_proof"` } +// CtAuditor is a struct that represents the auditor's view of a transfer and is used in precompiles/solidity. type CtAuditor struct { AuditorAddress string `json:"auditorAddress"` EncryptedTransferAmountLo []byte `json:"encryptedTransferAmountLo"`