diff --git a/.github/workflows/merge_hook.yml b/.github/workflows/merge_hook.yml new file mode 100644 index 0000000..6141a5e --- /dev/null +++ b/.github/workflows/merge_hook.yml @@ -0,0 +1,44 @@ +--- +name: Release Merged Hook + +on: + pull_request: + types: [closed] + branches: + - master + +jobs: + trigger-dispatch: + if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/v') + runs-on: ubuntu-latest + steps: + - name: Extract Version from Branch Name + id: version-extract + run: | + VERSION=$(echo "${{ github.event.pull_request.head.ref }}" | sed 's|release/v||') + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: Trigger Repository Dispatch Event + env: + VERSION: ${{ env.VERSION }} + run: | + curl -X POST -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: Bearer ${{ secrets.REPO_ACCESS }}" \ + "https://api.github.com/repos/KiraCore/sekin/dispatches" \ + -d @- < 2 && denom[0] == 'v' && strings.Contains(denom, "/") + if derivedParam == "true" && !isDerivedDenom { + continue + } + if derivedParam == "false" && isDerivedDenom { + continue + } + + filteredData = append(filteredData, balance) + } + + // if request does not have tokens list, return with pagination + offsetParam := r.URL.Query().Get("offset") + offset, err := strconv.Atoi(offsetParam) + if err != nil { + offset = 0 + } + limitParam := r.URL.Query().Get("limit") + limit, err := strconv.Atoi(limitParam) + if err != nil { + limit = 100 + } + + lastIndex := offset + limit + if lastIndex > len(filteredData) { + lastIndex = len(filteredData) + } + data = filteredData[offset:lastIndex] + + result := BalancesResult{ + Balances: data, + Pagination: &Pagination{ + NextKey: fmt.Sprintf("%d", lastIndex), + Total: fmt.Sprintf("%d", len(filteredData)), + }, + } + + return result, nil, http.StatusOK } // QueryBalancesRequest is a function to query balances. diff --git a/gateway/kira/main.go b/gateway/kira/main.go index 4992465..ee4c5bf 100644 --- a/gateway/kira/main.go +++ b/gateway/kira/main.go @@ -1,6 +1,7 @@ package kira import ( + "github.com/KiraCore/interx/gateway/kira/metamask" "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" ) @@ -18,4 +19,5 @@ func RegisterRequest(router *mux.Router, gwCosmosmux *runtime.ServeMux, rpcAddr RegisterKiraSpendingRoutes(router, gwCosmosmux, rpcAddr) RegisterKiraUbiRoutes(router, gwCosmosmux, rpcAddr) RegisterKiraMultiStakingRoutes(router, gwCosmosmux, rpcAddr) + metamask.RegisterKiraMetamaskRoutes(router, gwCosmosmux, rpcAddr) } diff --git a/gateway/kira/metamask/cosmosaccountinfo.go b/gateway/kira/metamask/cosmosaccountinfo.go new file mode 100644 index 0000000..351fdce --- /dev/null +++ b/gateway/kira/metamask/cosmosaccountinfo.go @@ -0,0 +1,31 @@ +package metamask + +import ( + "net/http" + + "github.com/KiraCore/interx/common" + interxtypes "github.com/KiraCore/interx/types" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +func GetAccountInfo(params EthGetTransactionCount, gwCosmosmux *runtime.ServeMux, r *http.Request) (uint64, uint64, error) { + bech32Addr, err := hex2bech32(params.From, TypeKiraAddr) + if err != nil { + return 0, 0, err + } + + accountNumber, sequence := common.GetAccountNumberSequence(gwCosmosmux, r, bech32Addr) + + return accountNumber, sequence, nil +} + +func GetBalance(params EthGetBalanceParams, gwCosmosmux *runtime.ServeMux, r *http.Request) []interxtypes.Coin { + bech32Addr, err := hex2bech32(params.From, TypeKiraAddr) + if err != nil { + return nil + } + + balances := common.GetAccountBalances(gwCosmosmux, r.Clone(r.Context()), bech32Addr) + + return balances +} diff --git a/gateway/kira/metamask/cosmosblockinfo.go b/gateway/kira/metamask/cosmosblockinfo.go new file mode 100644 index 0000000..0fc2471 --- /dev/null +++ b/gateway/kira/metamask/cosmosblockinfo.go @@ -0,0 +1,162 @@ +package metamask + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/KiraCore/interx/common" + types "github.com/cometbft/cometbft/proto/tendermint/types" +) + +const ( + BlockQueryByNumber = iota + BLockQueryByHash +) + +type PartSetHeader struct { + Total uint32 `json:"total,omitempty"` + Hash []byte `json:"hash,omitempty"` +} + +type BlockID struct { + Hash string `json:"hash,omitempty"` + PartSetHeader PartSetHeader `json:"part_set_header"` +} + +type Consensus struct { + Block uint64 `json:"block,string,omitempty"` + App uint64 `json:"app,string,omitempty"` +} + +type Header struct { + // basic block info + Version Consensus `json:"version"` + ChainID string `json:"chain_id,omitempty"` + Height int64 `json:"height,string,omitempty"` + Time time.Time `json:"time"` + // prev block info + LastBlockId types.BlockID `json:"last_block_id"` + // hashes of block data + LastCommitHash string `json:"last_commit_hash,omitempty"` + DataHash string `json:"data_hash,omitempty"` + // hashes from the app output from the prev block + ValidatorsHash string `json:"validators_hash,omitempty"` + NextValidatorsHash string `json:"next_validators_hash,omitempty"` + ConsensusHash string `json:"consensus_hash,omitempty"` + AppHash string `json:"app_hash,omitempty"` + LastResultsHash string `json:"last_results_hash,omitempty"` + // consensus info + EvidenceHash string `json:"evidence_hash,omitempty"` + // proposer_address is the original block proposer address, formatted as a Bech32 string. + // In Tendermint, this type is `bytes`, but in the SDK, we convert it to a Bech32 string + // for better UX. + ProposerAddress string `json:"proposer_address,omitempty"` +} + +type Commit struct { + Height int64 `json:"height,string,omitempty"` + Round int32 `json:"round,omitempty"` + BlockID BlockID `json:"block_id"` + Signatures []types.CommitSig `json:"signatures"` +} + +type Data struct { + // Txs that will be applied by state @ block.Height+1. + // NOTE: not all txs here are valid. We're just agreeing on the order first. + // This means that block.AppHash does not include these txs. + Txs []string `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"` +} + +type Block struct { + Header Header `json:"header"` + Data Data `json:"data"` + Evidence types.EvidenceList `json:"evidence"` + LastCommit Commit `json:"last_commit,omitempty"` +} + +type CosmosBlockInfo struct { + BlockId BlockID `json:"block_id,omitempty"` + // Since: cosmos-sdk 0.47 + SdkBlock Block `json:"block,omitempty"` +} + +func GetBlockNumber(rpcAddr string) (int, error) { + sentryStatus := common.GetKiraStatus((rpcAddr)) + currentHeight, err := strconv.Atoi(sentryStatus.SyncInfo.LatestBlockHeight) + return currentHeight, err +} + +func GetBlockByNumberOrHash(blockParam string, rpcAddr string, queryType int) (CosmosBlockInfo, []string, interface{}) { + var responseData, blockErr interface{} + var statusCode int + if queryType == BlockQueryByNumber { + var blockNum int64 + var err error + if strings.Contains(blockParam, "0x") { + blockNum, err = hex2int64(blockParam) + } else { + blockNum, err = strconv.ParseInt(blockParam, 10, 64) + } + if err != nil { + return CosmosBlockInfo{}, nil, err + } + + responseData, blockErr, statusCode = queryBlockByHeight(rpcAddr, strconv.Itoa(int(blockNum))) + } else if queryType == BLockQueryByHash { + if !strings.Contains(blockParam, "0x") { + blockParam = "0x" + blockParam + } + responseData, blockErr, statusCode = queryBlockByHash(rpcAddr, blockParam) + } + if blockErr != nil { + return CosmosBlockInfo{}, nil, blockErr + } + + if statusCode != 200 { + return CosmosBlockInfo{}, nil, fmt.Errorf("request faield, status code - %d", statusCode) + } + + jsonData, err := json.Marshal(responseData) + if err != nil { + return CosmosBlockInfo{}, nil, err + } + + response := CosmosBlockInfo{} + err = json.Unmarshal(jsonData, &response) + if err != nil { + return CosmosBlockInfo{}, nil, err + } + + txhashes := []string{} + txs := response.SdkBlock.Data.Txs + for _, txStr := range txs { + txBz, err := base64.StdEncoding.DecodeString(txStr) + if err != nil { + return CosmosBlockInfo{}, nil, err + } + converted := []byte(txBz) + hasher := sha256.New() + hasher.Write(converted) + txhashes = append(txhashes, "0x"+hex.EncodeToString(hasher.Sum(nil))) + } + + return response, txhashes, nil +} + +func queryBlockByHeight(rpcAddr string, height string) (interface{}, interface{}, int) { + success, err, statusCode := common.MakeTendermintRPCRequest(rpcAddr, "/block", fmt.Sprintf("height=%s", height)) + + return success, err, statusCode +} + +func queryBlockByHash(rpcAddr string, height string) (interface{}, interface{}, int) { + success, err, statusCode := common.MakeTendermintRPCRequest(rpcAddr, "/block_by_hash", fmt.Sprintf("hash=%s", height)) + + return success, err, statusCode +} diff --git a/gateway/kira/metamask/cosmostxinfo.go b/gateway/kira/metamask/cosmostxinfo.go new file mode 100644 index 0000000..82869a8 --- /dev/null +++ b/gateway/kira/metamask/cosmostxinfo.go @@ -0,0 +1,120 @@ +package metamask + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/KiraCore/interx/common" +) + +// Attribute is a struct that represents an attribute in the JSON data +type Attribute struct { + Key string `json:"key"` + Value string `json:"value"` +} + +// Event is a struct that represents an event in the JSON data +type Event struct { + Type string `json:"type"` + Attributes []Attribute `json:"attributes"` +} + +// Msg is a struct that represents a message in the JSON data +type LogInfo struct { + MsgIndex int `json:"msg_index"` + Events []Event `json:"events"` +} + +// map[msgIndex][event_type][attribute_key] +type LogInfoForMap struct { + LogInfo map[int]map[string]map[string]string +} + +type Log struct { + logForMap map[int]LogInfoForMap + logForString string +} + +type TxInfo struct { + Hash string `json:"hash"` + Height string `json:"height"` + Index int `json:"index"` + TxResult struct { + Code int `json:"code"` + Data string `json:"data"` + Log string `json:"log"` + Info string `json:"info"` + GasWanted string `json:"gas_wanted"` + GasUsed string `json:"gas_used"` + Events []struct { + Type string `json:"type"` + Attributes []struct { + Key string `json:"key"` + Value string `json:"value"` + Index bool `json:"index"` + } `json:"attributes"` + } `json:"events"` + Codespace string `json:"codespace"` + } `json:"tx_result"` + Tx string `json:"tx"` +} + +func GetTxInfo(txHash string, rpcAddr string) (TxInfo, Log, interface{}) { + responseData, err, statusCode := queryTxByHash(rpcAddr, txHash) + logResult := Log{} + + if err != nil { + return TxInfo{}, logResult, err + } + + if statusCode != 200 { + return TxInfo{}, logResult, fmt.Errorf("request faield, status code - %d", statusCode) + } + + jsonData, err := json.Marshal(responseData) + if err != nil { + return TxInfo{}, logResult, err + } + + response := TxInfo{} + err = json.Unmarshal(jsonData, &response) + if err != nil { + return TxInfo{}, logResult, err + } + + var logInfos []LogInfo + + // Unmarshal the JSON data to the msgs slice + err = json.Unmarshal([]byte(response.TxResult.Log), &logInfos) + logResult.logForString = response.TxResult.Log + if err != nil { + return response, logResult, nil + } + + logInfosForMap := map[int]LogInfoForMap{} + for i, logInfo := range logInfos { + logInfosForMap[i] = LogInfoForMap{LogInfo: make(map[int]map[string]map[string]string)} + for _, event := range logInfo.Events { + if logInfosForMap[i].LogInfo[logInfo.MsgIndex] == nil { + logInfosForMap[i].LogInfo[logInfo.MsgIndex] = make(map[string]map[string]string) + } + if logInfosForMap[i].LogInfo[logInfo.MsgIndex][event.Type] == nil { + logInfosForMap[i].LogInfo[logInfo.MsgIndex][event.Type] = make(map[string]string) + } + for _, attribute := range event.Attributes { + logInfosForMap[i].LogInfo[logInfo.MsgIndex][event.Type][attribute.Key] = attribute.Value + } + } + } + + logResult.logForMap = logInfosForMap + return response, logResult, nil +} + +func queryTxByHash(rpcAddr string, hash string) (interface{}, interface{}, int) { + if !strings.HasPrefix(hash, "0x") { + hash = "0x" + hash + } + return common.MakeTendermintRPCRequest(rpcAddr, "/tx", fmt.Sprintf("hash=%s", hash)) +} diff --git a/gateway/kira/metamask/cosmostxsender.go b/gateway/kira/metamask/cosmostxsender.go new file mode 100644 index 0000000..a35d49d --- /dev/null +++ b/gateway/kira/metamask/cosmostxsender.go @@ -0,0 +1,589 @@ +package metamask + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/KiraCore/interx/config" + tx "github.com/KiraCore/interx/proto-gen/cosmos/tx/v1beta1" + custodytypes "github.com/KiraCore/sekai/x/custody/types" + evidencetypes "github.com/KiraCore/sekai/x/evidence/types" + customgovtypes "github.com/KiraCore/sekai/x/gov/types" + multistakingtypes "github.com/KiraCore/sekai/x/multistaking/types" + customslashingtypes "github.com/KiraCore/sekai/x/slashing/types" + spendingtypes "github.com/KiraCore/sekai/x/spending/types" + customstakingtypes "github.com/KiraCore/sekai/x/staking/types" + tokenstypes "github.com/KiraCore/sekai/x/tokens/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc" +) + +const ( + MsgBankSend = iota + // customgov + MsgSubmitEvidence + MsgSubmitProposal + MsgVoteProposal + MsgRegisterIdentityRecords + MsgDeleteIdentityRecords + MsgRequestIdentityRecordsVerify + MsgHandleIdentityRecordsVerifyRequest + MsgCancelIdentityRecordsVerifyRequest + MsgSetNetworkProperties + MsgSetExecutionFee + MsgClaimCouncilor + MsgWhitelistPermissions + MsgBlacklistPermissions + MsgCreateRole + MsgAssignRole + MsgUnassignRole + MsgWhitelistRolePermission + MsgBlacklistRolePermission + MsgRemoveWhitelistRolePermission + MsgRemoveBlacklistRolePermission + // staking + MsgClaimValidator + // tokens + MsgUpsertTokenAlias + MsgUpsertTokenRate + // slashing + MsgActivate + MsgPause + MsgUnpause + // spending + MsgCreateSpendingPool + MsgDepositSpendingPool + MsgRegisterSpendingPoolBeneficiary + MsgClaimSpendingPool + // multistaking + MsgUpsertStakingPool + MsgDelegate + MsgUndelegate + MsgClaimRewards + MsgClaimUndelegation + MsgSetCompoundInfo + MsgRegisterDelegator + // custody module + MsgCreateCustody + MsgAddToCustodyWhiteList + MsgAddToCustodyCustodians + MsgRemoveFromCustodyCustodians + MsgDropCustodyCustodians + MsgRemoveFromCustodyWhiteList + MsgDropCustodyWhiteList + MsgApproveCustodyTx + MsgDeclineCustodyTx +) + +var grpcConn *grpc.ClientConn + +type ListRoleParam struct { + RoleIdentifier string `json:"role_identifier"` + Permission uint32 `json:"permission"` +} + +type PermissionsParam struct { + Permission uint32 `json:"permission"` + ControlledAddr string `json:"controlled_addr"` +} + +type RoleParam struct { + RoleId uint32 `json:"role_id"` + Controller string `json:"controller"` +} + +type DelegateParam struct { + Amounts string `json:"amounts"` + To string `json:"to"` +} + +type CustodianParam struct { + NewAddrs []string `json:"new_addrs"` + OldKey string `json:"old_key"` + NewKey string `json:"new_key"` + Next string `json:"next"` + Target string `json:"target"` +} + +type SingleCustodianParam struct { + NewAddr string `json:"new_addr"` + OldKey string `json:"old_key"` + NewKey string `json:"new_key"` + Next string `json:"next"` + Target string `json:"target"` +} + +type CustodyParam struct { + To string `json:"to"` + Hash string `json:"hash"` +} + +type SpendingPoolParam struct { + Name string `json:"name"` + Amounts string `json:"amounts"` +} + +// decode 256bit param like bool, uint, hex-typed address etc +func Decode256Bit(data *[]byte, params *[][]byte) error { + if len(*data) < 32 { + return errors.New("decoding 256bit failed, not enough length") + } + *params = append(*params, (*data)[:32]) + *data = (*data)[32:] + + return nil +} + +// decode string-typed param +// structure: +// * offset - offset of the string in the data : 32byte +// * length - length of the string : 32byte +// * content - content of the string : (length/32+1)*32byte +func DecodeString(data *[]byte, params *[][]byte) error { + // offset := data[:32] // string value offset + *data = (*data)[32:] + + length, err := bytes2uint64((*data)[:32]) + if err != nil { + return err + } + *data = (*data)[32:] + + *params = append(*params, (*data)[:length]) + *data = (*data)[(length/32+1)*32:] + return nil +} + +func DecodeParam(data []byte, txType int) ([][]byte, error) { + if txType == MsgBankSend { + return nil, nil + } + + var params [][]byte + + // decode data field v, r, s, sender + for i := 0; i < 4; i++ { + err := Decode256Bit(&data, ¶ms) + if err != nil { + return nil, err + } + } + + // decode param string + err := DecodeString(&data, ¶ms) + + return params, err +} + +func sendTx(txRawData string, gwCosmosmux *runtime.ServeMux, r *http.Request) (string, error) { + byteData, err := hex.DecodeString(txRawData[2:]) + if err != nil { + return "", err + } + + ethTxData, err := GetEthTxInfo(byteData) + if err != nil { + return "", err + } + + txBytes, err := SignTx(ethTxData, byteData, gwCosmosmux, r) + if err != nil { + return "", err + } + + txHash, err := sendCosmosTx(r.Context(), txBytes) + if err != nil { + return "", err + } + + return txHash, nil +} + +func getTxType(txData []byte) (int, error) { + submitEvidencePrefix, _ := hex.DecodeString("85db2453") + submitProposalPrefix, _ := hex.DecodeString("00000000") + voteProposalPrefix, _ := hex.DecodeString("7f1f06dc") + registerIdentityRecordsPrefix, _ := hex.DecodeString("bc05f106") + deleteIdentityRecordsPrefix, _ := hex.DecodeString("2dbad8e8") + requestIdentityRecordsVerifyPrefix, _ := hex.DecodeString("9765358e") + handleIdentityRecordsVerifyRequestPrefix, _ := hex.DecodeString("4335ed0c") + cancelIdentityRecordsVerifyRequestPrefix, _ := hex.DecodeString("eeaa0488") + setNetworkPropertiesPrefix, _ := hex.DecodeString("f8060fb5") + setExecutionFeePrefix, _ := hex.DecodeString("c7586de8") + claimCouncilorPrefix, _ := hex.DecodeString("b7b8ff46") + whitelistPermissionsPrefix, _ := hex.DecodeString("2f313ab8") + blacklistPermissionsPrefix, _ := hex.DecodeString("3864f845") + createRolePrefix, _ := hex.DecodeString("2d8abfdf") + assignRolePrefix, _ := hex.DecodeString("fcc121b5") + unassignRolePrefix, _ := hex.DecodeString("c79ca19d") + whitelistRolePermissionPrefix, _ := hex.DecodeString("59472362") + blacklistRolePermissionPrefix, _ := hex.DecodeString("99c557da") + removeWhitelistRolePermissionPrefix, _ := hex.DecodeString("2a11d702") + removeBlacklistRolePermissionPrefix, _ := hex.DecodeString("f5f865e4") + claimValidatorPrefix, _ := hex.DecodeString("00000000") + upsertTokenAliasPrefix, _ := hex.DecodeString("f69a4787") + upsertTokenRatePrefix, _ := hex.DecodeString("3b30a97a") + activatePrefix, _ := hex.DecodeString("a1374dc2") + pausePrefix, _ := hex.DecodeString("1371cf19") + unpausePrefix, _ := hex.DecodeString("b9179894") + createSpendingPoolPrefix, _ := hex.DecodeString("4ed8a0a2") + depositSpendingPoolPrefix, _ := hex.DecodeString("e10c925c") + registerSpendingPoolBeneficiaryPrefix, _ := hex.DecodeString("7ab7eecf") + claimSpendingPoolPrefix, _ := hex.DecodeString("efeed4a0") + upsertStakingPoolPrefix, _ := hex.DecodeString("fb24f5cc") + delegatePrefix, _ := hex.DecodeString("4b193c09") + undelegatePrefix, _ := hex.DecodeString("94574f0c") + claimRewardsPrefix, _ := hex.DecodeString("9838bc2f") + claimUndelegationPrefix, _ := hex.DecodeString("2f608d76") + setCompoundInfoPrefix, _ := hex.DecodeString("e2d6a093") + registerDelegatorPrefix, _ := hex.DecodeString("99db185d") + createCustodyPrefix, _ := hex.DecodeString("bebde6d1") + addToCustodyWhiteListPrefix, _ := hex.DecodeString("25a1d834") + addToCustodyCustodiansPrefix, _ := hex.DecodeString("8c7fdb91") + removeFromCustodyCustodiansPrefix, _ := hex.DecodeString("90be51cf") + dropCustodyCustodiansPrefix, _ := hex.DecodeString("0ca697b4") + removeFromCustodyWhiteListPrefix, _ := hex.DecodeString("fa431c3e") + dropCustodyWhiteListPrefix, _ := hex.DecodeString("bc65010a") + approveCustodyTxPrefix, _ := hex.DecodeString("5da292d4") + declineCustodyTxPrefix, _ := hex.DecodeString("dce4399a") + + var msgType int + switch { + case txData == nil: + msgType = MsgBankSend + case len(txData) == 0: + msgType = MsgBankSend + case bytes.Equal(txData, delegatePrefix): + msgType = MsgDelegate + case bytes.Equal(txData, undelegatePrefix): + msgType = MsgUndelegate + case bytes.Equal(txData, submitEvidencePrefix): + msgType = MsgSubmitEvidence + case bytes.Equal(txData, submitProposalPrefix): + msgType = MsgSubmitProposal + case bytes.Equal(txData, voteProposalPrefix): + msgType = MsgVoteProposal + case bytes.Equal(txData, registerIdentityRecordsPrefix): + msgType = MsgRegisterIdentityRecords + case bytes.Equal(txData, deleteIdentityRecordsPrefix): + msgType = MsgDeleteIdentityRecords + case bytes.Equal(txData, requestIdentityRecordsVerifyPrefix): + msgType = MsgRequestIdentityRecordsVerify + case bytes.Equal(txData, handleIdentityRecordsVerifyRequestPrefix): + msgType = MsgHandleIdentityRecordsVerifyRequest + case bytes.Equal(txData, cancelIdentityRecordsVerifyRequestPrefix): + msgType = MsgCancelIdentityRecordsVerifyRequest + case bytes.Equal(txData, setNetworkPropertiesPrefix): + msgType = MsgSetNetworkProperties + case bytes.Equal(txData, setExecutionFeePrefix): + msgType = MsgSetExecutionFee + case bytes.Equal(txData, claimCouncilorPrefix): + msgType = MsgClaimCouncilor + case bytes.Equal(txData, whitelistPermissionsPrefix): + msgType = MsgWhitelistPermissions + case bytes.Equal(txData, blacklistPermissionsPrefix): + msgType = MsgBlacklistPermissions + case bytes.Equal(txData, createRolePrefix): + msgType = MsgCreateRole + case bytes.Equal(txData, assignRolePrefix): + msgType = MsgAssignRole + case bytes.Equal(txData, unassignRolePrefix): + msgType = MsgUnassignRole + case bytes.Equal(txData, whitelistRolePermissionPrefix): + msgType = MsgWhitelistRolePermission + case bytes.Equal(txData, blacklistRolePermissionPrefix): + msgType = MsgBlacklistRolePermission + case bytes.Equal(txData, removeWhitelistRolePermissionPrefix): + msgType = MsgRemoveWhitelistRolePermission + case bytes.Equal(txData, removeBlacklistRolePermissionPrefix): + msgType = MsgBlacklistRolePermission + case bytes.Equal(txData, claimValidatorPrefix): + msgType = MsgClaimValidator + case bytes.Equal(txData, upsertTokenAliasPrefix): + msgType = MsgUpsertTokenAlias + case bytes.Equal(txData, upsertTokenRatePrefix): + msgType = MsgUpsertTokenRate + case bytes.Equal(txData, activatePrefix): + msgType = MsgActivate + case bytes.Equal(txData, pausePrefix): + msgType = MsgPause + case bytes.Equal(txData, unpausePrefix): + msgType = MsgUnpause + case bytes.Equal(txData, createSpendingPoolPrefix): + msgType = MsgCreateSpendingPool + case bytes.Equal(txData, depositSpendingPoolPrefix): + msgType = MsgDepositSpendingPool + case bytes.Equal(txData, registerSpendingPoolBeneficiaryPrefix): + msgType = MsgRegisterSpendingPoolBeneficiary + case bytes.Equal(txData, claimSpendingPoolPrefix): + msgType = MsgClaimSpendingPool + case bytes.Equal(txData, upsertStakingPoolPrefix): + msgType = MsgUpsertStakingPool + case bytes.Equal(txData, claimRewardsPrefix): + msgType = MsgClaimRewards + case bytes.Equal(txData, claimUndelegationPrefix): + msgType = MsgClaimUndelegation + case bytes.Equal(txData, setCompoundInfoPrefix): + msgType = MsgSetCompoundInfo + case bytes.Equal(txData, registerDelegatorPrefix): + msgType = MsgRegisterDelegator + case bytes.Equal(txData, createCustodyPrefix): + msgType = MsgCreateCustody + case bytes.Equal(txData, addToCustodyWhiteListPrefix): + msgType = MsgAddToCustodyWhiteList + case bytes.Equal(txData, addToCustodyCustodiansPrefix): + msgType = MsgAddToCustodyCustodians + case bytes.Equal(txData, removeFromCustodyCustodiansPrefix): + msgType = MsgRemoveFromCustodyCustodians + case bytes.Equal(txData, dropCustodyCustodiansPrefix): + msgType = MsgDropCustodyCustodians + case bytes.Equal(txData, removeFromCustodyWhiteListPrefix): + msgType = MsgRemoveFromCustodyWhiteList + case bytes.Equal(txData, dropCustodyWhiteListPrefix): + msgType = MsgDropCustodyWhiteList + case bytes.Equal(txData, approveCustodyTxPrefix): + msgType = MsgApproveCustodyTx + case bytes.Equal(txData, declineCustodyTxPrefix): + msgType = MsgDeclineCustodyTx + default: + return 0, errors.New("no such functions") + } + return msgType, nil +} + +func getInstanceOfTx(txType int) cosmostypes.Msg { + switch txType { + case MsgSubmitEvidence: + return &evidencetypes.MsgSubmitEvidence{} + case MsgSubmitProposal: + return &customgovtypes.MsgSubmitProposal{} + case MsgVoteProposal: + return &customgovtypes.MsgVoteProposal{} + case MsgRegisterIdentityRecords: + return &customgovtypes.MsgRegisterIdentityRecords{} + case MsgDeleteIdentityRecords: + return &customgovtypes.MsgDeleteIdentityRecords{} + case MsgRequestIdentityRecordsVerify: + return &customgovtypes.MsgRequestIdentityRecordsVerify{} + case MsgHandleIdentityRecordsVerifyRequest: + return &customgovtypes.MsgHandleIdentityRecordsVerifyRequest{} + case MsgCancelIdentityRecordsVerifyRequest: + return &customgovtypes.MsgCancelIdentityRecordsVerifyRequest{} + case MsgSetNetworkProperties: + return &customgovtypes.MsgSetNetworkProperties{} + case MsgSetExecutionFee: + return &customgovtypes.MsgSetExecutionFee{} + case MsgClaimCouncilor: + return &customgovtypes.MsgClaimCouncilor{} + case MsgWhitelistPermissions: + return &customgovtypes.MsgWhitelistPermissions{} + case MsgBlacklistPermissions: + return &customgovtypes.MsgBlacklistPermissions{} + case MsgCreateRole: + return &customgovtypes.MsgCreateRole{} + case MsgAssignRole: + return &customgovtypes.MsgAssignRole{} + case MsgUnassignRole: + return &customgovtypes.MsgUnassignRole{} + case MsgWhitelistRolePermission: + return &customgovtypes.MsgWhitelistRolePermission{} + case MsgBlacklistRolePermission: + return &customgovtypes.MsgBlacklistRolePermission{} + case MsgRemoveWhitelistRolePermission: + return &customgovtypes.MsgRemoveWhitelistRolePermission{} + case MsgRemoveBlacklistRolePermission: + return &customgovtypes.MsgRemoveBlacklistRolePermission{} + case MsgClaimValidator: + return &customstakingtypes.MsgClaimValidator{} + case MsgUpsertTokenAlias: + return &tokenstypes.MsgUpsertTokenAlias{} + case MsgUpsertTokenRate: + return &tokenstypes.MsgUpsertTokenRate{} + case MsgActivate: + return &customslashingtypes.MsgActivate{} + case MsgPause: + return &customslashingtypes.MsgPause{} + case MsgUnpause: + return &customslashingtypes.MsgUnpause{} + case MsgCreateSpendingPool: + return &spendingtypes.MsgCreateSpendingPool{} + case MsgDepositSpendingPool: + return &spendingtypes.MsgDepositSpendingPool{} + case MsgRegisterSpendingPoolBeneficiary: + return &spendingtypes.MsgRegisterSpendingPoolBeneficiary{} + case MsgClaimSpendingPool: + return &spendingtypes.MsgClaimSpendingPool{} + case MsgUpsertStakingPool: + return &multistakingtypes.MsgUpsertStakingPool{} + case MsgDelegate: + return &multistakingtypes.MsgDelegate{} + case MsgUndelegate: + return &multistakingtypes.MsgUndelegate{} + case MsgClaimRewards: + return &multistakingtypes.MsgClaimRewards{} + case MsgClaimUndelegation: + return &multistakingtypes.MsgClaimUndelegation{} + case MsgSetCompoundInfo: + return &multistakingtypes.MsgSetCompoundInfo{} + case MsgRegisterDelegator: + return &multistakingtypes.MsgRegisterDelegator{} + case MsgCreateCustody: + return &custodytypes.MsgCreateCustodyRecord{} + case MsgAddToCustodyWhiteList: + return &custodytypes.MsgAddToCustodyWhiteList{} + case MsgAddToCustodyCustodians: + return &custodytypes.MsgAddToCustodyCustodians{} + case MsgRemoveFromCustodyCustodians: + return &custodytypes.MsgRemoveFromCustodyCustodians{} + case MsgDropCustodyCustodians: + return &custodytypes.MsgDropCustodyCustodians{} + case MsgRemoveFromCustodyWhiteList: + return &custodytypes.MsgRemoveFromCustodyWhiteList{} + case MsgDropCustodyWhiteList: + return &custodytypes.MsgDropCustodyWhiteList{} + case MsgApproveCustodyTx: + return &custodytypes.MsgApproveCustodyTransaction{} + case MsgDeclineCustodyTx: + return &custodytypes.MsgDeclineCustodyTransaction{} + default: + return nil + } +} + +func SignTx(ethTxData EthTxData, ethTxBytes []byte, gwCosmosmux *runtime.ServeMux, r *http.Request) ([]byte, error) { + // Create a new TxBuilder. + txBuilder := config.EncodingCg.TxConfig.NewTxBuilder() + + addr1, err := hex2bech32(ethTxData.From, TypeKiraAddr) + if err != nil { + return nil, err + } + + var msg cosmostypes.Msg = &tokenstypes.MsgEthereumTx{ + TxType: "NativeSend", + Sender: addr1, + Hash: ethTxData.Hash, + Data: ethTxBytes, + } + + var signature []byte + if len(ethTxData.Data) >= 4 { + txType, err := getTxType(ethTxData.Data[:4]) + if err != nil { + return nil, err + } + params, err := DecodeParam(ethTxData.Data[4:], txType) + if err != nil { + return nil, err + } + if len(params) < 5 { + return nil, errors.New("insufficient number of params") + } + + v, r, s := params[0][len(params[0])-1:], params[1], params[2] + signature = getSignatureV2(r, s, v) + + msg = getInstanceOfTx(txType) + if msg == nil { + return nil, fmt.Errorf("unrecognized transaction type: %d", txType) + } + err = json.Unmarshal(params[4], &msg) + if err != nil { + return nil, err + } + } + + // fmt.Println(msg) + err = txBuilder.SetMsgs(msg) + if err != nil { + return nil, err + } + txBuilder.SetGasLimit(ethTxData.GasLimit) + // TODO: set fee amount - how can I get the fee amount from eth tx? or fix this? + txBuilder.SetFeeAmount(cosmostypes.NewCoins(cosmostypes.NewInt64Coin(config.DefaultKiraDenom, 200))) + // txBuilder.SetMemo() + // txBuilder.SetTimeoutHeight() + // txHash, err := sendTx(context.Background(), byteData) + + privs := []cryptotypes.PrivKey{config.Config.PrivKey} + accSeqs := []uint64{ethTxData.Nonce} // The accounts' sequence numbers + + // First round: gather all the signer infos + var sigsV2 []signing.SignatureV2 + for i, priv := range privs { + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: config.EncodingCg.TxConfig.SignModeHandler().DefaultMode(), + Signature: signature, + }, + Sequence: accSeqs[i], + } + + sigsV2 = append(sigsV2, sigV2) + } + + err = txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + txBytes, err := config.EncodingCg.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, err + } + + return txBytes, err +} + +func sendCosmosTx(ctx context.Context, txBytes []byte) (string, error) { + // --snip-- + + // Create a connection to the gRPC server. + if grpcConn == nil { + var err error + + grpcConn, err = grpc.Dial( + "127.0.0.1:9090", // Or your gRPC server address. + grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism. + ) + + if err != nil { + return "", err + } + + // defer grpcConn.Close() + } + + // Broadcast the tx via gRPC. We create a new client for the Protobuf Tx + // service. + txClient := tx.NewServiceClient(grpcConn) + // We then call the BroadcastTx method on this client. + grpcRes, err := txClient.BroadcastTx( + ctx, + &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, // Proto-binary of the signed tx, see previous step. + }, + ) + + if err != nil { + return "", err + } + + // fmt.Println(grpcRes.TxResponse) + + if grpcRes.TxResponse.Code != 0 { + return "", errors.New(fmt.Sprintln("send tx failed - result code: ", grpcRes.TxResponse.Code, grpcRes.TxResponse.RawLog)) + } + + return grpcRes.TxResponse.TxHash, nil +} diff --git a/gateway/kira/metamask/ethtxgenerator.go b/gateway/kira/metamask/ethtxgenerator.go new file mode 100644 index 0000000..1204d7d --- /dev/null +++ b/gateway/kira/metamask/ethtxgenerator.go @@ -0,0 +1,112 @@ +package metamask + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// LogsBloom is a struct that represents a logsBloom field +type LogsBloom struct { + m int // size of the bit array + bits []bool // bit array +} + +// NewLogsBloom creates a new logsBloom field with the given size +func NewLogsBloom(m int) *LogsBloom { + return &LogsBloom{ + m: m, + bits: make([]bool, m), + } +} + +// hash is a helper function that hashes a byte slice using keccak256 algorithm +func hash(b []byte) []byte { + return crypto.Keccak256(b) +} + +// Add adds an element to the logsBloom field +func (lb *LogsBloom) Add(b []byte) { + h := hash(b) + for i := 0; i < 3; i++ { + // extract 11-bit segments from the first, third, and fifth 16-bit words of the hash + // https://github.com/ethereum/wiki/wiki/Design-Rationale#bloom-filter + seg := new(big.Int).SetBytes(h[i*2 : i*2+2]) + seg.Rsh(seg, uint(4-i)) + seg.And(seg, big.NewInt(2047)) + lb.bits[seg.Int64()] = true + } +} + +// Contains checks if an element is in the logsBloom field +func (lb *LogsBloom) Contains(b []byte) bool { + h := hash(b) + for i := 0; i < 3; i++ { + seg := new(big.Int).SetBytes(h[i*2 : i*2+2]) + seg.Rsh(seg, uint(4-i)) + seg.And(seg, big.NewInt(2047)) + if !lb.bits[seg.Int64()] { + return false + } + } + return true +} + +// Or performs a bitwise OR operation with another logsBloom field +func (lb *LogsBloom) Or(other *LogsBloom) { + for i := 0; i < lb.m; i++ { + lb.bits[i] = lb.bits[i] || other.bits[i] + } +} + +// CreateLogsBloomFromLogs creates a logsBloom field from a slice of ethereum tx logs +func CreateLogsBloomFromLogs(logs []*types.Log, m int) *LogsBloom { + lb := NewLogsBloom(m) + for _, log := range logs { + // add the topics and data to the logsBloom field + for _, topic := range log.Topics { + lb.Add(topic.Bytes()) + } + lb.Add(log.Data) + } + return lb +} + +func boolsToBytes(bools []bool) []byte { + bi := new(big.Int) // create a new big.Int + for i, b := range bools { + if b { + bi.SetBit(bi, i, 1) // set the ith bit to 1 + } + } + return bi.Bytes() // return the byte representation +} + +// func convertCosmosLog2EvmLog(logInfos []LogInfo) []types.Log { + +// var logs []types.Log + +// types.LegacyTx + +// for _, logInfo := range logInfos { +// log := &types.Log{ +// Address +// Topics: []common.Hash{ +// common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), +// common.HexToHash("0x000000000000000000000000" + bech322hex(logInfo.Events[0].Attributes[1])), +// common.HexToHash("0x000000000000000000000000"), +// }, +// Data: common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), +// } + +// logs = append(logs, *log) +// } +// } + +func GetLogsBloom(logs []*types.Log) []byte { + // create a logsBloom field from the log with 2048 bits + lb := CreateLogsBloomFromLogs(logs, 2048) + + return boolsToBytes(lb.bits) +} diff --git a/gateway/kira/metamask/ethtxinfo.go b/gateway/kira/metamask/ethtxinfo.go new file mode 100644 index 0000000..fdecb00 --- /dev/null +++ b/gateway/kira/metamask/ethtxinfo.go @@ -0,0 +1,90 @@ +package metamask + +import ( + "errors" + "log" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +type EthTxData struct { + Nonce uint64 `json:"nonce"` + GasPrice big.Int `json:"gas_price"` + GasLimit uint64 `json:"gas_limit"` + From string `json:"from"` + To string `json:"to"` + Value big.Int `json:"value"` + Data []byte `json:"data"` + Hash string `json:"hash"` + V big.Int `json:"signature_v"` + R big.Int `json:"signature_r"` + S big.Int `json:"signature_s"` +} + +func GetSenderAddrFromRawTxBytes(rawTxBytes []byte) (common.Address, error) { + var rawTx ethtypes.Transaction + if err := rlp.DecodeBytes(rawTxBytes, &rawTx); err != nil { + return common.Address{}, err + } + + signer := ethtypes.NewEIP155Signer(rawTx.ChainId()) + sender, err := signer.Sender(&rawTx) + if err != nil { + return common.Address{}, err + } + return sender, nil +} + +func validateTx(rawTxBytes []byte, sender common.Address) bool { + senderFromTx, err := GetSenderAddrFromRawTxBytes(rawTxBytes) + if err != nil { + return false + } + + if senderFromTx.Hex() == sender.Hex() { + return true + } + return false +} + +// SendRawTransaction send a raw Ethereum transaction. +func GetEthTxInfo(data hexutil.Bytes) (EthTxData, error) { + tx := new(ethtypes.Transaction) + rlp.DecodeBytes(data, &tx) + + msg, err := tx.AsMessage(ethtypes.NewEIP155Signer(tx.ChainId()), big.NewInt(1)) + if err != nil { + log.Fatal(err) + return EthTxData{}, errors.New("decoding transaction data is failed") + } + + v, r, s := tx.RawSignatureValues() + + // check the local node config in case unprotected txs are disabled + if !tx.Protected() { + // Ensure only eip155 signed transactions are submitted if EIP155Required is set. + return EthTxData{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC") + } + + if !validateTx(data, msg.From()) { + return EthTxData{}, errors.New("validation is failed") + } + + return EthTxData{ + Nonce: tx.Nonce(), + GasPrice: *tx.GasPrice(), + GasLimit: tx.Gas(), + From: msg.From().String(), + To: msg.To().String(), + Value: *tx.Value(), + Data: tx.Data(), + Hash: tx.Hash().Hex(), + V: *v, + R: *r, + S: *s, + }, nil +} diff --git a/gateway/kira/metamask/legacy_cosmostxsender.go b/gateway/kira/metamask/legacy_cosmostxsender.go new file mode 100644 index 0000000..513b674 --- /dev/null +++ b/gateway/kira/metamask/legacy_cosmostxsender.go @@ -0,0 +1,1585 @@ +package metamask + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math" + "math/big" + "net/http" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/KiraCore/interx/common" + "github.com/KiraCore/interx/config" + custodytypes "github.com/KiraCore/sekai/x/custody/types" + evidencetypes "github.com/KiraCore/sekai/x/evidence/types" + customgovtypes "github.com/KiraCore/sekai/x/gov/types" + multistakingtypes "github.com/KiraCore/sekai/x/multistaking/types" + slashingtypes "github.com/KiraCore/sekai/x/slashing/types" + spendingtypes "github.com/KiraCore/sekai/x/spending/types" + tokenstypes "github.com/KiraCore/sekai/x/tokens/types" + clienttx "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/accounts/abi" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +func legacySendTx(txRawData string, gwCosmosmux *runtime.ServeMux, r *http.Request) (string, error) { + byteData, err := hex.DecodeString(txRawData[2:]) + if err != nil { + return "", err + } + + ethTxData, err := GetEthTxInfo(byteData) + + if err != nil { + return "", err + } + + var txBytes []byte + + submitEvidencePrefix, _ := hex.DecodeString("85db2453") + submitProposalPrefix, _ := hex.DecodeString("00000000") + voteProposalPrefix, _ := hex.DecodeString("7f1f06dc") + registerIdentityRecordsPrefix, _ := hex.DecodeString("bc05f106") + deleteIdentityRecordPrefix, _ := hex.DecodeString("25581f17") + requestIdentityRecordsVerifyPrefix, _ := hex.DecodeString("9765358e") + handleIdentityRecordsVerifyRequestPrefix, _ := hex.DecodeString("4335ed0c") + cancelIdentityRecordsVerifyRequestPrefix, _ := hex.DecodeString("eeaa0488") + setNetworkPropertiesPrefix, _ := hex.DecodeString("f8060fb5") + setExecutionFeePrefix, _ := hex.DecodeString("c7586de8") + claimCouncilorPrefix, _ := hex.DecodeString("b7b8ff46") + whitelistPermissionsPrefix, _ := hex.DecodeString("2f313ab8") + blacklistPermissionsPrefix, _ := hex.DecodeString("3864f845") + createRolePrefix, _ := hex.DecodeString("2d8abfdf") + assignRolePrefix, _ := hex.DecodeString("fcc121b5") + unassignRolePrefix, _ := hex.DecodeString("c79ca19d") + whitelistRolePermissionPrefix, _ := hex.DecodeString("59472362") + blacklistRolePermissionPrefix, _ := hex.DecodeString("99c557da") + removeWhitelistRolePermissionPrefix, _ := hex.DecodeString("2a11d702") + removeBlacklistRolePermissionPrefix, _ := hex.DecodeString("f5f865e4") + claimValidatorPrefix, _ := hex.DecodeString("00000000") + upsertTokenAliasPrefix, _ := hex.DecodeString("f69a4787") + upsertTokenRatePrefix, _ := hex.DecodeString("3b30a97a") + activatePrefix, _ := hex.DecodeString("a1374dc2") + pausePrefix, _ := hex.DecodeString("1371cf19") + unpausePrefix, _ := hex.DecodeString("b9179894") + createSpendingPoolPrefix, _ := hex.DecodeString("4ed8a0a2") + depositSpendingPoolPrefix, _ := hex.DecodeString("e10c925c") + registerSpendingPoolBeneficiaryPrefix, _ := hex.DecodeString("7ab7eecf") + claimSpendingPoolPrefix, _ := hex.DecodeString("efeed4a0") + upsertStakingPoolPrefix, _ := hex.DecodeString("fb24f5cc") + delegatePrefix, _ := hex.DecodeString("4b193c09") + undelegatePrefix, _ := hex.DecodeString("94574f0c") + claimRewardsPrefix, _ := hex.DecodeString("9e796524") + claimUndelegationPrefix, _ := hex.DecodeString("2f608d76") + setCompoundInfoPrefix, _ := hex.DecodeString("e2d6a093") + registerDelegatorPrefix, _ := hex.DecodeString("99db185d") + createCustodyPrefix, _ := hex.DecodeString("bebde6d1") + addToCustodyWhiteListPrefix, _ := hex.DecodeString("25a1d834") + addToCustodyCustodiansPrefix, _ := hex.DecodeString("8c7fdb91") + removeFromCustodyCustodiansPrefix, _ := hex.DecodeString("90be51cf") + dropCustodyCustodiansPrefix, _ := hex.DecodeString("0ca697b4") + removeFromCustodyWhiteListPrefix, _ := hex.DecodeString("fa431c3e") + dropCustodyWhiteListPrefix, _ := hex.DecodeString("bc65010a") + approveCustodyTxPrefix, _ := hex.DecodeString("5da292d4") + declineCustodyTxPrefix, _ := hex.DecodeString("dce4399a") + + var msgType int + switch { + case ethTxData.Data == nil: + msgType = MsgBankSend + case len(ethTxData.Data) == 0: + msgType = MsgBankSend + case bytes.Equal(ethTxData.Data[:4], delegatePrefix): + msgType = MsgDelegate + case bytes.Equal(ethTxData.Data[:4], undelegatePrefix): + msgType = MsgUndelegate + case bytes.Equal(ethTxData.Data[:4], submitEvidencePrefix): + msgType = MsgSubmitEvidence + case bytes.Equal(ethTxData.Data[:4], submitProposalPrefix): + msgType = MsgSubmitProposal + case bytes.Equal(ethTxData.Data[:4], voteProposalPrefix): + msgType = MsgVoteProposal + case bytes.Equal(ethTxData.Data[:4], registerIdentityRecordsPrefix): + msgType = MsgRegisterIdentityRecords + case bytes.Equal(ethTxData.Data[:4], deleteIdentityRecordPrefix): + msgType = MsgDeleteIdentityRecords + case bytes.Equal(ethTxData.Data[:4], requestIdentityRecordsVerifyPrefix): + msgType = MsgRequestIdentityRecordsVerify + case bytes.Equal(ethTxData.Data[:4], handleIdentityRecordsVerifyRequestPrefix): + msgType = MsgHandleIdentityRecordsVerifyRequest + case bytes.Equal(ethTxData.Data[:4], cancelIdentityRecordsVerifyRequestPrefix): + msgType = MsgCancelIdentityRecordsVerifyRequest + case bytes.Equal(ethTxData.Data[:4], setNetworkPropertiesPrefix): + msgType = MsgSetNetworkProperties + case bytes.Equal(ethTxData.Data[:4], setExecutionFeePrefix): + msgType = MsgSetExecutionFee + case bytes.Equal(ethTxData.Data[:4], claimCouncilorPrefix): + msgType = MsgClaimCouncilor + case bytes.Equal(ethTxData.Data[:4], whitelistPermissionsPrefix): + msgType = MsgWhitelistPermissions + case bytes.Equal(ethTxData.Data[:4], blacklistPermissionsPrefix): + msgType = MsgBlacklistPermissions + case bytes.Equal(ethTxData.Data[:4], createRolePrefix): + msgType = MsgCreateRole + case bytes.Equal(ethTxData.Data[:4], assignRolePrefix): + msgType = MsgAssignRole + case bytes.Equal(ethTxData.Data[:4], unassignRolePrefix): + msgType = MsgUnassignRole + case bytes.Equal(ethTxData.Data[:4], whitelistRolePermissionPrefix): + msgType = MsgWhitelistRolePermission + case bytes.Equal(ethTxData.Data[:4], blacklistRolePermissionPrefix): + msgType = MsgBlacklistRolePermission + case bytes.Equal(ethTxData.Data[:4], removeWhitelistRolePermissionPrefix): + msgType = MsgRemoveWhitelistRolePermission + case bytes.Equal(ethTxData.Data[:4], removeBlacklistRolePermissionPrefix): + msgType = MsgBlacklistRolePermission + case bytes.Equal(ethTxData.Data[:4], claimValidatorPrefix): + msgType = MsgClaimValidator + case bytes.Equal(ethTxData.Data[:4], upsertTokenAliasPrefix): + msgType = MsgUpsertTokenAlias + case bytes.Equal(ethTxData.Data[:4], upsertTokenRatePrefix): + msgType = MsgUpsertTokenRate + case bytes.Equal(ethTxData.Data[:4], activatePrefix): + msgType = MsgActivate + case bytes.Equal(ethTxData.Data[:4], pausePrefix): + msgType = MsgPause + case bytes.Equal(ethTxData.Data[:4], unpausePrefix): + msgType = MsgUnpause + case bytes.Equal(ethTxData.Data[:4], createSpendingPoolPrefix): + msgType = MsgCreateSpendingPool + case bytes.Equal(ethTxData.Data[:4], depositSpendingPoolPrefix): + msgType = MsgDepositSpendingPool + case bytes.Equal(ethTxData.Data[:4], registerSpendingPoolBeneficiaryPrefix): + msgType = MsgRegisterSpendingPoolBeneficiary + case bytes.Equal(ethTxData.Data[:4], claimSpendingPoolPrefix): + msgType = MsgClaimSpendingPool + case bytes.Equal(ethTxData.Data[:4], upsertStakingPoolPrefix): + msgType = MsgUpsertStakingPool + case bytes.Equal(ethTxData.Data[:4], claimRewardsPrefix): + msgType = MsgClaimRewards + case bytes.Equal(ethTxData.Data[:4], claimUndelegationPrefix): + msgType = MsgClaimUndelegation + case bytes.Equal(ethTxData.Data[:4], setCompoundInfoPrefix): + msgType = MsgSetCompoundInfo + case bytes.Equal(ethTxData.Data[:4], registerDelegatorPrefix): + msgType = MsgRegisterDelegator + case bytes.Equal(ethTxData.Data[:4], createCustodyPrefix): + msgType = MsgCreateCustody + case bytes.Equal(ethTxData.Data[:4], addToCustodyWhiteListPrefix): + msgType = MsgAddToCustodyWhiteList + case bytes.Equal(ethTxData.Data[:4], addToCustodyCustodiansPrefix): + msgType = MsgAddToCustodyCustodians + case bytes.Equal(ethTxData.Data[:4], removeFromCustodyCustodiansPrefix): + msgType = MsgRemoveFromCustodyCustodians + case bytes.Equal(ethTxData.Data[:4], dropCustodyCustodiansPrefix): + msgType = MsgDropCustodyCustodians + case bytes.Equal(ethTxData.Data[:4], removeFromCustodyWhiteListPrefix): + msgType = MsgRemoveFromCustodyWhiteList + case bytes.Equal(ethTxData.Data[:4], dropCustodyWhiteListPrefix): + msgType = MsgDropCustodyWhiteList + case bytes.Equal(ethTxData.Data[:4], approveCustodyTxPrefix): + msgType = MsgApproveCustodyTx + case bytes.Equal(ethTxData.Data[:4], declineCustodyTxPrefix): + msgType = MsgDeclineCustodyTx + default: + return "", errors.New("no such functions") + } + + txBytes, err = legacySignTx(ethTxData, gwCosmosmux, r, msgType) + + if err != nil { + return "", err + } + + // if ethTxData + + txHash, err := sendCosmosTx(r.Context(), txBytes) + if err != nil { + return "", err + } + + return txHash, nil +} + +func legacyGetStructHash(txType int, valParam string) ethcommon.Hash { + abiPack := abi.ABI{} + var structABIPack []byte + + var funcSig []byte + + switch txType { + case MsgSubmitEvidence: + funcSig = crypto.Keccak256([]byte("submitEvidence(string param)")) + case MsgSubmitProposal: + funcSig = crypto.Keccak256([]byte("submitProposal(string param)")) + case MsgVoteProposal: + funcSig = crypto.Keccak256([]byte("voteProposal(string param)")) + case MsgRegisterIdentityRecords: + funcSig = crypto.Keccak256([]byte("registerIdentityRecords(string param)")) + case MsgDeleteIdentityRecords: + funcSig = crypto.Keccak256([]byte("deleteIdentityRecord(string param)")) + case MsgRequestIdentityRecordsVerify: + funcSig = crypto.Keccak256([]byte("requestIdentityRecordsVerify(string param)")) + case MsgHandleIdentityRecordsVerifyRequest: + funcSig = crypto.Keccak256([]byte("handleIdentityRecordsVerifyRequest(string param)")) + case MsgCancelIdentityRecordsVerifyRequest: + funcSig = crypto.Keccak256([]byte("cancelIdentityRecordsVerifyRequest(string param)")) + case MsgSetNetworkProperties: + funcSig = crypto.Keccak256([]byte("setNetworkProperties(string param)")) + case MsgSetExecutionFee: + funcSig = crypto.Keccak256([]byte("setExecutionFee(string param)")) + case MsgClaimCouncilor: + funcSig = crypto.Keccak256([]byte("claimCouncilor(string param)")) + case MsgWhitelistPermissions: + funcSig = crypto.Keccak256([]byte("whitelistPermissions(string param)")) + case MsgBlacklistPermissions: + funcSig = crypto.Keccak256([]byte("blacklistPermissions(string param)")) + case MsgCreateRole: + funcSig = crypto.Keccak256([]byte("createRole(string param)")) + case MsgAssignRole: + funcSig = crypto.Keccak256([]byte("assignRole(string param)")) + case MsgUnassignRole: + funcSig = crypto.Keccak256([]byte("unassignRole(string param)")) + case MsgWhitelistRolePermission: + funcSig = crypto.Keccak256([]byte("whitelistRolePermission(string param)")) + case MsgBlacklistRolePermission: + funcSig = crypto.Keccak256([]byte("blacklistRolePermission(string param)")) + case MsgRemoveWhitelistRolePermission: + funcSig = crypto.Keccak256([]byte("removeWhitelistRolePermission(string param)")) + case MsgRemoveBlacklistRolePermission: + funcSig = crypto.Keccak256([]byte("removeBlacklistRolePermission(string param)")) + case MsgClaimValidator: + // funcSig = crypto.Keccak256([]byte("delegate(string param)")) + case MsgUpsertTokenAlias: + funcSig = crypto.Keccak256([]byte("upsertTokenAlias(string param)")) + case MsgUpsertTokenRate: + funcSig = crypto.Keccak256([]byte("upsertTokenRate(string param)")) + case MsgActivate: + funcSig = crypto.Keccak256([]byte("activate()")) + case MsgPause: + funcSig = crypto.Keccak256([]byte("pause()")) + case MsgUnpause: + funcSig = crypto.Keccak256([]byte("unpause()")) + case MsgCreateSpendingPool: + funcSig = crypto.Keccak256([]byte("createSpendingPool(string param)")) + case MsgDepositSpendingPool: + funcSig = crypto.Keccak256([]byte("depositSpendingPool(string param)")) + case MsgRegisterSpendingPoolBeneficiary: + funcSig = crypto.Keccak256([]byte("registerSpendingPoolBeneficiary(string param)")) + case MsgClaimSpendingPool: + funcSig = crypto.Keccak256([]byte("claimSpendingPool(string param)")) + case MsgUpsertStakingPool: + funcSig = crypto.Keccak256([]byte("upsertStakingPool(string param)")) + case MsgDelegate: + funcSig = crypto.Keccak256([]byte("delegate(string param)")) + case MsgUndelegate: + funcSig = crypto.Keccak256([]byte("undelegate(string param)")) + case MsgClaimRewards: + funcSig = crypto.Keccak256([]byte("claimRewards()")) + case MsgClaimUndelegation: + funcSig = crypto.Keccak256([]byte("claimUndelegation(string param)")) + case MsgSetCompoundInfo: + funcSig = crypto.Keccak256([]byte("setCompoundInfo(string param)")) + case MsgRegisterDelegator: + funcSig = crypto.Keccak256([]byte("registerDelegator()")) + case MsgCreateCustody: + funcSig = crypto.Keccak256([]byte("createCustody(string param)")) + case MsgAddToCustodyWhiteList: + funcSig = crypto.Keccak256([]byte("addToCustodyWhiteList(string param)")) + case MsgAddToCustodyCustodians: + funcSig = crypto.Keccak256([]byte("addToCustodyCustodians(string param)")) + case MsgRemoveFromCustodyCustodians: + funcSig = crypto.Keccak256([]byte("removeFromCustodyCustodians(string param)")) + case MsgDropCustodyCustodians: + funcSig = crypto.Keccak256([]byte("dropCustodyCustodians(string param)")) + case MsgRemoveFromCustodyWhiteList: + funcSig = crypto.Keccak256([]byte("removeFromCustodyWhiteList(string param)")) + case MsgDropCustodyWhiteList: + funcSig = crypto.Keccak256([]byte("dropCustodyWhiteList(string param)")) + case MsgApproveCustodyTx: + funcSig = crypto.Keccak256([]byte("approveCustodyTransaction(string param)")) + case MsgDeclineCustodyTx: + funcSig = crypto.Keccak256([]byte("declineCustodyTransaction(string param)")) + default: + } + + structJsonData := []byte(`[{"Type":"function","Name":"encode","Inputs":[{"Type":"bytes32","Name":"funcsig"},{"Type":"bytes32","Name":"param"}],"Outputs":[]}]`) + _ = abiPack.UnmarshalJSON(structJsonData) + + var funcSignature [32]byte + copy(funcSignature[:], funcSig) + + structABIPack, _ = PackABIParams(abiPack, + "encode", + convertByteArr2Bytes32(funcSig), + convertByteArr2Bytes32(crypto.Keccak256([]byte(valParam))), + ) + + structHash := crypto.Keccak256Hash(structABIPack) + + return structHash +} + +func legacyValidateEIP712Sign(v, r, s []byte, sender ethcommon.Address, valParam string, txType int) bool { + abiPack := abi.ABI{} + + // get EIP712DomainHash + jsonData := []byte(`[{"Type":"function","Name":"encode","Inputs":[{"Type":"bytes32","Name":"funcsig"},{"Type":"bytes32","Name":"name"},{"Type":"bytes32","Name":"version"},{"Type":"bytes32","Name":"chainId"}],"Outputs":[]}]`) + abiPack.UnmarshalJSON(jsonData) + funcSig := crypto.Keccak256([]byte("EIP712Domain(string name,string version,uint256 chainId)")) + + eip712DomainSeparatorABIPack, _ := PackABIParams(abiPack, + "encode", + convertByteArr2Bytes32(funcSig), + convertByteArr2Bytes32(crypto.Keccak256([]byte("Kira"))), + convertByteArr2Bytes32(crypto.Keccak256([]byte("1"))), + convertByteArr2Bytes32(uint32To32Bytes(config.DefaultChainID)), + ) + eip712DomainSeparator := crypto.Keccak256Hash(eip712DomainSeparatorABIPack) + + // get StructHash + structHash := legacyGetStructHash(txType, valParam) + + // Define the final hash + hash := crypto.Keccak256Hash( + append(append([]byte("\x19\x01"), eip712DomainSeparator.Bytes()...), structHash.Bytes()...), + ) + + signature := getSignature(r, s, v) + + // Recover the public key from the signature + pubKey, err := crypto.SigToPub(hash.Bytes(), signature) + // pbBytes, err := crypto.Ecrecover(hash.Bytes(), signature) + // fmt.Println(string(pbBytes), err) + // pubKey, err := crypto.UnmarshalPubkey(pbBytes) + + if err != nil { + fmt.Println("eip712 err", err) + return false + } + + // Derive the signer's address from the public key + signerAddress := crypto.PubkeyToAddress(*pubKey) + + return signerAddress.Hex() == sender.Hex() +} + +func legacySignTx(ethTxData EthTxData, gwCosmosmux *runtime.ServeMux, r *http.Request, txType int) ([]byte, error) { + // Create a new TxBuilder. + txBuilder := config.EncodingCg.TxConfig.NewTxBuilder() + + addr1, err := hex2bech32(ethTxData.From, TypeKiraAddr) + if err != nil { + return nil, err + } + cosmosAddr1, err := cosmostypes.AccAddressFromBech32(addr1) + if err != nil { + return nil, err + } + + params := [][]byte(nil) + if txType != MsgBankSend { + params, err = DecodeParam(ethTxData.Data[4:], txType) + if err != nil { + return nil, err + } + valParam := string(params[4]) + + validation := legacyValidateEIP712Sign(params[0][len(params[0])-1:], params[1], params[2], ethcommon.BytesToAddress(params[3][12:]), valParam, txType) + if !validation { + return nil, errors.New("eip712 validation is failed") + } + } + + var msg cosmostypes.Msg + + switch txType { + case MsgBankSend: + addr2, err := hex2bech32(ethTxData.To, TypeKiraAddr) + if err != nil { + return nil, err + } + cosmosAddr2, err := cosmostypes.AccAddressFromBech32(addr2) + if err != nil { + return nil, err + } + + balance := ethTxData.Value.Div(ðTxData.Value, big.NewInt(int64(math.Pow10(12)))) + msg = banktypes.NewMsgSend(cosmosAddr1, cosmosAddr2, + cosmostypes.NewCoins(cosmostypes.NewInt64Coin(config.DefaultKiraDenom, balance.Int64()))) + case MsgSubmitEvidence: + // V, R, S, signer, height, power, time, consensusAddr + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type Equivocation struct { + Height int64 `json:"height"` + Time time.Time `json:"time"` + Power int64 `json:"power"` + ConsensusAddress string `json:"consensus_address"` + } + + var evidenceJsonParam Equivocation + err = json.Unmarshal(params[4], &evidenceJsonParam) + if err != nil { + return nil, err + } + + evidenceParam := evidencetypes.Equivocation{ + Height: evidenceJsonParam.Height, + Time: evidenceJsonParam.Time, + Power: evidenceJsonParam.Power, + ConsensusAddress: evidenceJsonParam.ConsensusAddress, + } + + msg, err = evidencetypes.NewMsgSubmitEvidence(from, &evidenceParam) + if err != nil { + return nil, err + } + case MsgSubmitProposal: + // from, err := bytes2cosmosAddr(params[3][12:]) + // if err != nil { + // return nil, err + // } + + // type SubmitProposalParam struct { + // Title string `json:"title"` + // Description string `json:"description"` + // Content string `json:"content"` + // } + + // var proposalParam SubmitProposalParam + // err = json.Unmarshal(params[4], &proposalParam) + // if err != nil { + // return nil, err + // } + + // msg, err := customgovtypes.NewMsgSubmitProposal(from) + // if err != nil { + // return nil, err + // } + case MsgVoteProposal: + // V, R, S, signer, param + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type VoteProposalParam struct { + ProposalID uint64 `json:"proposal_id"` + Option uint64 `json:"option"` + Slash string `json:"slash"` + } + + var proposalParam VoteProposalParam + err = json.Unmarshal(params[4], &proposalParam) + if err != nil { + return nil, err + } + + slash, err := sdkmath.LegacyNewDecFromStr(proposalParam.Slash) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgVoteProposal(proposalParam.ProposalID, from, customgovtypes.VoteOption(proposalParam.Option), slash) + case MsgRegisterIdentityRecords: + // V, R, S, signer, identityInfo, + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type IdentityRecordParam struct { + Infos []customgovtypes.IdentityInfoEntry `json:"identity_infos"` + } + + var recordParam IdentityRecordParam + err = json.Unmarshal(params[4], &recordParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgRegisterIdentityRecords(from, recordParam.Infos) + case MsgDeleteIdentityRecords: + // V, R, S, signer, len, keys, + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type IdentityRecordParam struct { + Keys []string `json:"keys"` + } + + var recordParam IdentityRecordParam + err = json.Unmarshal(params[4], &recordParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgDeleteIdentityRecords(from, recordParam.Keys) + case MsgRequestIdentityRecordsVerify: + // V, R, S, signer, tip, verifier, len, recordIds + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type IdentityRecordParam struct { + Balance string `json:"balance"` + Verifier string `json:"verifier"` + RecordIds []uint64 `json:"record_ids"` + } + + var recordParam IdentityRecordParam + err = json.Unmarshal(params[4], &recordParam) + if err != nil { + return nil, err + } + + balance, err := cosmostypes.ParseCoinNormalized(recordParam.Balance) + if err != nil { + return nil, err + } + + verifier, err := string2cosmosAddr(recordParam.Verifier) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgRequestIdentityRecordsVerify(from, verifier, recordParam.RecordIds, balance) + case MsgHandleIdentityRecordsVerifyRequest: + // V, R, S, signer, requestId, isApprove + verifier, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type IdentityRecordParam struct { + RequestID uint64 `json:"request_id"` + IsApproved bool `json:"is_approved"` + } + + var recordParam IdentityRecordParam + err = json.Unmarshal(params[4], &recordParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgHandleIdentityRecordsVerifyRequest(verifier, recordParam.RequestID, recordParam.IsApproved) + case MsgCancelIdentityRecordsVerifyRequest: + // V, R, S, signer, verifyRequestId + executor, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type IdentityRecordParam struct { + VerifyRequestId uint64 `json:"verify_request_id"` + } + + var recordParam IdentityRecordParam + err = json.Unmarshal(params[4], &recordParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgCancelIdentityRecordsVerifyRequest(executor, recordParam.VerifyRequestId) + case MsgSetNetworkProperties: + // V, R, S, signer, networkProperties + proposer, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type NetworkProperties struct { + MinTxFee uint64 `json:"min_tx_fee"` + MaxTxFee uint64 `json:"max_tx_fee"` + VoteQuorum uint64 `json:"vote_quorum"` + MinimumProposalEndTime uint64 `json:"minimum_proposal_end_time"` + ProposalEnactmentTime uint64 `json:"proposal_enactment_time"` + MinProposalEndBlocks uint64 `json:"min_proposal_end_blocks"` + MinProposalEnactmentBlocks uint64 `json:"min_proposal_enactment_blocks"` + EnableForeignFeePayments bool `json:"enable_foreign_fee_payments"` + MischanceRankDecreaseAmount uint64 `json:"mischance_rank_decrease_amount"` + MaxMischance uint64 `json:"max_mischance"` + MischanceConfidence uint64 `json:"mischance_confidence"` + InactiveRankDecreasePercent string `json:"inactive_rank_decrease_percent"` + MinValidators uint64 `json:"min_validators"` + PoorNetworkMaxBankSend uint64 `json:"poor_network_max_bank_send"` + UnjailMaxTime uint64 `json:"unjail_max_time"` + EnableTokenWhitelist bool `json:"enable_token_whitelist"` + EnableTokenBlacklist bool `json:"enable_token_blacklist"` + MinIdentityApprovalTip uint64 `json:"min_identity_approval_tip"` + UniqueIdentityKeys string `json:"unique_identity_keys"` + UbiHardcap uint64 `json:"ubi_hardcap"` + ValidatorsFeeShare string `json:"validators_fee_share"` + InflationRate string `json:"inflation_rate"` + InflationPeriod uint64 `json:"inflation_period"` + UnstakingPeriod uint64 `json:"unstaking_period"` + MaxDelegators uint64 `json:"max_delegators"` + MinDelegationPushout uint64 `json:"min_delegation_pushout"` + SlashingPeriod uint64 `json:"slashing_period"` + MaxJailedPercentage string `json:"max_jailed_percentage"` + MaxSlashingPercentage string `json:"max_slashing_percentage"` + MinCustodyReward uint64 `json:"min_custody_reward"` + MaxCustodyBufferSize uint64 `json:"max_custody_buffer_size"` + MaxCustodyTxSize uint64 `json:"max_custody_tx_size"` + AbstentionRankDecreaseAmount uint64 `json:"abstention_rank_decrease_amount"` + MaxAbstention uint64 `json:"max_abstention"` + MinCollectiveBond uint64 `json:"min_collective_bond"` + MinCollectiveBondingTime uint64 `json:"min_collective_bonding_time"` + MaxCollectiveOutputs uint64 `json:"max_collective_outputs"` + MinCollectiveClaimPeriod uint64 `json:"min_collective_claim_period"` + ValidatorRecoveryBond uint64 `json:"validator_recovery_bond"` + MaxAnnualInflation string `json:"max_annual_inflation"` + MaxProposalTitleSize uint64 `json:"max_proposal_title_size"` + MaxProposalDescriptionSize uint64 `json:"max_proposal_description_size"` + MaxProposalPollOptionSize uint64 `json:"max_proposal_poll_option_size"` + MaxProposalPollOptionCount uint64 `json:"max_proposal_poll_option_count"` + MaxProposalReferenceSize uint64 `json:"max_proposal_reference_size"` + MaxProposalChecksumSize uint64 `json:"max_proposal_checksum_size"` + MinDappBond uint64 `json:"min_dapp_bond"` + MaxDappBond uint64 `json:"max_dapp_bond"` + DappLiquidationThreshold uint64 `json:"dapp_liquidation_threshold"` + DappLiquidationPeriod uint64 `json:"dapp_liquidation_period"` + DappBondDuration uint64 `json:"dapp_bond_duration"` + DappVerifierBond string `json:"dapp_verifier_bond"` + DappAutoDenounceTime uint64 `json:"dapp_auto_denounce_time"` + DappMischanceRankDecreaseAmount uint64 `json:"dapp_mischance_rank_decrease_amount"` + DappMaxMischance uint64 `json:"dapp_max_mischance"` + DappInactiveRankDecreasePercent uint64 `json:"dapp_inactive_rank_decrease_percent"` + DappPoolSlippageDefault string `json:"dapp_pool_slippage_default"` + MintingFtFee uint64 `json:"minting_ft_fee"` + MintingNftFee uint64 `json:"minting_nft_fee"` + VetoThreshold string `json:"veto_threshold"` + } + + var networkProperties NetworkProperties + err = json.Unmarshal(params[4], &networkProperties) + if err != nil { + return nil, err + } + + inActiveRankDecreasePercent, err := cosmostypes.NewDecFromStr(networkProperties.InactiveRankDecreasePercent) + if err != nil { + return nil, err + } + validatorsFeeShare, err := cosmostypes.NewDecFromStr(networkProperties.ValidatorsFeeShare) + if err != nil { + return nil, err + } + inflationRate, err := cosmostypes.NewDecFromStr(networkProperties.InflationRate) + if err != nil { + return nil, err + } + maxJailedPercentage, err := cosmostypes.NewDecFromStr(networkProperties.MaxJailedPercentage) + if err != nil { + return nil, err + } + maxSlashingPercentage, err := cosmostypes.NewDecFromStr(networkProperties.MaxSlashingPercentage) + if err != nil { + return nil, err + } + maxAnnualInflation, err := cosmostypes.NewDecFromStr(networkProperties.MaxAnnualInflation) + if err != nil { + return nil, err + } + dappVerifierBond, err := cosmostypes.NewDecFromStr(networkProperties.DappVerifierBond) + if err != nil { + return nil, err + } + dappPoolSlippageDefault, err := cosmostypes.NewDecFromStr(networkProperties.DappPoolSlippageDefault) + if err != nil { + return nil, err + } + vetoThreshold, err := cosmostypes.NewDecFromStr(networkProperties.VetoThreshold) + if err != nil { + return nil, err + } + + networkPropertiesParam := customgovtypes.NetworkProperties{ + MinTxFee: networkProperties.MinTxFee, + MaxTxFee: networkProperties.MaxTxFee, + VoteQuorum: networkProperties.VoteQuorum, + MinimumProposalEndTime: networkProperties.MinimumProposalEndTime, + ProposalEnactmentTime: networkProperties.ProposalEnactmentTime, + MinProposalEndBlocks: networkProperties.MinProposalEndBlocks, + MinProposalEnactmentBlocks: networkProperties.MinProposalEnactmentBlocks, + EnableForeignFeePayments: networkProperties.EnableForeignFeePayments, + MischanceRankDecreaseAmount: networkProperties.MischanceRankDecreaseAmount, + MaxMischance: networkProperties.MaxMischance, + MischanceConfidence: networkProperties.MischanceConfidence, + InactiveRankDecreasePercent: inActiveRankDecreasePercent, + MinValidators: networkProperties.MinValidators, + PoorNetworkMaxBankSend: networkProperties.PoorNetworkMaxBankSend, + UnjailMaxTime: networkProperties.UnjailMaxTime, + EnableTokenWhitelist: networkProperties.EnableTokenWhitelist, + EnableTokenBlacklist: networkProperties.EnableTokenBlacklist, + MinIdentityApprovalTip: networkProperties.MinIdentityApprovalTip, + UniqueIdentityKeys: networkProperties.UniqueIdentityKeys, + UbiHardcap: networkProperties.UbiHardcap, + ValidatorsFeeShare: validatorsFeeShare, + InflationRate: inflationRate, + InflationPeriod: networkProperties.InflationPeriod, + UnstakingPeriod: networkProperties.UnstakingPeriod, + MaxDelegators: networkProperties.MaxDelegators, + MinDelegationPushout: networkProperties.MinDelegationPushout, + SlashingPeriod: networkProperties.SlashingPeriod, + MaxJailedPercentage: maxJailedPercentage, + MaxSlashingPercentage: maxSlashingPercentage, + MinCustodyReward: networkProperties.MinCustodyReward, + MaxCustodyBufferSize: networkProperties.MaxCustodyBufferSize, + MaxCustodyTxSize: networkProperties.MaxCustodyTxSize, + AbstentionRankDecreaseAmount: networkProperties.AbstentionRankDecreaseAmount, + MaxAbstention: networkProperties.MaxAbstention, + MinCollectiveBond: networkProperties.MinCollectiveBond, + MinCollectiveBondingTime: networkProperties.MinCollectiveBondingTime, + MaxCollectiveOutputs: networkProperties.MaxCollectiveOutputs, + MinCollectiveClaimPeriod: networkProperties.MinCollectiveClaimPeriod, + ValidatorRecoveryBond: networkProperties.ValidatorRecoveryBond, + MaxAnnualInflation: maxAnnualInflation, + MaxProposalTitleSize: networkProperties.MaxProposalTitleSize, + MaxProposalDescriptionSize: networkProperties.MaxProposalDescriptionSize, + MaxProposalPollOptionSize: networkProperties.MaxProposalPollOptionSize, + MaxProposalPollOptionCount: networkProperties.MaxProposalPollOptionCount, + MaxProposalReferenceSize: networkProperties.MaxProposalReferenceSize, + MaxProposalChecksumSize: networkProperties.MaxProposalChecksumSize, + MinDappBond: networkProperties.MinDappBond, + MaxDappBond: networkProperties.MaxDappBond, + DappLiquidationThreshold: networkProperties.DappLiquidationThreshold, + DappLiquidationPeriod: networkProperties.DappLiquidationPeriod, + DappBondDuration: networkProperties.DappBondDuration, + DappVerifierBond: dappVerifierBond, + DappAutoDenounceTime: networkProperties.DappAutoDenounceTime, + DappMischanceRankDecreaseAmount: networkProperties.DappMischanceRankDecreaseAmount, + DappMaxMischance: networkProperties.DappMaxMischance, + DappInactiveRankDecreasePercent: networkProperties.DappInactiveRankDecreasePercent, + DappPoolSlippageDefault: dappPoolSlippageDefault, + MintingFtFee: networkProperties.MintingFtFee, + MintingNftFee: networkProperties.MintingNftFee, + VetoThreshold: vetoThreshold, + } + + msg = customgovtypes.NewMsgSetNetworkProperties(proposer, &networkPropertiesParam) + case MsgSetExecutionFee: + // V, R, S, signer, executionFee, failureFee, timeout, defaultParams + proposer, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type ExecutionFeeParam struct { + TransactionType string `json:"transaction_type"` + ExecutionFee uint64 `json:"execution_fee"` + FailureFee uint64 `json:"failure_fee"` + Timeout uint64 `json:"timeout"` + DefaultParams uint64 `json:"default_params"` + } + + var feeParam ExecutionFeeParam + err = json.Unmarshal(params[4], &feeParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgSetExecutionFee(feeParam.TransactionType, feeParam.ExecutionFee, feeParam.FailureFee, + feeParam.Timeout, feeParam.DefaultParams, proposer) + case MsgClaimCouncilor: + // V, R, S, signer, moniker string, username string, description string, social string, contact string, avatar string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type ClaimCouncilorParam struct { + Moniker string `json:"moniker"` + Username string `json:"username"` + Description string `json:"description"` + Social string `json:"social"` + Contact string `json:"contact"` + Avatar string `json:"avatar"` + } + + var councilorParam ClaimCouncilorParam + err = json.Unmarshal(params[4], &councilorParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgClaimCouncilor(sender, councilorParam.Moniker, councilorParam.Username, councilorParam.Description, + councilorParam.Social, councilorParam.Contact, councilorParam.Avatar) + case MsgWhitelistPermissions: + // V, R, S, signer, permission uint256, controlledAddr string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var permissionParam PermissionsParam + err = json.Unmarshal(params[4], &permissionParam) + if err != nil { + return nil, err + } + + controlledAddr, err := string2cosmosAddr(permissionParam.ControlledAddr) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgWhitelistPermissions(sender, controlledAddr, permissionParam.Permission) + case MsgBlacklistPermissions: + // V, R, S, signer, permission uint256, controlledAddr string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var permissionParam PermissionsParam + err = json.Unmarshal(params[4], &permissionParam) + if err != nil { + return nil, err + } + + controlledAddr, err := string2cosmosAddr(permissionParam.ControlledAddr) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgBlacklistPermissions(sender, controlledAddr, permissionParam.Permission) + case MsgCreateRole: + // V, R, S, signer, sid string, description string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type RoleParam struct { + Sid string `json:"sid"` + Description string `json:"description"` + } + + var roleParam RoleParam + err = json.Unmarshal(params[4], &roleParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgCreateRole(sender, roleParam.Sid, roleParam.Description) + case MsgAssignRole: + // V, R, S, signer, roleid uint32, controller string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var roleParam RoleParam + err = json.Unmarshal(params[4], &roleParam) + if err != nil { + return nil, err + } + + controller, err := string2cosmosAddr(roleParam.Controller) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgAssignRole(sender, controller, roleParam.RoleId) + case MsgUnassignRole: + // V, R, S, signer, roleid uint32, controller address + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var roleParam RoleParam + err = json.Unmarshal(params[4], &roleParam) + if err != nil { + return nil, err + } + + controller, err := string2cosmosAddr(roleParam.Controller) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgUnassignRole(sender, controller, roleParam.RoleId) + case MsgWhitelistRolePermission: + // V, R, S, signer, permission uint32, roleIdentifier string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var roleParam ListRoleParam + err = json.Unmarshal(params[4], &roleParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgWhitelistRolePermission(sender, roleParam.RoleIdentifier, roleParam.Permission) + case MsgBlacklistRolePermission: + // V, R, S, signer, permission uint32, roleIdentifier string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var roleParam ListRoleParam + err = json.Unmarshal(params[4], &roleParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgBlacklistRolePermission(sender, roleParam.RoleIdentifier, roleParam.Permission) + case MsgRemoveWhitelistRolePermission: + // V, R, S, signer, permission uint32, roleIdentifier string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var roleParam ListRoleParam + err = json.Unmarshal(params[4], &roleParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgRemoveWhitelistRolePermission(sender, roleParam.RoleIdentifier, roleParam.Permission) + case MsgRemoveBlacklistRolePermission: + // V, R, S, signer, permission uint32, roleIdentifier string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var roleParam ListRoleParam + err = json.Unmarshal(params[4], &roleParam) + if err != nil { + return nil, err + } + + msg = customgovtypes.NewMsgRemoveBlacklistRolePermission(sender, roleParam.RoleIdentifier, roleParam.Permission) + case MsgClaimValidator: + // V, R, S, signer, moniker string, valKey cosmostypes.ValAddress, pubKey cryptotypes.PubKey + + // type ClaimParam struct { + // Moniker string `json:"moniker"` + // ValKey string `json:"val_key"` + // PubKey string `json:"pub_key"` + // } + + // var claimParam ClaimParam + // err = json.Unmarshal(params[4], &claimParam) + // if err != nil { + // return nil, err + // } + + // valKey, err := cosmostypes.ValAddressFromBech32(claimParam.ValKey) + // if err != nil { + // return nil, err + // } + + // // how to get pub key from string? + // cryptotypes + + // msg, err = stakingtypes.NewMsgClaimValidator(claimParam.Moniker, valKey, claimParam.PubKey) + // if err != nil { + // return nil, err + // } + case MsgUpsertTokenAlias: + // V, R, S, signer, param + proposer, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type UpsertTokenAliasParam struct { + Symbol string `json:"symbol"` + Name string `json:"name"` + Icon string `json:"icon"` + Decimals uint32 `json:"decimals"` + Denoms []string `json:"denoms"` + Invalidated bool `json:"invalidated"` + } + + var upsertTokenAliasParam UpsertTokenAliasParam + err = json.Unmarshal(params[4], &upsertTokenAliasParam) + if err != nil { + return nil, err + } + + msg = tokenstypes.NewMsgUpsertTokenAlias(proposer, upsertTokenAliasParam.Symbol, upsertTokenAliasParam.Name, + upsertTokenAliasParam.Icon, upsertTokenAliasParam.Decimals, upsertTokenAliasParam.Denoms, upsertTokenAliasParam.Invalidated) + case MsgUpsertTokenRate: + // V, R, S, signer, feePayments bool, stakeToken bool, invalidated bool, denom string, rate cosmostypes.Dec, + // stakeCap cosmostypes.Dec, stakeMin cosmostypes.Int + proposer, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type UpsertTokenRateParam struct { + Denom string `json:"denom"` + Rate string `json:"rate"` + IsFeePayments bool `json:"is_fee_payments"` + StakeCap string `json:"stake_cap"` + StakeMin string `json:"stake_min"` + IsStakeToken bool `json:"is_stake_token"` + Invalidated bool `json:"invalidated"` + } + + var upsertParam UpsertTokenRateParam + err = json.Unmarshal(params[4], &upsertParam) + if err != nil { + return nil, err + } + + rate, err := cosmostypes.NewDecFromStr(upsertParam.Rate) + if err != nil { + return nil, err + } + stakeCap, err := cosmostypes.NewDecFromStr(upsertParam.StakeCap) + if err != nil { + return nil, err + } + stakeMin, ok := cosmostypes.NewIntFromString(upsertParam.StakeMin) + if !ok { + return nil, errors.New(fmt.Sprintln("StakeMin - decoding from string to Int type is failed:", upsertParam.StakeMin)) + } + + msg = tokenstypes.NewMsgUpsertTokenRate(proposer, upsertParam.Denom, rate, upsertParam.IsFeePayments, + stakeCap, stakeMin, upsertParam.IsStakeToken, upsertParam.Invalidated) + case MsgActivate: + // V, R, S, signer + validator, err := bytes2cosmosValAddr(params[3][12:]) + if err != nil { + return nil, err + } + msg = slashingtypes.NewMsgActivate(validator) + case MsgPause: + // V, R, S, signer + validator, err := bytes2cosmosValAddr(params[3][12:]) + if err != nil { + return nil, err + } + msg = slashingtypes.NewMsgPause(validator) + case MsgUnpause: + // V, R, S, signer + validator, err := bytes2cosmosValAddr(params[3][12:]) + if err != nil { + return nil, err + } + msg = slashingtypes.NewMsgUnpause(validator) + case MsgCreateSpendingPool: + // V, R, S, signer, name string, claimStart uint64, claimEnd uint64, rates cosmostypes.DecCoins, voteQuorum uint64, + // votePeriod uint64, voteEnactment uint64, owners spendingtypes.PermInfo, beneficiaries spendingtypes.WeightedPermInfo, + // sender cosmostypes.AccAddress, dynamicRate bool, dynamicRatePeriod uint64 + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type WeightedRole struct { + Role uint64 `json:"role"` + Weight string `json:"weight"` + } + + type WeightedAccount struct { + Account string `json:"account"` + Weight string `json:"weight"` + } + + type WeightedPermInfo struct { + Roles []WeightedRole `json:"roles"` + Accounts []WeightedAccount `json:"accounts"` + } + + type SpendingPoolParam struct { + Name string `json:"name"` + ClaimStart uint64 `json:"claim_start"` + ClaimEnd uint64 `json:"claim_end"` + Rates []string `json:"rates"` + VoteQuorum uint64 `json:"vote_quorum"` + VotePeriod uint64 `json:"vote_period"` + VoteEnactment uint64 `json:"vote_enactment"` + Owners spendingtypes.PermInfo `json:"owners"` + Beneficiaries WeightedPermInfo `json:"beneficiaries"` + IsDynamicRate bool `json:"is_dynamic_rate"` + DynamicRatePeriod uint64 `json:"dynamic_rate_period"` + } + + var poolParam SpendingPoolParam + err = json.Unmarshal(params[4], &poolParam) + if err != nil { + return nil, err + } + + var rates cosmostypes.DecCoins + for _, rate := range poolParam.Rates { + coin, err := cosmostypes.ParseDecCoin(rate) + if err != nil { + return nil, err + } + rates.Add(coin) + } + + var beneficiaries spendingtypes.WeightedPermInfo + for _, account := range poolParam.Beneficiaries.Accounts { + weight, err := cosmostypes.NewDecFromStr(account.Weight) + if err != nil { + return nil, err + } + weightedAccount := spendingtypes.WeightedAccount{ + Account: account.Account, + Weight: weight, + } + beneficiaries.Accounts = append(beneficiaries.Accounts, weightedAccount) + } + for _, role := range poolParam.Beneficiaries.Roles { + weight, err := cosmostypes.NewDecFromStr(role.Weight) + if err != nil { + return nil, err + } + weightedRole := spendingtypes.WeightedRole{ + Role: role.Role, + Weight: weight, + } + beneficiaries.Roles = append(beneficiaries.Roles, weightedRole) + } + + msg = spendingtypes.NewMsgCreateSpendingPool(poolParam.Name, poolParam.ClaimStart, poolParam.ClaimEnd, rates, + poolParam.VoteQuorum, poolParam.VotePeriod, poolParam.VoteEnactment, poolParam.Owners, beneficiaries, + sender, poolParam.IsDynamicRate, poolParam.DynamicRatePeriod) + case MsgDepositSpendingPool: + // V, R, S, signer, amount string, name string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var poolParam SpendingPoolParam + err = json.Unmarshal(params[4], &poolParam) + if err != nil { + return nil, err + } + + amounts, err := cosmostypes.ParseCoinsNormalized(poolParam.Amounts) + if err != nil { + return nil, err + } + + msg = spendingtypes.NewMsgDepositSpendingPool(poolParam.Name, amounts, sender) + case MsgRegisterSpendingPoolBeneficiary: + // V, R, S, signer, name string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var poolParam SpendingPoolParam + err = json.Unmarshal(params[4], &poolParam) + if err != nil { + return nil, err + } + + msg = spendingtypes.NewMsgRegisterSpendingPoolBeneficiary(poolParam.Name, sender) + case MsgClaimSpendingPool: + // V, R, S, signer, name string + sender, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var poolParam SpendingPoolParam + err = json.Unmarshal(params[4], &poolParam) + if err != nil { + return nil, err + } + + msg = spendingtypes.NewMsgClaimSpendingPool(poolParam.Name, sender) + case MsgUpsertStakingPool: + // V, R, S, signer, enabled bool, validator string, commission cosmostypes.Dec + from, err := hex2bech32(hex.EncodeToString(params[3][12:]), TypeKiraAddr) + if err != nil { + return nil, err + } + + type StakingPoolParam struct { + Validator string `json:"validator"` + Enabled bool `json:"enabled"` + Commission string `json:"commission"` + } + + var poolParam StakingPoolParam + err = json.Unmarshal(params[4], &poolParam) + if err != nil { + return nil, err + } + + commission, err := cosmostypes.NewDecFromStr(poolParam.Commission) + if err != nil { + return nil, err + } + + msg = multistakingtypes.NewMsgUpsertStakingPool(from, poolParam.Validator, poolParam.Enabled, commission) + case MsgDelegate: + // V, R, S, signer, param + from, err := hex2bech32(hex.EncodeToString(params[3][12:]), TypeKiraAddr) + if err != nil { + return nil, err + } + + var delegateParam DelegateParam + err = json.Unmarshal(params[4], &delegateParam) + if err != nil { + return nil, err + } + + amounts, err := cosmostypes.ParseCoinsNormalized(delegateParam.Amounts) + if err != nil { + return nil, err + } + + msg = multistakingtypes.NewMsgDelegate(from, delegateParam.To, amounts) + case MsgUndelegate: + // V, R, S, signer, amount, validator + from, err := hex2bech32(hex.EncodeToString(params[3][12:]), TypeKiraAddr) + if err != nil { + return nil, err + } + + var delegateParam DelegateParam + err = json.Unmarshal(params[4], &delegateParam) + if err != nil { + return nil, err + } + + amounts, err := cosmostypes.ParseCoinsNormalized(delegateParam.Amounts) + if err != nil { + return nil, err + } + + msg = multistakingtypes.NewMsgUndelegate(from, delegateParam.To, amounts) + case MsgClaimRewards: + // V, R, S, signer + from, err := hex2bech32(hex.EncodeToString(params[3][12:]), TypeKiraAddr) + if err != nil { + return nil, err + } + msg = multistakingtypes.NewMsgClaimRewards(from) + case MsgClaimUndelegation: + // V, R, S, signer, undelegationId uint64 + from, err := hex2bech32(hex.EncodeToString(params[3][12:]), TypeKiraAddr) + if err != nil { + return nil, err + } + + type UndelegationParam struct { + UndelegationId uint64 `json:"undelegation_id"` + } + + var undelegationParam UndelegationParam + err = json.Unmarshal(params[4], &undelegationParam) + if err != nil { + return nil, err + } + + msg = multistakingtypes.NewMsgClaimUndelegation(from, undelegationParam.UndelegationId) + case MsgSetCompoundInfo: + // V, R, S, signer, allDenom bool, len, denoms []string + from, err := hex2bech32(hex.EncodeToString(params[3][12:]), TypeKiraAddr) + if err != nil { + return nil, err + } + + type UndelegationParam struct { + IsAllDenom bool `json:"is_all_denom"` + Denoms []string `json:"denoms"` + } + + var undelegationParam UndelegationParam + err = json.Unmarshal(params[4], &undelegationParam) + if err != nil { + return nil, err + } + + msg = multistakingtypes.NewMsgSetCompoundInfo(from, undelegationParam.IsAllDenom, undelegationParam.Denoms) + case MsgRegisterDelegator: + // V, R, S, signer + from, err := hex2bech32(hex.EncodeToString(params[3][12:]), TypeKiraAddr) + if err != nil { + return nil, err + } + + msg = multistakingtypes.NewMsgRegisterDelegator(from) + case MsgCreateCustody: + // V, R, S, signer, custodyMode uint256, key string, nextController string, len, boolArr, len, strArr + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + type CustodyParam struct { + CustodySettings custodytypes.CustodySettings `json:"custody_settings"` + OldKey string `json:"old_key"` + NewKey string `json:"new_key"` + Next string `json:"next"` + Target string `json:"target"` + } + + var custodyParam CustodyParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + msg = custodytypes.NewMsgCreateCustody(from, custodyParam.CustodySettings, custodyParam.OldKey, custodyParam.NewKey, + custodyParam.Next, custodyParam.Target) + case MsgAddToCustodyWhiteList: + // V, R, S, signer, oldKey string, newKey string, next string, target string + // len, newAddr []string, + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam CustodianParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + var newAddrs []cosmostypes.AccAddress + for _, addrStr := range custodyParam.NewAddrs { + addr, err := cosmostypes.AccAddressFromBech32(addrStr) + if err != nil { + return nil, err + } + newAddrs = append(newAddrs, addr) + } + + msg = custodytypes.NewMsgAddToCustodyWhiteList(from, newAddrs, custodyParam.OldKey, custodyParam.NewKey, + custodyParam.Next, custodyParam.Target) + case MsgAddToCustodyCustodians: + // V, R, S, signer, oldKey string, newKey string, next string, target string + // len, newAddr []string + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam CustodianParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + var newAddrs []cosmostypes.AccAddress + for _, addrStr := range custodyParam.NewAddrs { + addr, err := cosmostypes.AccAddressFromBech32(addrStr) + if err != nil { + return nil, err + } + newAddrs = append(newAddrs, addr) + } + + msg = custodytypes.NewMsgAddToCustodyCustodians(from, newAddrs, custodyParam.OldKey, custodyParam.NewKey, + custodyParam.Next, custodyParam.Target) + case MsgRemoveFromCustodyCustodians: + // V, R, S, signer, newAddr cosmostypes.AccAddress, + // oldKey string, newKey string, next string, target string + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam SingleCustodianParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + newAddr, err := cosmostypes.AccAddressFromBech32(custodyParam.NewAddr) + if err != nil { + return nil, err + } + + msg = custodytypes.NewMsgRemoveFromCustodyCustodians(from, newAddr, custodyParam.OldKey, custodyParam.NewKey, + custodyParam.Next, custodyParam.Target) + case MsgDropCustodyCustodians: + // V, R, S, signer + // oldKey string, newKey string, next string, target string + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam CustodianParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + msg = custodytypes.NewMsgDropCustodyCustodians(from, custodyParam.OldKey, custodyParam.NewKey, + custodyParam.Next, custodyParam.Target) + case MsgRemoveFromCustodyWhiteList: + // V, R, S, signer, newAddr string, + // oldKey string, newKey string, next string, target string + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam SingleCustodianParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + newAddr, err := cosmostypes.AccAddressFromBech32(custodyParam.NewAddr) + if err != nil { + return nil, err + } + + msg = custodytypes.NewMsgRemoveFromCustodyWhiteList(from, newAddr, custodyParam.OldKey, custodyParam.NewKey, + custodyParam.Next, custodyParam.Target) + case MsgDropCustodyWhiteList: + // V, R, S, signer + // oldKey string, newKey string, next string, target string + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam CustodianParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + msg = custodytypes.NewMsgDropCustodyWhiteList(from, custodyParam.OldKey, custodyParam.NewKey, + custodyParam.Next, custodyParam.Target) + case MsgApproveCustodyTx: + // V, R, S, signer, to string, hash string + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam CustodyParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + to, err := cosmostypes.AccAddressFromBech32(custodyParam.To) + if err != nil { + return nil, err + } + + msg = custodytypes.NewMsgApproveCustodyTransaction(from, to, custodyParam.Hash) + case MsgDeclineCustodyTx: + // V, R, S, signer, to string, hash string + from, err := bytes2cosmosAddr(params[3][12:]) + if err != nil { + return nil, err + } + + var custodyParam CustodyParam + err = json.Unmarshal(params[4], &custodyParam) + if err != nil { + return nil, err + } + + to, err := cosmostypes.AccAddressFromBech32(custodyParam.To) + if err != nil { + return nil, err + } + + msg = custodytypes.NewMsgDeclineCustodyTransaction(from, to, custodyParam.Hash) + } + + // fmt.Println(msg) + err = txBuilder.SetMsgs(msg) + if err != nil { + return nil, err + } + txBuilder.SetGasLimit(ethTxData.GasLimit) + // TODO: set fee amount - how can I get the fee amount from eth tx? or fix this? + txBuilder.SetFeeAmount(cosmostypes.NewCoins(cosmostypes.NewInt64Coin(config.DefaultKiraDenom, 200))) + // txBuilder.SetMemo() + // txBuilder.SetTimeoutHeight() + // txHash, err := sendTx(context.Background(), byteData) + + accNum, _ := common.GetAccountNumberSequence(gwCosmosmux, r, addr1) + + privs := []cryptotypes.PrivKey{config.Config.PrivKey} + accNums := []uint64{accNum} // The accounts' account numbers + accSeqs := []uint64{ethTxData.Nonce} // The accounts' sequence numbers + + // First round: gather all the signer infos + var sigsV2 []signing.SignatureV2 + for i, priv := range privs { + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: config.EncodingCg.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: accSeqs[i], + } + + sigsV2 = append(sigsV2, sigV2) + } + + err = txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + interxStatus := common.GetInterxStatus("http://127.0.0.1:11000") + + // Second round: all signer infos are set, so each signer can sign. + sigsV2 = []signing.SignatureV2{} + for i, priv := range privs { + signerData := xauthsigning.SignerData{ + ChainID: interxStatus.InterxInfo.ChainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + } + sigV2, err := clienttx.SignWithPrivKey( + config.EncodingCg.TxConfig.SignModeHandler().DefaultMode(), signerData, + txBuilder, priv, config.EncodingCg.TxConfig, accSeqs[i]) + if err != nil { + return nil, err + } + + sigsV2 = append(sigsV2, sigV2) + } + err = txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + txBytes, err := config.EncodingCg.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, err + } + + return txBytes, err +} diff --git a/gateway/kira/metamask/metamask.go b/gateway/kira/metamask/metamask.go new file mode 100644 index 0000000..41b9ad8 --- /dev/null +++ b/gateway/kira/metamask/metamask.go @@ -0,0 +1,334 @@ +package metamask + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math" + "math/big" + "net/http" + + "github.com/KiraCore/interx/common" + "github.com/KiraCore/interx/config" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +type EthMetamaskRequestBody struct { + ID interface{} `json:"id"` + Method string `json:"method"` + JsonRPC string `json:"jsonrpc"` + Params []interface{} `json:"params"` + // Add other fields from the request body if needed +} + +type EthMetamaskResponseBody struct { + ID interface{} `json:"id"` + JsonRPC string `json:"jsonrpc"` + Result interface{} `json:"result"` + // Add other fields from the request body if needed +} + +type EthEstimateGas struct { + From string `json:"from"` + Value string `json:"value"` + GasPrice string `json:"gasPrice"` + Data string `json:"data"` + To string `json:"to"` +} + +// balance of the account of given address +type EthGetBalanceParams struct { + From string + // integer block number, or the string "latest", "earliest" or "pending" + + // defaultBlock parameter: + // HEX String - an integer block number + // String "earliest" for the earliest/genesis block + // String "latest" - for the latest mined block + // String "safe" - for the latest safe head block + // String "finalized" - for the latest finalized block + // String "pending" - for the pending state/transactions + BlockNum string +} + +type EthGetBlockNumber struct { + BlockNum string + // If true it returns the full transaction objects, if false only the hashes of the transactions + Filter bool +} + +// number of transactions sent from an address. +type EthGetTransactionCount struct { + From string + // integer block number, or the string "latest", "earliest" or "pending" + BlockNum string +} + +func RegisterKiraMetamaskRoutes(r *mux.Router, gwCosmosmux *runtime.ServeMux, rpcAddr string) { + r.HandleFunc(config.MetamaskEndpoint, MetamaskRequestHandler(gwCosmosmux, rpcAddr)).Methods("POST") + // r.HandleFunc(config.RegisterAddrEndpoint, AddressRegisterHandler(gwCosmosmux, rpcAddr)).Methods("Get") + + common.AddRPCMethod("POST", config.MetamaskEndpoint, "This is an API to interact with metamask.", true) + // common.AddRPCMethod("POST", config.MetamaskEndpoint, "This is an API to interact with metamask.", true) +} + +func MetamaskRequestHandler(gwCosmosmux *runtime.ServeMux, rpcAddr string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + byteData, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Unmarshal the request body into a struct + var requestBody EthMetamaskRequestBody + err = json.Unmarshal(byteData, &requestBody) + if err != nil { + fmt.Println("unmarshal err:", err, byteData) + http.Error(w, "Failed to parse request body", http.StatusBadRequest) + return + } + + fmt.Println("eth method:", requestBody.Method) + // fmt.Println("eth params:", requestBody.Params) + + response := map[string]interface{}{ + "id": requestBody.ID, + "jsonrpc": requestBody.JsonRPC, + } + + var result interface{} + + switch requestBody.Method { + case "eth_chainId": + result = fmt.Sprintf("0x%x", config.DefaultChainID) + case "eth_getBalance": + params := EthGetBalanceParams{ + From: requestBody.Params[0].(string), + BlockNum: requestBody.Params[1].(string), + } + + balances := GetBalance(params, gwCosmosmux, r) + + result = "0x0" + if len(balances) > 0 { + for _, coin := range balances { + if coin.Denom == config.DefaultKiraDenom { + balance := new(big.Int) + balance.SetString(coin.Amount, 10) + balance = balance.Mul(balance, big.NewInt(int64(math.Pow10(12)))) + result = fmt.Sprintf("0x%x", balance) + } + } + } + case "eth_blockNumber": + currentHeight, err := GetBlockNumber(rpcAddr) + + if err != nil { + fmt.Println("eth_getBlockByNumber err:", err) + return + } + + result = fmt.Sprintf("0x%x", currentHeight) + case "net_version": + result = config.DefaultChainID + case "eth_getBlockByNumber": + blockNum := requestBody.Params[0].(string) + response, txs, err := GetBlockByNumberOrHash(blockNum, rpcAddr, BlockQueryByNumber) + if err != nil { + fmt.Println("eth_getBlockByNumber err:", err) + return + } + + result = map[string]interface{}{ + "extraData": "0x737061726b706f6f6c2d636e2d6e6f64652d3132", + "gasLimit": "0x1c9c380", + "gasUsed": "0x79ccd3", + "hash": "0x" + response.BlockId.Hash, + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x" + response.SdkBlock.Header.ProposerAddress, + // "mixHash": "0x3d1fdd16f15aeab72e7db1013b9f034ee33641d92f71c0736beab4e67d34c7a7", + // "nonce": "0x4db7a1c01d8a8072", + "number": "0x" + fmt.Sprintf("%x", response.SdkBlock.Header.Height), + "parentHash": "0x" + hex.EncodeToString(response.SdkBlock.Header.LastBlockId.Hash), + "receiptsRoot": "0x" + response.SdkBlock.Header.DataHash, + "size": "0x41c7", + "stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d", + "timestamp": fmt.Sprintf("0x%x", response.SdkBlock.Header.Time.UnixMilli()), + "totalDifficulty": "0x12ac11391a2f3872fcd", + "transactions": txs, + "transactionsRoot": "0x" + response.SdkBlock.Header.DataHash, + "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncles": []string{}, + } + case "eth_getBlockByHash": + blockHash := requestBody.Params[0].(string) + + response, txs, err := GetBlockByNumberOrHash(blockHash, rpcAddr, BLockQueryByHash) + if err != nil { + fmt.Println("eth_getBlockByNumber err:", err) + return + } + + result = map[string]interface{}{ + "extraData": "0x737061726b706f6f6c2d636e2d6e6f64652d3132", + "gasLimit": "0x1c9c380", + "gasUsed": "0x79ccd3", + "hash": "0x" + response.BlockId.Hash, + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x" + response.SdkBlock.Header.ProposerAddress, + // "mixHash": "0x3d1fdd16f15aeab72e7db1013b9f034ee33641d92f71c0736beab4e67d34c7a7", + // "nonce": "0x4db7a1c01d8a8072", + "number": "0x" + fmt.Sprintf("%x", response.SdkBlock.Header.Height), + "parentHash": "0x" + hex.EncodeToString(response.SdkBlock.Header.LastBlockId.Hash), + "receiptsRoot": "0x" + response.SdkBlock.Header.DataHash, + "size": "0x41c7", + "stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d", + "timestamp": fmt.Sprintf("0x%x", response.SdkBlock.Header.Time.UnixMilli()), + "totalDifficulty": "0x12ac11391a2f3872fcd", + "transactions": txs, + "transactionsRoot": "0x" + response.SdkBlock.Header.DataHash, + "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncles": []string{}, + } + + file, _ := json.MarshalIndent(result, "", " ") + fmt.Println("getblockbyhash:", string(file)) + case "eth_estimateGas": + var params EthEstimateGas + byteData, err := json.Marshal(requestBody.Params[0]) + if err != nil { + fmt.Println("Error:", err) + return + } + err = json.Unmarshal(byteData, ¶ms) + if err != nil { + fmt.Println("Error:", err) + return + } + result = "0x5cec" + case "eth_gasPrice": + result = "0x64" + case "eth_getCode": + result = "" + case "eth_getTransactionCount": + params := EthGetTransactionCount{ + From: requestBody.Params[0].(string), + BlockNum: requestBody.Params[1].(string), + } + + _, sequence, err := GetAccountInfo(params, gwCosmosmux, r) + if err != nil { + fmt.Println("eth_getTransactionCount err:", err) + return + } + + result = fmt.Sprintf("0x%x", sequence) + case "eth_sendTransaction": + // var params EthSendTransaction + // byteData, err := json.Marshal(requestBody.Params[0]) + // if err != nil { + // fmt.Println("Error:", err) + // return + // } + // err = json.Unmarshal(byteData, ¶ms) + // if err != nil { + // fmt.Println("Error:", err) + // return + // } + // fmt.Println("eth_sendTransaction params:", params) + case "eth_sendRawTransaction": + fmt.Println("params:", requestBody.Params) + strData, ok := requestBody.Params[0].(string) + if !ok { + fmt.Println("eth_sendRawTransaction err: convert from interface{} to string failed") + return + } + + txHash, err := sendTx(strData, gwCosmosmux, r) + if err != nil { + fmt.Println("eth_sendRawTransaction err:", err) + return + } + + result = "0x" + txHash + + fmt.Println("sendrawtx:", result) + case "eth_getTransactionReceipt": + txHash := requestBody.Params[0].(string) + fmt.Println("eth_getTransactionReceipt", txHash) + txInfo, logInfos, err := GetTxInfo(txHash, rpcAddr) + var txStatus, txIndex int + blockInfo := CosmosBlockInfo{} + var fromAddr, toAddr string + + if err != nil { + txStatus = 0 + } else { + var txhashes []string + blockInfo, txhashes, err = GetBlockByNumberOrHash(txInfo.Height, rpcAddr, BlockQueryByNumber) + if err != nil { + fmt.Println("eth_getTransactionReceipt err:", err) + return + } + + if logInfos.logForString != "" { + fromAddr, err = bech322hex(logInfos.logForMap[0].LogInfo[0]["transfer"]["sender"]) + if err != nil { + fmt.Println("eth_getTransactionReceipt err:", err) + return + } + toAddr, err = bech322hex(logInfos.logForMap[0].LogInfo[0]["transfer"]["recipient"]) + if err != nil { + fmt.Println("eth_getTransactionReceipt err:", err) + return + } + } + + txIndex = 0 + for i, blockTxHash := range txhashes { + if blockTxHash == txHash { + txIndex = i + } + } + + txStatus = 1 + } + + result = map[string]interface{}{ + "blockHash": "0x" + blockInfo.BlockId.Hash, + "blockNumber": fmt.Sprintf("0x%x", blockInfo.SdkBlock.Header.Height), + "contractAddress": nil, // string of the address if it was created + "cumulativeGasUsed": "0x79ccd3", + "effectiveGasPrice": "0x64", + "from": "0x" + fromAddr, + "gasUsed": "0x5208", + "logs": []string{}, + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": fmt.Sprintf("0x%x", txStatus), + "to": "0x" + toAddr, + "transactionHash": "0x" + txInfo.Hash, + "transactionIndex": fmt.Sprintf("0x%x", txIndex), + "type": "0x2", + } + + file, _ := json.MarshalIndent(result, "", " ") + fmt.Println("gettxreceipt:", string(file)) + default: + fmt.Println("unknown method:", requestBody.Method) + } + + response["result"] = result + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) + } +} diff --git a/gateway/kira/metamask/utils.go b/gateway/kira/metamask/utils.go new file mode 100644 index 0000000..abffbb3 --- /dev/null +++ b/gateway/kira/metamask/utils.go @@ -0,0 +1,222 @@ +package metamask + +import ( + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "strconv" + "strings" + + "github.com/KiraCore/interx/config" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/ethereum/go-ethereum/accounts/abi" +) + +const ( + TypeKiraAddr = iota + TypeKiraValAddr +) + +func hex2bech32(hexAddr string, addrType int) (string, error) { + hexAddress := hexAddr + if strings.Contains(hexAddr, "0x") { + hexAddress = hexAddr[2:] + } + byteArray, err := hex.DecodeString(hexAddress) + if err != nil { + return "", err + } + + var bech32Addr string + switch addrType { + case TypeKiraAddr: + bech32Addr, err = bech32.ConvertAndEncode(config.DefaultKiraAddrPrefix, byteArray) + case TypeKiraValAddr: + bech32Addr, err = bech32.ConvertAndEncode(config.DefaultKiraValAddrPrefix, byteArray) + default: + err = errors.New(fmt.Sprintln("invalid addrType: ", addrType)) + } + if err != nil { + return "", err + } + return bech32Addr, nil +} + +func bech322hex(bech32Addr string) (string, error) { + _, data, err := bech32.DecodeAndConvert(bech32Addr) + if err != nil { + return "", nil + } + + // Encode the byte slice as a hex string + hexStr := hex.EncodeToString(data) + return hexStr, nil +} + +func hex2int64(hexStr string) (int64, error) { + hexString := hexStr + if strings.Contains(hexStr, "0x") { + hexString = hexStr[2:] + } + integer, err := strconv.ParseInt(hexString, 16, 64) + if err != nil { + return 0, err + } + return integer, nil +} + +func hex2int32(hexStr string) (int32, error) { + hexString := hexStr + if strings.Contains(hexStr, "0x") { + hexString = hexStr[2:] + } + integer, err := strconv.ParseInt(hexString, 16, 32) + if err != nil { + return 0, err + } + return int32(integer), nil +} + +func hex2uint64(hexStr string) (uint64, error) { + hexString := hexStr + if strings.Contains(hexStr, "0x") { + hexString = hexStr[2:] + } + integer, err := strconv.ParseUint(hexString, 16, 64) + if err != nil { + return 0, err + } + return integer, nil +} + +func hex2uint32(hexStr string) (uint32, error) { + hexString := hexStr + if strings.Contains(hexStr, "0x") { + hexString = hexStr[2:] + } + integer, err := strconv.ParseUint(hexString, 16, 32) + if err != nil { + return 0, err + } + return uint32(integer), nil +} + +func bytes2uint64(param []byte) (uint64, error) { + integer, err := strconv.ParseUint(hex.EncodeToString(param), 16, 64) + if err != nil { + return 0, err + } + return integer, nil +} + +func bytes2uint32(param []byte) (uint32, error) { + integer, err := strconv.ParseUint(hex.EncodeToString(param), 16, 32) + if err != nil { + return 0, err + } + return uint32(integer), nil +} + +func bytes2int64(param []byte) (int64, error) { + integer, err := strconv.ParseInt(hex.EncodeToString(param), 16, 64) + if err != nil { + return 0, err + } + return integer, nil +} + +func bytes2int32(param []byte) (int32, error) { + integer, err := strconv.ParseUint(hex.EncodeToString(param), 16, 32) + if err != nil { + return 0, err + } + return int32(integer), nil +} + +func uint32To32Bytes(val uint32) []byte { + byteArr := make([]byte, 4) + binary.BigEndian.PutUint32(byteArr, val) + + paddedByteArr := addPaddingTo32Bytes(byteArr) + + return paddedByteArr +} + +func addPaddingTo32Bytes(byteArr []byte) []byte { + // Pad the byte array with leading zeros to get the desired length + paddedByteArr := make([]byte, 32) + copy(paddedByteArr[32-len(byteArr):], byteArr) + + return paddedByteArr +} + +func bytes2cosmosAddr(param []byte) (cosmostypes.AccAddress, error) { + addrBech32, err := hex2bech32(hex.EncodeToString(param), TypeKiraAddr) + if err != nil { + return nil, err + } + + addr, err := cosmostypes.AccAddressFromBech32(addrBech32) + if err != nil { + return nil, err + } + + return addr, nil +} + +func string2cosmosAddr(param string) (cosmostypes.AccAddress, error) { + addr, err := cosmostypes.AccAddressFromBech32(param) + + return addr, err +} + +func bytes2cosmosValAddr(param []byte) (cosmostypes.ValAddress, error) { + addr, err := cosmostypes.ValAddressFromHex("0x" + hex.EncodeToString(param)) + if err != nil { + return nil, err + } + + return addr, nil +} + +func bytes2bool(param []byte) bool { + return (param[len(param)-1] == 0x01) +} + +func PackABIParams(abi abi.ABI, name string, args ...interface{}) ([]byte, error) { + method, exist := abi.Methods[name] + if !exist { + return nil, fmt.Errorf("method '%s' not found", name) + } + arguments, err := method.Inputs.Pack(args...) + if err != nil { + return nil, err + } + // Pack up the method ID too if not a constructor and return + return arguments, nil +} + +func convertByteArr2Bytes32(val []byte) [32]byte { + var returnVal [32]byte + copy(returnVal[:], val) + return returnVal +} + +func getSignature(r, s, v []byte) []byte { + sig := make([]byte, 65) + copy(sig[:32], r) + copy(sig[32:64], s) + copy(sig[64:], v) + sig[64] = sig[64] - 27 + return sig +} + +func getSignatureV2(r, s, v []byte) []byte { + sig := make([]byte, 65) + copy(sig[:32], r) + copy(sig[32:64], s) + copy(sig[64:], v) + return sig +} diff --git a/gateway/kira/tokens.go b/gateway/kira/tokens.go index 4a84bee..feb5b17 100644 --- a/gateway/kira/tokens.go +++ b/gateway/kira/tokens.go @@ -2,9 +2,12 @@ package kira import ( "encoding/json" + "fmt" "net/http" + "strconv" "strings" + "cosmossdk.io/math" "github.com/KiraCore/interx/common" "github.com/KiraCore/interx/config" "github.com/KiraCore/interx/types" @@ -31,44 +34,97 @@ func queryKiraTokensAliasesHandler(r *http.Request, gwCosmosmux *runtime.ServeMu Icon string `json:"icon"` Amount sdk.Int `json:"amount"` } + type Pagination struct { + NextKey string `json:"next_key"` + Total string `json:"total"` + } type TokenAliasesResult struct { Data []TokenAliasesData `json:"token_aliases_data"` DefaultDenom string `json:"default_denom"` Bech32Prefix string `json:"bech32_prefix"` + Pagination *Pagination `json:"pagination,omitempty"` } - tokens, defaultDenom, bech32Prefix := common.GetTokenAliases(gwCosmosmux, r.Clone(r.Context())) + tokenAliases, defaultDenom, bech32Prefix := common.GetTokenAliases(gwCosmosmux, r.Clone(r.Context())) tokensSupply := common.GetTokenSupply(gwCosmosmux, r.Clone(r.Context())) + supplyMap := make(map[string]math.Int) + for _, supply := range tokensSupply { + supplyMap[supply.Denom] = supply.Amount + } + + allData := make([]TokenAliasesData, 0) + for _, tokenAlias := range tokenAliases { + supplyAmount := math.ZeroInt() + for _, denom := range tokenAlias.Denoms { + if supply, ok := supplyMap[denom]; ok { + supplyAmount = supply + break + } + } + allData = append(allData, TokenAliasesData{ + Decimals: tokenAlias.Decimals, + Denoms: tokenAlias.Denoms, + Name: tokenAlias.Name, + Symbol: tokenAlias.Symbol, + Icon: tokenAlias.Icon, + Amount: supplyAmount, + }) + } + data := make([]TokenAliasesData, 0) - for _, token := range tokens { - flag := false - for _, denom := range token.Denoms { - for _, supply := range tokensSupply { - if denom == supply.Denom { - data = append(data, TokenAliasesData{ - Decimals: token.Decimals, - Denoms: token.Denoms, - Name: token.Name, - Symbol: token.Symbol, - Icon: token.Icon, - Amount: supply.Amount, - }) - - flag = true + // request could have tokens list if so, returns those + tokensParam := r.URL.Query().Get("tokens") + if tokensParam != "" { + tokensList := strings.Split(tokensParam, ",") + tokensMap := make(map[string]bool) + for _, token := range tokensList { + tokensMap[token] = true + } + + for _, aliasData := range allData { + for _, denom := range aliasData.Denoms { + if tokensMap[denom] { + data = append(data, aliasData) break } } - if flag { - break - } } + result := TokenAliasesResult{ + Data: data, + DefaultDenom: defaultDenom, + Bech32Prefix: bech32Prefix, + } + + return result, nil, http.StatusOK + } + + // if request does not have tokens list, return with pagination + offsetParam := r.URL.Query().Get("offset") + offset, err := strconv.Atoi(offsetParam) + if err != nil { + offset = 0 + } + limitParam := r.URL.Query().Get("limit") + limit, err := strconv.Atoi(limitParam) + if err != nil { + limit = 100 + } + + lastIndex := offset + limit + if lastIndex > len(allData) { + lastIndex = len(allData) } + data = allData[offset:lastIndex] result := TokenAliasesResult{ Data: data, DefaultDenom: defaultDenom, Bech32Prefix: bech32Prefix, + Pagination: &Pagination{ + NextKey: fmt.Sprintf("%d", lastIndex), + Total: fmt.Sprintf("%d", len(allData)), + }, } return result, nil, http.StatusOK diff --git a/go.mod b/go.mod index 3664ca9..d615da3 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/KiraCore/interx go 1.19 require ( + cosmossdk.io/math v1.2.0 github.com/KeisukeYamashita/go-jsonrpc v1.0.1 github.com/KiraCore/sekai v0.3.38 github.com/btcsuite/btcd v0.22.1 @@ -35,7 +36,6 @@ require ( cosmossdk.io/depinject v1.0.0-alpha.4 // indirect cosmossdk.io/errors v1.0.0 // indirect cosmossdk.io/log v1.2.1 // indirect - cosmossdk.io/math v1.2.0 // indirect cosmossdk.io/tools/rosetta v0.2.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -170,3 +170,4 @@ require ( ) replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + diff --git a/interx b/interx deleted file mode 100755 index f52d427..0000000 Binary files a/interx and /dev/null differ diff --git a/main.go b/main.go index f2ef8fb..9ab2f62 100644 --- a/main.go +++ b/main.go @@ -80,6 +80,7 @@ func main() { initNodeDiscoveryInterxPort := initCommand.String("node_discovery_interx_port", "11000", "The default interx port to be used in node discovery") initNodeDiscoveryTendermintPort := initCommand.String("node_discovery_tendermint_port", "26657", "The default tendermint port to be used in node discovery") initNodeDiscoveryTimeout := initCommand.String("node_discovery_timeout", "3s", "The connection timeout to be used in node discovery") + initSnapShotInterval := initCommand.Uint64("snapshot_interval", 100, "The snapshot time interval amount to be used to sleep snapshot function") startHomePtr := startCommand.String("home", homeDir+"/.interxd", "The interx configuration path. (Required)") @@ -155,6 +156,7 @@ func main() { *initNodeDiscoveryTendermintPort, *initNodeDiscoveryTimeout, nodeKey, + *initSnapShotInterval, ) fmt.Printf("Created interx configuration file: %s\n", *initHomePtr+"/config.json") diff --git a/scripts/sbuild.sh b/scripts/sbuild.sh new file mode 100755 index 0000000..7934a11 --- /dev/null +++ b/scripts/sbuild.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e +set -x +. /etc/profile + +go mod tidy +CGO_ENABLED=0 go build -a -tags netgo -installsuffix cgo -ldflags "-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=ignore -extldflags '-static'" -o "/interxd" . +go mod verify +echoInfo "INFO: Sucessfully intalled INTERX $(/interxd version)" diff --git a/scripts/test-local/Tokens/query-tokens-aliases.sh b/scripts/test-local/Tokens/query-tokens-aliases.sh index f74f19c..5070c08 100755 --- a/scripts/test-local/Tokens/query-tokens-aliases.sh +++ b/scripts/test-local/Tokens/query-tokens-aliases.sh @@ -8,8 +8,8 @@ TEST_NAME="TOKENS-RATES-QUERY" && timerStart $TEST_NAME echoInfo "INFO: $TEST_NAME - Integration Test - START" INTERX_GATEWAY="127.0.0.1:11000" -TOKENS_NUMS_INTERX=$(curl --fail "$INTERX_GATEWAY/api/kira/tokens/aliases" | jq '. | length' || exit 1) -TOKENS_NUMS_CLI=$(showTokenAliases | jq '. | length') +TOKENS_NUMS_INTERX=$(curl --fail "$INTERX_GATEWAY/api/kira/tokens/aliases" | jq '.token_aliases_data | length' || exit 1) +TOKENS_NUMS_CLI=$(showTokenAliases | jq '.data | length') [[ $TOKENS_NUMS_INTERX != $TOKENS_NUMS_CLI ]] && echoErr "ERROR: Expected number of tokens to be '$TOKENS_NUMS_CLI', but got '$TOKENS_NUMS_INTERX'" && exit 1 diff --git a/tasks/node_discover.go b/tasks/node_discover.go index 125ec14..3817439 100644 --- a/tasks/node_discover.go +++ b/tasks/node_discover.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "io/ioutil" "net" "net/http" @@ -361,6 +362,48 @@ func getBlockFromInterx(rpcAddr string, height string) (*struct { return response, nil } +type GeoData struct { + Query string `json:"query"` + Status string `json:"status"` + Country string `json:"country"` + CountryCode string `json:"countryCode"` + Region string `json:"region"` + RegionName string `json:"regionName"` + City string `json:"city"` + Zip string `json:"zip"` + Lat string `json:"lat"` + Lon string `json:"lon"` + TimeZone string `json:"timezone"` + Isp string `json:"isp"` + Org string `json:"org"` + As string `json:"as"` +} + +func getGeoData(ipAddr string) GeoData { + geodata := GeoData{} + // http://ip-api.com/json/24.48.0.1 + geoApiEndpoint := "http://ip-api.com/json/" + ipAddr + res, err := http.Get(geoApiEndpoint) + if err != nil { + common.GetLogger().Error("failed to query geo info", err) + return geodata + } + defer res.Body.Close() + + resBody, err := io.ReadAll(res.Body) + if err != nil { + common.GetLogger().Error("failed to read response body", err) + return geodata + } + + err = json.Unmarshal(resBody, &geodata) + if err != nil { + common.GetLogger().Error("failed to unmarshal geodata", err) + return geodata + } + return geodata +} + func NodeDiscover(rpcAddr string, isLog bool) { initPrivateIps() @@ -447,6 +490,12 @@ func NodeDiscover(rpcAddr string, isLog bool) { } nodeInfo.Safe = kiraStatus.NodeInfo.Network == common.NodeStatus.Chainid + nodeInfo.PeersNumber = int64(len(nodeInfo.Peers)) // e.g. 160 + nodeInfo.Address = kiraStatus.ValidatorInfo.Address.String() // e.g. "kira1epxqxf2l4x4yj35j54vkyh0l32a5mq3rss735h" + + geodata := getGeoData(ipAddr) + nodeInfo.CountryCode = geodata.CountryCode // e.g. "DE" + nodeInfo.DataCenter = geodata.Isp // e.g. "Contabo GmbH" if nodeInfo.Safe { commonBlock := common.NodeStatus.Block @@ -479,6 +528,7 @@ func NodeDiscover(rpcAddr string, isLog bool) { peers := peersFromIP[ipAddr] for _, peer := range peers { nodeInfo.Peers = append(nodeInfo.Peers, string(peer.NodeInfo.ID())) + nodeInfo.PeersNumber = int64(len(nodeInfo.Peers)) ip, _ := getHostname(peer.NodeInfo.ListenAddr) if isPrivateIP(ip) { @@ -488,6 +538,7 @@ func NodeDiscover(rpcAddr string, isLog bool) { privNodeInfo.Port, _ = getPort(peer.NodeInfo.ListenAddr) privNodeInfo.Peers = []string{} privNodeInfo.Peers = append(privNodeInfo.Peers, nodeInfo.ID) + privNodeInfo.PeersNumber = int64(len(privNodeInfo.Peers)) privNodeInfo.Alive = true privNodeInfo.Synced = nodeInfo.Synced privNodeInfo.BlockHeightAtSync = nodeInfo.BlockHeightAtSync @@ -517,16 +568,6 @@ func NodeDiscover(rpcAddr string, isLog bool) { } } - global.Mutex.Lock() - if pid, isIn := idOfPubList[nodeInfo.ID]; isIn { - PubP2PNodeListResponse.NodeList[pid] = nodeInfo - } else { - PubP2PNodeListResponse.NodeList = append(PubP2PNodeListResponse.NodeList, nodeInfo) - idOfPubList[nodeInfo.ID] = len(PubP2PNodeListResponse.NodeList) - 1 - } - global.Mutex.Unlock() - isPubNodeId[nodeInfo.ID] = true - interxStartTime := makeTimestamp() interxAddress := getInterxAddress(ipAddr) interxStatus := common.GetInterxStatus(interxAddress) @@ -552,6 +593,7 @@ func NodeDiscover(rpcAddr string, isLog bool) { } interxInfo.Safe = interxStatus.InterxInfo.ChainID == common.NodeStatus.Chainid + nodeInfo.Address = interxStatus.InterxInfo.KiraAddr if nodeInfo.Safe { commonBlock := common.NodeStatus.Block @@ -597,6 +639,16 @@ func NodeDiscover(rpcAddr string, isLog bool) { isSnapshotIP[snapNode.IP] = true } } + + global.Mutex.Lock() + if pid, isIn := idOfPubList[nodeInfo.ID]; isIn { + PubP2PNodeListResponse.NodeList[pid] = nodeInfo + } else { + PubP2PNodeListResponse.NodeList = append(PubP2PNodeListResponse.NodeList, nodeInfo) + idOfPubList[nodeInfo.ID] = len(PubP2PNodeListResponse.NodeList) - 1 + } + global.Mutex.Unlock() + isPubNodeId[nodeInfo.ID] = true } lastUpdate := time.Now().Unix() diff --git a/tasks/snapshot_checksum.go b/tasks/snapshot_checksum.go index b62109b..d4bc101 100644 --- a/tasks/snapshot_checksum.go +++ b/tasks/snapshot_checksum.go @@ -71,6 +71,7 @@ func calcChecksum(isLog bool) { func CalcSnapshotChecksum(isLog bool) { available := 0 for { + time.Sleep(time.Duration(config.Config.SnapshotInterval) * time.Millisecond) file, err := os.Stat(config.SnapshotPath()) if err != nil { diff --git a/types/nodes.go b/types/nodes.go index 73dbe18..4bec183 100644 --- a/types/nodes.go +++ b/types/nodes.go @@ -39,6 +39,10 @@ type P2PNode struct { Safe bool `json:"safe"` BlockHeightAtSync int64 `json:"block_height_at_sync"` BlockDiff int64 `json:"block_diff"` + PeersNumber int64 `json:"peers_number"` // e.g. 160 + CountryCode string `json:"country_code"` // e.g. "DE" + DataCenter string `json:"data_center"` // e.g. "Contabo GmbH" + Address string `json:"address"` // e.g. "kira1epxqxf2l4x4yj35j54vkyh0l32a5mq3rss735h" } type InterxNode struct {