Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upload btc execution metadata to ipfs #339

Merged
merged 20 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/ChainSafe/sygma-relayer/chains"
"github.com/ChainSafe/sygma-relayer/chains/btc"
"github.com/ChainSafe/sygma-relayer/chains/btc/mempool"
"github.com/ChainSafe/sygma-relayer/chains/btc/uploader"
"github.com/ChainSafe/sygma-relayer/chains/evm"
"github.com/ChainSafe/sygma-relayer/chains/evm/calls/contracts/bridge"
"github.com/ChainSafe/sygma-relayer/chains/evm/calls/events"
Expand Down Expand Up @@ -331,6 +332,8 @@ func Run() error {

mempool := mempool.NewMempoolAPI(config.MempoolUrl)
mh := &btcExecutor.BtcMessageHandler{}
uploader := uploader.NewIPFSUploader(config.UploaderConfig)

executor := btcExecutor.NewExecutor(
propStore,
host,
Expand All @@ -341,7 +344,8 @@ func Run() error {
mempool,
resources,
config.Network,
exitLock)
exitLock,
uploader)

btcChain := btc.NewBtcChain(listener, executor, mh, *config.GeneralChainConfig.Id)
domains[*config.GeneralChainConfig.Id] = btcChain
Expand Down
27 changes: 17 additions & 10 deletions chains/btc/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,23 @@ type Resource struct {
Script []byte
}

type UploaderConfig struct {
URL string `mapstructure:"url" default:"https://api.pinata.cloud/pinning/pinFileToIPFS"`
Token string `mapstructure:"token" default:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5mb3JtYXRpb24iOnsiaWQiOiJkOTU2YTZiNC1mOWFlLTRmZDctYTc5My0zZTFmM2FjYzc4MjIiLCJlbWFpbCI6InRjYXIxMjEyOTNAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInBpbl9wb2xpY3kiOnsicmVnaW9ucyI6W3siZGVzaXJlZFJlcGxpY2F0aW9uQ291bnQiOjEsImlkIjoiRlJBMSJ9LHsiZGVzaXJlZFJlcGxpY2F0aW9uQ291bnQiOjEsImlkIjoiTllDMSJ9XSwidmVyc2lvbiI6MX0sIm1mYV9lbmFibGVkIjpmYWxzZSwic3RhdHVzIjoiQUNUSVZFIn0sImF1dGhlbnRpY2F0aW9uVHlwZSI6InNjb3BlZEtleSIsInNjb3BlZEtleUtleSI6ImNkZDljZGFmYWJhOTYxMGYxNDdjIiwic2NvcGVkS2V5U2VjcmV0IjoiMGI0OTQ4NmZjMTYzNmQxZTIyZTE5OGFlZWViOGE2NmRjMDM4NmRmNWRlOTkyY2NlOTlkZDBiYzQ5YzBiODdmYSIsImV4cCI6MTc1MzM2MTkxN30.xP92Ts9661M3CjxIEKobZ_jwdIPmxb_m2POJ4mpNBss"`
tcar121293 marked this conversation as resolved.
Show resolved Hide resolved
}
type RawBtcConfig struct {
chain.GeneralChainConfig `mapstructure:",squash"`
Resources []RawResource `mapstrcture:"resources"`
StartBlock int64 `mapstructure:"startBlock"`
FeeAddress string `mapstructure:"feeAddress"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
BlockInterval int64 `mapstructure:"blockInterval" default:"5"`
BlockRetryInterval uint64 `mapstructure:"blockRetryInterval" default:"5"`
BlockConfirmations int64 `mapstructure:"blockConfirmations" default:"10"`
Network string `mapstructure:"network" default:"mainnet"`
MempoolUrl string `mapstructure:"mempoolUrl"`
Resources []RawResource `mapstrcture:"resources"`
StartBlock int64 `mapstructure:"startBlock"`
FeeAddress string `mapstructure:"feeAddress"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
BlockInterval int64 `mapstructure:"blockInterval" default:"5"`
BlockRetryInterval uint64 `mapstructure:"blockRetryInterval" default:"5"`
BlockConfirmations int64 `mapstructure:"blockConfirmations" default:"10"`
Network string `mapstructure:"network" default:"mainnet"`
MempoolUrl string `mapstructure:"mempoolUrl"`
UploaderConfig UploaderConfig `mapstructure:"uploaderConfig"`
tcar121293 marked this conversation as resolved.
Show resolved Hide resolved
}

func (c *RawBtcConfig) Validate() error {
Expand Down Expand Up @@ -79,6 +84,7 @@ type BtcConfig struct {
Script []byte
MempoolUrl string
Network chaincfg.Params
UploaderConfig UploaderConfig
}

// NewBtcConfig decodes and validates an instance of an BtcConfig from
Expand Down Expand Up @@ -152,6 +158,7 @@ func NewBtcConfig(chainConfig map[string]interface{}) (*BtcConfig, error) {
MempoolUrl: c.MempoolUrl,
FeeAddress: feeAddress,
Resources: resources,
UploaderConfig: c.UploaderConfig,
}
return config, nil
}
Expand Down
8 changes: 8 additions & 0 deletions chains/btc/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ func (s *NewBtcConfigTestSuite) Test_ValidConfig() {
Tweak: "tweak",
},
},
"uploaderConfig": config.UploaderConfig{
URL: "https://testIPFSProvider.com",
Token: "testToken",
},
}

actualConfig, err := config.NewBtcConfig(rawConfig)
Expand Down Expand Up @@ -143,5 +147,9 @@ func (s *NewBtcConfigTestSuite) Test_ValidConfig() {
FeeAmount: big.NewInt(10000000),
},
},
UploaderConfig: config.UploaderConfig{
URL: "https://testIPFSProvider.com",
Token: "testToken",
},
})
}
29 changes: 29 additions & 0 deletions chains/btc/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ChainSafe/sygma-relayer/chains/btc/config"
"github.com/ChainSafe/sygma-relayer/chains/btc/connection"
"github.com/ChainSafe/sygma-relayer/chains/btc/mempool"
"github.com/ChainSafe/sygma-relayer/chains/btc/uploader"
"github.com/ChainSafe/sygma-relayer/comm"
"github.com/ChainSafe/sygma-relayer/store"
"github.com/ChainSafe/sygma-relayer/tss"
Expand Down Expand Up @@ -60,6 +61,7 @@ type Executor struct {
propMutex sync.Mutex

exitLock *sync.RWMutex
uploader uploader.Uploader
}

func NewExecutor(
Expand All @@ -73,6 +75,7 @@ func NewExecutor(
resources map[[32]byte]config.Resource,
chainCfg chaincfg.Params,
exitLock *sync.RWMutex,
uploader uploader.Uploader,
) *Executor {
return &Executor{
propStorer: propStorer,
Expand All @@ -85,6 +88,7 @@ func NewExecutor(
resources: resources,
mempool: mempool,
chainCfg: chainCfg,
uploader: uploader,
}
}

Expand Down Expand Up @@ -273,6 +277,8 @@ func (e *Executor) rawTx(proposals []*BtcTransferProposal, resource config.Resou

func (e *Executor) outputs(tx *wire.MsgTx, proposals []*BtcTransferProposal) (uint64, error) {
outputAmount := uint64(0)
var dataToUpload []map[string]interface{}

for _, prop := range proposals {
addr, err := btcutil.DecodeAddress(prop.Data.Recipient, &e.chainCfg)
if err != nil {
Expand All @@ -282,10 +288,33 @@ func (e *Executor) outputs(tx *wire.MsgTx, proposals []*BtcTransferProposal) (ui
if err != nil {
return 0, err
}

dataToUpload = append(dataToUpload, map[string]interface{}{
"sourceDomain": prop.Source,
"depositNonce": prop.Data.DepositNonce,
})

txOut := wire.NewTxOut(int64(prop.Data.Amount), destinationAddrByte)
tx.AddTxOut(txOut)

outputAmount += prop.Data.Amount
}

// Upload to IPFS
cid, err := e.uploader.Upload(dataToUpload)
if err != nil {
log.Error().Err(err).Msg("Error occured while uploading metadata to ipfs")
}

// Store the CID in OP_RETURN
opReturnData := []byte("syg_" + cid)
opReturnScript, err := txscript.NullDataScript(opReturnData)
if err != nil {
log.Error().Err(err).Msg("Error occured while constructiong OP_RETURN data")
}

opReturnOut := wire.NewTxOut(0, opReturnScript)
tx.AddTxOut(opReturnOut)
return outputAmount, nil
}

Expand Down
81 changes: 81 additions & 0 deletions chains/btc/uploader/ipfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package uploader

import (
"bytes"
"encoding/json"
"io"
"mime/multipart"
"net/http"

"github.com/ChainSafe/sygma-relayer/chains/btc/config"
)

type Uploader interface {
Upload(proposals []map[string]interface{}) (string, error)
}

type IPFSUploader struct {
config config.UploaderConfig
}

func NewIPFSUploader(config config.UploaderConfig) *IPFSUploader {
return &IPFSUploader{config: config}
}

type IPFSResponse struct {
IpfsHash string `json:"IpfsHash"`
}

func (s *IPFSUploader) Upload(dataToUpload []map[string]interface{}) (string, error) {

tcar121293 marked this conversation as resolved.
Show resolved Hide resolved
// Convert proposals to JSON
jsonData, err := json.Marshal(dataToUpload)
if err != nil {
return "", err
}

// Create a multipart form file
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", "metadata.json")
tcar121293 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return "", err
}
_, err = part.Write(jsonData)
if err != nil {
return "", err
}
writer.Close()

// Create a new request
tcar121293 marked this conversation as resolved.
Show resolved Hide resolved
req, err := http.NewRequest("POST", s.config.URL, body)
if err != nil {
return "", err
}

// Set the headers
req.Header.Add("Authorization", "Bearer "+s.config.Token)
tcar121293 marked this conversation as resolved.
Show resolved Hide resolved
req.Header.Add("Content-Type", writer.FormDataContentType())

// Make the request
client := &http.Client{}
resp, err := client.Do(req)
tcar121293 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return "", err
}
defer resp.Body.Close()

// Read the response
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}

// Parse the response
var ipfsResponse IPFSResponse
if err := json.Unmarshal(respBody, &ipfsResponse); err != nil {
return "", err
}

return ipfsResponse.IpfsHash, nil
}
7 changes: 5 additions & 2 deletions example/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import (

"github.com/ChainSafe/sygma-relayer/chains/btc"
"github.com/ChainSafe/sygma-relayer/chains/btc/mempool"
"github.com/ChainSafe/sygma-relayer/chains/btc/uploader"
substrateListener "github.com/ChainSafe/sygma-relayer/chains/substrate/listener"
substratePallet "github.com/ChainSafe/sygma-relayer/chains/substrate/pallet"
"github.com/ChainSafe/sygma-relayer/relayer/transfer"
propStore "github.com/ChainSafe/sygma-relayer/store"
"github.com/ChainSafe/sygma-relayer/tss"
"github.com/sygmaprotocol/sygma-core/chains/evm/listener"
"github.com/sygmaprotocol/sygma-core/chains/evm/transactor/gas"
"github.com/sygmaprotocol/sygma-core/chains/evm/transactor/transaction"
Expand Down Expand Up @@ -61,7 +63,6 @@ import (
"github.com/ChainSafe/sygma-relayer/config"
"github.com/ChainSafe/sygma-relayer/keyshare"
"github.com/ChainSafe/sygma-relayer/topology"
"github.com/ChainSafe/sygma-relayer/tss"
evmClient "github.com/sygmaprotocol/sygma-core/chains/evm/client"
)

Expand Down Expand Up @@ -274,6 +275,7 @@ func Run() error {

mempool := mempool.NewMempoolAPI(config.MempoolUrl)
mh := &btcExecutor.BtcMessageHandler{}
uploader := uploader.NewIPFSUploader(config.UploaderConfig)
executor := btcExecutor.NewExecutor(
propStore,
host,
Expand All @@ -284,7 +286,8 @@ func Run() error {
mempool,
resources,
config.Network,
exitLock)
exitLock,
uploader)

btcChain := btc.NewBtcChain(listener, executor, mh, *config.GeneralChainConfig.Id)
chains[*config.GeneralChainConfig.Id] = btcChain
Expand Down
Loading