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

fix: post audit fixes #329

Merged
merged 13 commits into from
Jul 17, 2024
76 changes: 47 additions & 29 deletions chains/btc/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ import (
var (
signingTimeout = 30 * time.Minute

INPUT_SIZE = 180
OUTPUT_SIZE = 34
INPUT_SIZE uint64 = 180
OUTPUT_SIZE uint64 = 34
FEE_ROUNDING_FACTOR uint64 = 5
)

type MempoolAPI interface {
Expand Down Expand Up @@ -93,8 +94,8 @@ func (e *Executor) Execute(proposals []*proposal.Proposal) error {
e.exitLock.RLock()
defer e.exitLock.RUnlock()

sessionID := proposals[0].MessageID
props, err := e.proposalsForExecution(proposals)
messageID := proposals[0].MessageID
props, err := e.proposalsForExecution(proposals, messageID)
if err != nil {
return err
}
Expand All @@ -118,15 +119,14 @@ func (e *Executor) Execute(proposals []*proposal.Proposal) error {
return fmt.Errorf("no resource for ID %s", hex.EncodeToString(resourceID[:]))
}

sessionID := fmt.Sprintf("%s-%s", sessionID, hex.EncodeToString(resourceID[:]))
return e.executeResourceProps(props, resource, sessionID)
return e.executeResourceProps(props, resource, messageID)
})
}
return p.Wait()
}

func (e *Executor) executeResourceProps(props []*BtcTransferProposal, resource config.Resource, sessionID string) error {
log.Info().Str("SessionID", sessionID).Msgf("Executing proposals for resource %s", hex.EncodeToString(resource.ResourceID[:]))
func (e *Executor) executeResourceProps(props []*BtcTransferProposal, resource config.Resource, messageID string) error {
log.Info().Str("messageID", messageID).Msgf("Executing proposals %+v for resource %s", props, hex.EncodeToString(resource.ResourceID[:]))

tx, utxos, err := e.rawTx(props, resource)
if err != nil {
Expand All @@ -137,8 +137,11 @@ func (e *Executor) executeResourceProps(props []*BtcTransferProposal, resource c
p := pool.New().WithErrors()
executionContext, cancelExecution := context.WithCancel(context.Background())
watchContext, cancelWatch := context.WithCancel(context.Background())
sessionID := fmt.Sprintf("%s-%s", messageID, hex.EncodeToString(resource.ResourceID[:]))
defer cancelWatch()
p.Go(func() error { return e.watchExecution(watchContext, cancelExecution, tx, props, sigChn, sessionID) })
p.Go(func() error {
return e.watchExecution(watchContext, cancelExecution, tx, props, sigChn, sessionID, messageID)
})
prevOuts := make(map[wire.OutPoint]*wire.TxOut)
for _, utxo := range utxos {
txOut := wire.NewTxOut(int64(utxo.Value), resource.Script)
Expand All @@ -151,8 +154,14 @@ func (e *Executor) executeResourceProps(props []*BtcTransferProposal, resource c
prevOutputFetcher := txscript.NewMultiPrevOutFetcher(prevOuts)
sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)

var buf buffer.Buffer
_ = tx.Serialize(&buf)
bytes := buf.Bytes()
log.Info().Str("messageID", messageID).Msgf("Assembled raw unsigned transaction %s", hex.EncodeToString(bytes))

// we need to sign each input individually
for i := range tx.TxIn {
sessionID := fmt.Sprintf("%s-%d", sessionID, i)
txHash, err := txscript.CalcTaprootSignatureHash(sigHashes, txscript.SigHashDefault, tx, i, prevOutputFetcher)
if err != nil {
return err
Expand All @@ -164,7 +173,8 @@ func (e *Executor) executeResourceProps(props []*BtcTransferProposal, resource c
i,
msg,
resource.Tweak,
fmt.Sprintf("%s-%d", sessionID, i),
messageID,
sessionID,
e.host,
e.comm,
e.fetcher)
Expand All @@ -177,7 +187,14 @@ func (e *Executor) executeResourceProps(props []*BtcTransferProposal, resource c
return p.Wait()
}

func (e *Executor) watchExecution(ctx context.Context, cancelExecution context.CancelFunc, tx *wire.MsgTx, proposals []*BtcTransferProposal, sigChn chan interface{}, sessionID string) error {
func (e *Executor) watchExecution(
ctx context.Context,
cancelExecution context.CancelFunc,
tx *wire.MsgTx,
proposals []*BtcTransferProposal,
sigChn chan interface{},
sessionID string,
messageID string) error {
timeout := time.NewTicker(signingTimeout)
defer timeout.Stop()
defer cancelExecution()
Expand All @@ -197,15 +214,15 @@ func (e *Executor) watchExecution(ctx context.Context, cancelExecution context.C
}
cancelExecution()

hash, err := e.sendTx(tx, signatures)
hash, err := e.sendTx(tx, signatures, messageID)
if err != nil {
_ = e.comm.Broadcast(e.host.Peerstore().Peers(), []byte{}, comm.TssFailMsg, sessionID)
e.storeProposalsStatus(proposals, store.FailedProp)
return err
}

e.storeProposalsStatus(proposals, store.ExecutedProp)
log.Info().Str("SessionID", sessionID).Msgf("Sent proposals execution with hash: %s", hash)
log.Info().Str("messageID", messageID).Msgf("Sent proposals execution with hash: %s", hash)
}
case <-timeout.C:
{
Expand All @@ -225,7 +242,7 @@ func (e *Executor) rawTx(proposals []*BtcTransferProposal, resource config.Resou
if err != nil {
return nil, nil, err
}
feeEstimate, err := e.fee(int64(len(proposals)), int64(len(proposals)))
feeEstimate, err := e.fee(uint64(len(proposals)), uint64(len(proposals)))
if err != nil {
return nil, nil, err
}
Expand All @@ -236,26 +253,26 @@ func (e *Executor) rawTx(proposals []*BtcTransferProposal, resource config.Resou
if inputAmount < outputAmount {
return nil, nil, fmt.Errorf("utxo input amount %d less than output amount %d", inputAmount, outputAmount)
}
fee, err := e.fee(int64(len(utxos)), int64(len(proposals))+1)
fee, err := e.fee(uint64(len(utxos)), uint64(len(proposals))+1)
if err != nil {
return nil, nil, err
}

returnAmount := int64(inputAmount) - fee - outputAmount
returnAmount := inputAmount - fee - outputAmount
if returnAmount > 0 {
// return extra funds
returnScript, err := txscript.PayToAddrScript(resource.Address)
if err != nil {
return nil, nil, err
}
txOut := wire.NewTxOut(returnAmount, returnScript)
txOut := wire.NewTxOut(int64(returnAmount), returnScript)
tx.AddTxOut(txOut)
}
return tx, utxos, err
}

func (e *Executor) outputs(tx *wire.MsgTx, proposals []*BtcTransferProposal) (int64, error) {
outputAmount := int64(0)
func (e *Executor) outputs(tx *wire.MsgTx, proposals []*BtcTransferProposal) (uint64, error) {
outputAmount := uint64(0)
for _, prop := range proposals {
addr, err := btcutil.DecodeAddress(prop.Data.Recipient, &e.chainCfg)
if err != nil {
Expand All @@ -265,16 +282,16 @@ func (e *Executor) outputs(tx *wire.MsgTx, proposals []*BtcTransferProposal) (in
if err != nil {
return 0, err
}
txOut := wire.NewTxOut(prop.Data.Amount, destinationAddrByte)
txOut := wire.NewTxOut(int64(prop.Data.Amount), destinationAddrByte)
tx.AddTxOut(txOut)
outputAmount += prop.Data.Amount
}
return outputAmount, nil
}

func (e *Executor) inputs(tx *wire.MsgTx, address btcutil.Address, outputAmount int64) (int64, []mempool.Utxo, error) {
func (e *Executor) inputs(tx *wire.MsgTx, address btcutil.Address, outputAmount uint64) (uint64, []mempool.Utxo, error) {
usedUtxos := make([]mempool.Utxo, 0)
inputAmount := int64(0)
inputAmount := uint64(0)
utxos, err := e.mempool.Utxos(address.String())
if err != nil {
return 0, nil, err
Expand All @@ -289,23 +306,24 @@ func (e *Executor) inputs(tx *wire.MsgTx, address btcutil.Address, outputAmount
tx.AddTxIn(txIn)

usedUtxos = append(usedUtxos, utxo)
inputAmount += int64(utxo.Value)
inputAmount += uint64(utxo.Value)
if inputAmount > outputAmount {
break
}
}
return inputAmount, usedUtxos, nil
}

func (e *Executor) fee(numOfInputs, numOfOutputs int64) (int64, error) {
func (e *Executor) fee(numOfInputs, numOfOutputs uint64) (uint64, error) {
recommendedFee, err := e.mempool.RecommendedFee()
if err != nil {
return 0, err
}
return (numOfInputs*int64(INPUT_SIZE) + numOfOutputs*int64(OUTPUT_SIZE)) * recommendedFee.EconomyFee, nil

return (numOfInputs*INPUT_SIZE + numOfOutputs*OUTPUT_SIZE) * ((recommendedFee.EconomyFee/FEE_ROUNDING_FACTOR)*FEE_ROUNDING_FACTOR + FEE_ROUNDING_FACTOR), nil
}

func (e *Executor) sendTx(tx *wire.MsgTx, signatures []taproot.Signature) (*chainhash.Hash, error) {
func (e *Executor) sendTx(tx *wire.MsgTx, signatures []taproot.Signature, messageID string) (*chainhash.Hash, error) {
for i, sig := range signatures {
tx.TxIn[i].Witness = wire.TxWitness{sig}
}
Expand All @@ -316,7 +334,7 @@ func (e *Executor) sendTx(tx *wire.MsgTx, signatures []taproot.Signature) (*chai
return nil, err
}
bytes := buf.Bytes()
log.Debug().Msgf("Assembled raw transaction %s", hex.EncodeToString(bytes))
log.Debug().Str("messageID", messageID).Msgf("Assembled raw transaction %s", hex.EncodeToString(bytes))
return e.conn.SendRawTransaction(tx, true)
}

Expand All @@ -330,7 +348,7 @@ func (e *Executor) signaturesFilled(signatures []taproot.Signature) bool {
return true
}

func (e *Executor) proposalsForExecution(proposals []*proposal.Proposal) ([]*BtcTransferProposal, error) {
func (e *Executor) proposalsForExecution(proposals []*proposal.Proposal, messageID string) ([]*BtcTransferProposal, error) {
e.propMutex.Lock()
props := make([]*BtcTransferProposal, 0)
for _, prop := range proposals {
Expand All @@ -340,7 +358,7 @@ func (e *Executor) proposalsForExecution(proposals []*proposal.Proposal) ([]*Btc
}

if executed {
log.Info().Msgf("Proposal %s already executed", fmt.Sprintf("%d-%d-%d", prop.Source, prop.Destination, prop.Data.(BtcTransferProposalData).DepositNonce))
log.Warn().Str("messageID", messageID).Msgf("Proposal %s already executed", fmt.Sprintf("%d-%d-%d", prop.Source, prop.Destination, prop.Data.(BtcTransferProposalData).DepositNonce))
continue
}

Expand Down
4 changes: 2 additions & 2 deletions chains/btc/executor/message-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

type BtcTransferProposalData struct {
Amount int64
Amount uint64
Recipient string
DepositNonce uint64
ResourceId [32]byte
Expand Down Expand Up @@ -64,7 +64,7 @@ func ERC20MessageHandler(msg *transfer.TransferMessage) (*proposal.Proposal, err
bigAmount.Div(bigAmount, divisor)

return proposal.NewProposal(msg.Source, msg.Destination, BtcTransferProposalData{
Amount: bigAmount.Int64(),
Amount: bigAmount.Uint64(),
Recipient: string(recipient),
DepositNonce: msg.Data.DepositNonce,
ResourceId: msg.Data.ResourceId,
Expand Down
3 changes: 2 additions & 1 deletion chains/btc/listener/deposit-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ func NewBtcDepositHandler() *BtcDepositHandler {
return &BtcDepositHandler{}
}

func (e *BtcDepositHandler) HandleDeposit(sourceID uint8,
func (e *BtcDepositHandler) HandleDeposit(
sourceID uint8,
depositNonce uint64,
resourceID [32]byte,
amount *big.Int,
Expand Down
16 changes: 10 additions & 6 deletions chains/btc/mempool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ type Utxo struct {
}

type Fee struct {
FastestFee int64
HalfHourFee int64
MinimumFee int64
EconomyFee int64
HourFee int64
FastestFee uint64
HalfHourFee uint64
MinimumFee uint64
EconomyFee uint64
HourFee uint64
}

type MempoolAPI struct {
Expand Down Expand Up @@ -77,7 +77,11 @@ func (a *MempoolAPI) Utxos(address string) ([]Utxo, error) {
return nil, err
}
sort.Slice(utxos, func(i int, j int) bool {
return utxos[i].Status.BlockTime < utxos[j].Status.BlockTime
if utxos[i].Status.BlockTime == utxos[j].Status.BlockTime {
return utxos[i].TxID < utxos[j].TxID
} else {
return utxos[i].Status.BlockTime < utxos[j].Status.BlockTime
}
})

return utxos, nil
Expand Down
11 changes: 11 additions & 0 deletions chains/btc/mempool/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ func (s *MempoolTestSuite) Test_Utxo_SuccessfulFetch() {
BlockTime: 1715083122,
},
},
{
TxID: "28154e2008912d27978225c096c22ffe2ea65e1d55bf440ee41c21f9489c7fe2",
Vout: 0,
Value: 11197,
Status: mempool.Status{
Confirmed: true,
BlockHeight: 2812826,
BlockHash: "000000000000001a01d4058773384f2c23aed5a7e5ede252f99e290fa58324a3",
BlockTime: 1715083122,
},
},
{
TxID: "28154e2008912d27978225c096c22ffe2ea65e1d55bf440ee41c21f9489c7fe9",
Vout: 0,
Expand Down
1 change: 1 addition & 0 deletions chains/btc/mempool/test-data/successful-utxo.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[
{"txid":"28154e2008912d27978225c096c22ffe2ea65e1d55bf440ee41c21f9489c7fe9","vout":0,"status":{"confirmed":true,"block_height":2812827,"block_hash":"000000000000001a01d4058773384f2c23aed5a7e5ede252f99e290fa58324a5","block_time":1715083123},"value":11198},
{"txid":"28154e2008912d27978225c096c22ffe2ea65e1d55bf440ee41c21f9489c7fe2","vout":0,"status":{"confirmed":true,"block_height":2812826,"block_hash":"000000000000001a01d4058773384f2c23aed5a7e5ede252f99e290fa58324a3","block_time":1715083122},"value":11197},
{"txid":"28154e2008912d27978225c096c22ffe2ea65e1d55bf440ee41c21f9489c7fe1","vout":0,"status":{"confirmed":true,"block_height":2812826,"block_hash":"000000000000001a01d4058773384f2c23aed5a7e5ede252f99e290fa58324a3","block_time":1715083122},"value":11197}
]
18 changes: 13 additions & 5 deletions chains/evm/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func (e *Executor) Execute(proposals []*proposal.Proposal) error {
if len(batch.proposals) == 0 {
continue
}
messageID := batch.proposals[0].MessageID

b := batch
p.Go(func() error {
Expand All @@ -95,13 +96,14 @@ func (e *Executor) Execute(proposals []*proposal.Proposal) error {
return err
}

sessionID := fmt.Sprintf("%s-%d", batch.proposals[0].MessageID, i)
sessionID := fmt.Sprintf("%s-%d", messageID, i)
log.Info().Str("messageID", batch.proposals[0].MessageID).Msgf("Starting session with ID: %s", sessionID)

msg := big.NewInt(0)
msg.SetBytes(propHash)
signing, err := signing.NewSigning(
msg,
messageID,
sessionID,
e.host,
e.comm,
Expand All @@ -122,14 +124,20 @@ func (e *Executor) Execute(proposals []*proposal.Proposal) error {

return err
})
ep.Go(func() error { return e.watchExecution(watchContext, cancelExecution, b, sigChn, sessionID) })
ep.Go(func() error { return e.watchExecution(watchContext, cancelExecution, b, sigChn, sessionID, messageID) })
return ep.Wait()
})
}
return p.Wait()
}

func (e *Executor) watchExecution(ctx context.Context, cancelExecution context.CancelFunc, batch *Batch, sigChn chan interface{}, sessionID string) error {
func (e *Executor) watchExecution(
ctx context.Context,
cancelExecution context.CancelFunc,
batch *Batch,
sigChn chan interface{},
sessionID string,
messageID string) error {
ticker := time.NewTicker(executionCheckPeriod)
timeout := time.NewTicker(signingTimeout)
defer ticker.Stop()
Expand All @@ -152,15 +160,15 @@ func (e *Executor) watchExecution(ctx context.Context, cancelExecution context.C
return err
}

log.Info().Str("messageID", sessionID).Msgf("Sent proposals execution with hash: %s", hash)
log.Info().Str("messageID", messageID).Msgf("Sent proposals execution with hash: %s", hash)
}
case <-ticker.C:
{
if !e.areProposalsExecuted(batch.proposals) {
continue
}

log.Info().Str("messageID", sessionID).Msgf("Successfully executed proposals")
log.Info().Str("messageID", messageID).Msgf("Successfully executed proposals")
return nil
}
case <-timeout.C:
Expand Down
5 changes: 3 additions & 2 deletions chains/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package chains

import (
"fmt"
"math/big"

"github.com/ChainSafe/sygma-relayer/relayer/transfer"
"github.com/ethereum/go-ethereum/common/hexutil"
Expand All @@ -17,8 +18,8 @@ func ProposalsHash(proposals []*transfer.TransferProposal, chainID int64, verifC
formattedProps := make([]interface{}, len(proposals))
for i, prop := range proposals {
formattedProps[i] = map[string]interface{}{
"originDomainID": math.NewHexOrDecimal256(int64(prop.Source)),
"depositNonce": math.NewHexOrDecimal256(int64(prop.Data.DepositNonce)),
"originDomainID": big.NewInt(int64(prop.Source)),
"depositNonce": new(big.Int).SetUint64(prop.Data.DepositNonce),
"resourceID": hexutil.Encode(prop.Data.ResourceId[:]),
"data": prop.Data.Data,
}
Expand Down
Loading
Loading