Skip to content

Commit

Permalink
feat: implement thor claims with verifiable data
Browse files Browse the repository at this point in the history
  • Loading branch information
shreyasbhat0 committed Jul 18, 2024
1 parent 43a89c6 commit e6dac5d
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 78 deletions.
14 changes: 12 additions & 2 deletions proto/arkeo/claim/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,30 @@ service Msg {
rpc AddClaim(MsgAddClaim) returns (MsgAddClaimResponse);
// this line is used by starport scaffolding # proto/tx/rpc
}


message MsgThorTxData {
string thor_data = 1;
string proof_signature = 2;
string proof_pubkey = 3;
}


message MsgClaimEth {
bytes creator = 1 [ (gogoproto.casttype) =
"github.com/cosmos/cosmos-sdk/types.AccAddress" ];
string eth_address = 2; // the adress the claim is for
string signature = 3; // EIP712 signature that has to be signed by ethAddress
string thor_tx = 4; // the tx hash of the thorchain tx that delegates the arkeo claim
MsgThorTxData thor_tx = 4; // the tx hash of the thorchain tx that delegates the arkeo claim
}

message MsgClaimEthResponse {}


message MsgClaimArkeo {
bytes creator = 1 [ (gogoproto.casttype) =
"github.com/cosmos/cosmos-sdk/types.AccAddress" ];
string thor_tx = 2; // the tx hash of the thorchain tx that delegates the arkeo claim
MsgThorTxData thor_tx_data = 2;
}

message MsgClaimArkeoResponse {}
Expand Down
49 changes: 7 additions & 42 deletions x/claim/client/offchain/thor_tx_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,9 @@ import (
"net/http"

"cosmossdk.io/errors"
"github.com/arkeonetwork/arkeo/x/claim/types"
)

type Coin struct {
Asset string `json:"asset"`
Amount string `json:"amount"`
}

type Tx struct {
ID string `json:"id"`
Chain string `json:"chain"`
FromAddress string `json:"from_address"`
ToAddress string `json:"to_address"`
Coins []Coin `json:"coins"`
Gas interface{} `json:"gas"`
Memo string `json:"memo"`
}

type ObservedTx struct {
Tx Tx `json:"tx"`
}

type KeysignMetric struct {
TxID string `json:"tx_id"`
NodeTSSTimes interface{} `json:"node_tss_times"`
}

type ThorChainTxData struct {
ObservedTx ObservedTx `json:"observed_tx"`
ConsensusHeight int `json:"consensus_height"`
FinalisedHeight int `json:"finalised_height"`
KeysignMetric KeysignMetric `json:"keysign_metric"`
}

type ThorTxData struct {
Hash string `json:"hash"`
TxData string `json:"tx_data"`
}

func fetchThorChainTxData(hash string) (string, error) {
url := fmt.Sprintf("https://thornode.ninerealms.com/thorchain/tx/%s", hash)

Expand All @@ -70,7 +35,7 @@ func fetchThorChainTxData(hash string) (string, error) {
return "", fmt.Errorf("error reading response body: %w", err)
}

var result ThorChainTxData
var result types.ThorChainTxData
if err = json.Unmarshal(body, &result); err != nil {
return "", fmt.Errorf("error unmarshalling response: %w", err)
}
Expand All @@ -81,12 +46,12 @@ func fetchThorChainTxData(hash string) (string, error) {
}

txDataHash := sha512.Sum512(resultBytes)
txHex := base64.RawURLEncoding.EncodeToString(txDataHash[:])
txDataHex := base64.RawURLEncoding.EncodeToString(resultBytes)
txHashBase64 := base64.StdEncoding.EncodeToString(txDataHash[:])
txDataBase64 := base64.StdEncoding.EncodeToString(resultBytes)

txData := ThorTxData{
Hash: txHex,
TxData: txDataHex,
txData := types.ThorTxData{
Hash: txHashBase64,
TxData: txDataBase64,
}

txDataBytes, err := json.Marshal(txData)
Expand Down
7 changes: 4 additions & 3 deletions x/claim/keeper/msg_server_claim_arkeo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"

"github.com/arkeonetwork/arkeo/x/claim/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
Expand All @@ -14,10 +15,10 @@ func (k msgServer) ClaimArkeo(goCtx context.Context, msg *types.MsgClaimArkeo) (
return nil, errors.Wrapf(err, "failed to get claim record for %s", msg.Creator)
}

if msg.ThorTx != "" {
arkeoClaimRecord, err = k.updateThorClaimRecord(ctx, msg.Creator.String(), msg.ThorTx, arkeoClaimRecord)
if msg.ThorTxData != nil {
arkeoClaimRecord, err = k.updateThorClaimRecord(ctx, msg.Creator.String(), msg.ThorTxData, arkeoClaimRecord)
if err != nil {
return nil, errors.Wrapf(err, "failed to get claim record for %s", msg.ThorTx)
return nil, errors.Wrapf(err, "failed to get claim record for %s", msg.ThorTxData)
}
}

Expand Down
2 changes: 1 addition & 1 deletion x/claim/keeper/msg_server_claim_eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (k msgServer) ClaimEth(goCtx context.Context, msg *types.MsgClaimEth) (*typ
return nil, errors.Wrapf(err, "failed to set claim record for %s", msg.Creator)
}

if msg.ThorTx != "" {
if msg.ThorTx != nil {
arkeoClaim, err = k.updateThorClaimRecord(ctx, msg.Creator.String(), msg.ThorTx, arkeoClaim)
if err != nil {
return nil, errors.Wrapf(err, "failed to get claim record for %s", msg.ThorTx)
Expand Down
66 changes: 36 additions & 30 deletions x/claim/keeper/msg_server_claim_thorchain.go
Original file line number Diff line number Diff line change
@@ -1,55 +1,47 @@
package keeper

import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"strings"

"github.com/arkeonetwork/arkeo/x/claim/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
"io"
"net/http"
"strings"
)

type ThorTxData struct {
ObservedTx struct {
Tx struct {
FromAddress string `json:"from_address"`
Memo string `json:"memo"`
} `json:"tx"`
} `json:"observed_tx"`
}

// Verify and update the claim record based on the memo of the thorchain tx
func (k msgServer) updateThorClaimRecord(ctx sdk.Context, creator string, thorTx string, arkeoClaimRecord types.ClaimRecord) (types.ClaimRecord, error) {
url := fmt.Sprintf("https://thornode.ninerealms.com/thorchain/tx/%s", thorTx)
func (k msgServer) updateThorClaimRecord(ctx sdk.Context, creator string, thorTxMsg *types.MsgThorTxData, arkeoClaimRecord types.ClaimRecord) (types.ClaimRecord, error) {

req, err := http.NewRequest(http.MethodGet, url, nil)
thorDataDecoded, err := base64.StdEncoding.DecodeString(thorTxMsg.ThorData)
if err != nil {
return types.ClaimRecord{}, errors.Wrapf(err, "failed to build request %s", req.RequestURI)
return types.ClaimRecord{}, fmt.Errorf("error base64 decoding faild: %w", err)
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
return types.ClaimRecord{}, errors.Wrapf(err, "failed to get thorchain tx for %s", thorTx)
var thorData *types.ThorTxData
if err := json.Unmarshal(thorDataDecoded, &thorData); err != nil {
return types.ClaimRecord{}, fmt.Errorf("error unmarshalling transaction data: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return types.ClaimRecord{}, fmt.Errorf("received non-OK HTTP status: %d", resp.StatusCode)
var thorTxData *types.ThorChainTxData
if err := json.Unmarshal([]byte(thorData.TxData), &thorTxData); err != nil {
return types.ClaimRecord{}, fmt.Errorf("error unmarshalling transaction data: %w", err)
}

body, err := io.ReadAll(resp.Body)
marshalledtxBytes, err := json.Marshal(thorData)
if err != nil {
return types.ClaimRecord{}, fmt.Errorf("error reading response body: %w", err)
return types.ClaimRecord{}, fmt.Errorf("error marshalling tx data: %w", err)
}

var txData ThorTxData
if err := json.Unmarshal(body, &txData); err != nil {
return types.ClaimRecord{}, fmt.Errorf("error unmarshalling transaction data: %w", err)
verifyTxData := base64.StdEncoding.EncodeToString(marshalledtxBytes)

if verifyTxData != thorData.Hash {
return types.ClaimRecord{}, fmt.Errorf("transaction data cehcksum failed")
}
thorAddress := txData.ObservedTx.Tx.FromAddress
memo := txData.ObservedTx.Tx.Memo

thorAddress := thorTxData.ObservedTx.Tx.FromAddress
memo := thorTxData.ObservedTx.Tx.Memo

thorAddressBytes, err := sdk.GetFromBech32(thorAddress, "thor")
if err != nil {
Expand All @@ -63,6 +55,20 @@ func (k msgServer) updateThorClaimRecord(ctx sdk.Context, creator string, thorTx
return types.ClaimRecord{}, fmt.Errorf("failed to encode address bytes with new prefix: %w", err)
}

decodedPubKey, err := hex.DecodeString(thorTxMsg.ProofPubkey)
if err != nil {
return types.ClaimRecord{}, fmt.Errorf("error in decoding pubkey: %w", err)
}

proofAddress, err := sdk.Bech32ifyAddressBytes(prefix, decodedPubKey)
if err != nil {
return types.ClaimRecord{}, fmt.Errorf("invalid thorchain address: %w", err)
}

if proofAddress != thorDerivedArkeoAddress {
return types.ClaimRecord{}, fmt.Errorf("address validation failed: %w", err)
}

thorClaimRecord, err := k.GetClaimRecord(ctx, thorDerivedArkeoAddress, types.ARKEO)
if err != nil {
return types.ClaimRecord{}, errors.Wrapf(err, "failed to get claim record for %s", thorDerivedArkeoAddress)
Expand Down
37 changes: 37 additions & 0 deletions x/claim/types/thor_tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package types

type Coin struct {
Asset string `json:"asset"`
Amount string `json:"amount"`
}

type Tx struct {
ID string `json:"id"`
Chain string `json:"chain"`
FromAddress string `json:"from_address"`
ToAddress string `json:"to_address"`
Coins []Coin `json:"coins"`
Gas interface{} `json:"gas"`
Memo string `json:"memo"`
}

type ObservedTx struct {
Tx Tx `json:"tx"`
}

type KeysignMetric struct {
TxID string `json:"tx_id"`
NodeTSSTimes interface{} `json:"node_tss_times"`
}

type ThorChainTxData struct {
ObservedTx ObservedTx `json:"observed_tx"`
ConsensusHeight int `json:"consensus_height"`
FinalisedHeight int `json:"finalised_height"`
KeysignMetric KeysignMetric `json:"keysign_metric"`
}

type ThorTxData struct {
Hash string `json:"hash"`
TxData string `json:"tx_data"`
}

0 comments on commit e6dac5d

Please sign in to comment.