Skip to content

Commit

Permalink
feat: refactor refund user pegout script
Browse files Browse the repository at this point in the history
 - add tests
  • Loading branch information
AndresQuijano committed Nov 21, 2024
1 parent d7ed917 commit 313b25c
Show file tree
Hide file tree
Showing 31 changed files with 280 additions and 100 deletions.
81 changes: 11 additions & 70 deletions cmd/utils/refund_user_pegout/refund_user_pegout.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,14 @@ import (
"context"
"flag"
"fmt"
"os"
"syscall"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/go-playground/validator/v10"
"github.com/rsksmart/liquidity-provider-server/cmd/utils/defaults"
"github.com/rsksmart/liquidity-provider-server/internal/adapters/dataproviders/rootstock"
"github.com/rsksmart/liquidity-provider-server/internal/adapters/dataproviders/rootstock/bindings"
"github.com/rsksmart/liquidity-provider-server/cmd/utils/scripts"
"github.com/rsksmart/liquidity-provider-server/internal/configuration/bootstrap"
"github.com/rsksmart/liquidity-provider-server/internal/configuration/bootstrap/wallet"
"github.com/rsksmart/liquidity-provider-server/internal/configuration/environment"
"github.com/rsksmart/liquidity-provider-server/internal/configuration/environment/secrets"
"github.com/rsksmart/liquidity-provider-server/internal/entities/blockchain"
"golang.org/x/term"
)

Expand All @@ -36,46 +31,24 @@ type RefundUserPegOutScriptInput struct {
type PasswordReader = func(int) ([]byte, error)

func main() {
ctx := context.Background()

scriptInput := new(RefundUserPegOutScriptInput)
ReadRefundUserPegOutScriptInput(scriptInput)
env, err := ParseRefundUserPegOutScriptInput(scriptInput, term.ReadPassword)
if err != nil {
ExitWithError(2, "Error reading input", err)
}

rskClient, err := bootstrap.Rootstock(ctx, env.Rsk)
if err != nil {
ExitWithError(2, "Error connecting to RSK node", err)
}
rskWallet, err := GetWallet(ctx, env, rskClient)
if err != nil {
ExitWithError(2, "Error accessing to wallet", err)
scripts.ExitWithError(2, "Error reading input", err)
}

err = ExecuteRefundUserPegOut(ctx, env, rskWallet, rskClient, common.HexToHash(scriptInput.QuoteHashBytes))
ctx := context.Background()
lbc, err := scripts.CreateLiquidityBridgeContract(ctx, bootstrap.Rootstock, env)
if err != nil {
ExitWithError(2, "Error on transaction execution", err)
scripts.ExitWithError(2, "Error accessing the Liquidity Bridge Contract", err)
}
}

func GetWallet(
ctx context.Context,
env environment.Environment,
rskClient *rootstock.RskClient,
) (rootstock.RskSignerWallet, error) {
secretLoader, err := secrets.GetSecretLoader(ctx, env)
txHash, err := ExecuteRefundUserPegOut(lbc, scriptInput.QuoteHashBytes)
if err != nil {
return nil, err
scripts.ExitWithError(2, "Error on transaction execution", err)
}
walletFactory, err := wallet.NewFactory(env, wallet.FactoryCreationArgs{
Ctx: ctx, Env: env, SecretLoader: secretLoader, RskClient: rskClient,
})
if err != nil {
return nil, err
}
return walletFactory.RskWallet()
fmt.Println("Refund user peg out executed successfully. Transaction hash: ", txHash)
}

func ReadRefundUserPegOutScriptInput(scriptInput *RefundUserPegOutScriptInput) {
Expand Down Expand Up @@ -142,38 +115,6 @@ func ParseRefundUserPegOutScriptInput(scriptInput *RefundUserPegOutScriptInput,
return env, nil
}

func ExecuteRefundUserPegOut(
ctx context.Context,
env environment.Environment,
rskWallet rootstock.RskSignerWallet,
rskClient *rootstock.RskClient,
quoteHashBytes common.Hash,
) error {
lbc, err := bindings.NewLiquidityBridgeContract(common.HexToAddress(env.Rsk.LbcAddress), rskClient.Rpc())
if err != nil {
return err
}

opts := &bind.TransactOpts{From: rskWallet.Address(), Signer: rskWallet.Sign}
tx, err := lbc.RefundUserPegOut(opts, quoteHashBytes)
if err != nil {
return err
}

receipt, err := bind.WaitMined(ctx, rskClient.Rpc(), tx)
if err != nil {
return err
}

if receipt.Status == 1 {
fmt.Println("Refund user peg out executed successfully. Transaction hash: ", receipt.TxHash.Hex())
return nil
} else {
return fmt.Errorf("transaction %s failed", receipt.TxHash.Hex())
}
}

func ExitWithError(code int, message string, err error) {
fmt.Println(fmt.Sprintf("%s: %s", message, err.Error()))
os.Exit(code)
func ExecuteRefundUserPegOut(lbc blockchain.LiquidityBridgeContract, quoteHash string) (string, error) {
return lbc.RefundUserPegOut(quoteHash)
}
90 changes: 90 additions & 0 deletions cmd/utils/refund_user_pegout/refund_user_pegout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package main

import (
"flag"
"testing"

"github.com/rsksmart/liquidity-provider-server/test"
"github.com/rsksmart/liquidity-provider-server/test/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/term"
)

func TestReadRefundUserPegOutScriptInput(t *testing.T) {
t.Run("should set flag values", func(t *testing.T) {
// Reset flags before test
flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)

scriptInput := new(RefundUserPegOutScriptInput)
ReadRefundUserPegOutScriptInput(scriptInput)

// Set test values
err := flag.CommandLine.Parse([]string{
"-network", "regtest",
"-quote-hash", "d93f58c82100a6cee4f19ac505c11d51b52cafe220f7f1944b70496f33d277fc",
"-rsk-endpoint", "http://localhost:4444",
"-secret-src", "env",
"-keystore-file", "./keystore.json",
})
require.NoError(t, err)

assert.Equal(t, "regtest", scriptInput.Network)
assert.Equal(t, "d93f58c82100a6cee4f19ac505c11d51b52cafe220f7f1944b70496f33d277fc", scriptInput.QuoteHashBytes)
assert.Equal(t, "http://localhost:4444", scriptInput.RskEndpoint)
assert.Equal(t, "env", scriptInput.SecretSource)
assert.Equal(t, "./keystore.json", scriptInput.KeystoreFile)
})
}

func TestParseRefundUserPegOutScriptInput(t *testing.T) {
t.Run("should validate required fields", func(t *testing.T) {
scriptInput := &RefundUserPegOutScriptInput{
Network: "",
QuoteHashBytes: "",
RskEndpoint: "",
SecretSource: "",
}

_, err := ParseRefundUserPegOutScriptInput(scriptInput, term.ReadPassword)
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid input")
})

t.Run("should parse valid input", func(t *testing.T) {
scriptInput := &RefundUserPegOutScriptInput{
Network: "regtest",
QuoteHashBytes: "d93f58c82100a6cee4f19ac505c11d51b52cafe220f7f1944b70496f33d277fc",
RskEndpoint: "http://localhost:4444",
SecretSource: "aws",
AwsLocalEndpoint: "http://localhost:4566",
}

env, err := ParseRefundUserPegOutScriptInput(scriptInput, func(fd int) ([]byte, error) {
return []byte("password"), nil
})
require.NoError(t, err)
assert.Equal(t, "regtest", env.LpsStage)
assert.Equal(t, "http://localhost:4444", env.Rsk.Endpoint)
assert.Equal(t, "aws", env.SecretSource)
assert.Equal(t, "http://localhost:4566", env.AwsLocalEndpoint)
})
}

func TestRefundUserPegOut(t *testing.T) {
t.Run("should execute refund user peg out successfully", func(t *testing.T) {
lbc := &mocks.LbcMock{}
quoteHash := "d93f58c82100a6cee4f19ac505c11d51b52cafe220f7f1944b70496f33d277fc"
expectedTxHash := test.AnyHash

// Setup mock expectations
lbc.On("RefundUserPegOut", quoteHash).Return(expectedTxHash, nil)

txHash, err := ExecuteRefundUserPegOut(lbc, quoteHash)
require.NoError(t, err)
assert.Equal(t, expectedTxHash, txHash)

// Verify all expectations were met
lbc.AssertExpectations(t)
})
}
4 changes: 3 additions & 1 deletion internal/adapters/dataproviders/rootstock/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package rootstock

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/rsksmart/liquidity-provider-server/internal/adapters/dataproviders/rootstock/bindings"
"math/big"
)

type RpcClientBinding interface {
Expand Down Expand Up @@ -70,6 +71,7 @@ type LbcBinding interface {
ProductFeePercentage(opts *bind.CallOpts) (*big.Int, error)
IsPegOutQuoteCompleted(opts *bind.CallOpts, quoteHash [32]byte) (bool, error)
UpdateProvider(opts *bind.TransactOpts, _name string, _url string) (*types.Transaction, error)
RefundUserPegOut(opts *bind.TransactOpts, quoteHash [32]byte) (*types.Transaction, error)
}

type LbcAdapter interface {
Expand Down
27 changes: 24 additions & 3 deletions internal/adapters/dataproviders/rootstock/lbc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"strings"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
geth "github.com/ethereum/go-ethereum/core/types"
Expand All @@ -15,9 +19,6 @@ import (
"github.com/rsksmart/liquidity-provider-server/internal/entities/liquidity_provider"
"github.com/rsksmart/liquidity-provider-server/internal/entities/quote"
log "github.com/sirupsen/logrus"
"math/big"
"strings"
"time"
)

// registerPeginGasLimit Fixed gas limit for registerPegin function, should change only if the function does
Expand Down Expand Up @@ -612,6 +613,26 @@ func (lbc *liquidityBridgeContractImpl) UpdateProvider(name, url string) (string
return receipt.TxHash.String(), nil
}

func (lbc *liquidityBridgeContractImpl) RefundUserPegOut(quoteHash string) (string, error) {
opts := &bind.TransactOpts{
From: lbc.signer.Address(),
Signer: lbc.signer.Sign,
}
receipt, err := awaitTx(lbc.client, "RefundUserPegOut", func() (*geth.Transaction, error) {
return lbc.contract.RefundUserPegOut(opts, common.HexToHash(quoteHash))
})

if err != nil {
return "", fmt.Errorf("refund user peg out error: %w", err)
} else if receipt == nil {
return "", errors.New("refund user peg out error: incomplete receipt")
} else if receipt.Status == 0 {
txHash := receipt.TxHash.String()
return txHash, fmt.Errorf("refund user peg out error: transaction reverted (%s)", txHash)
}
return receipt.TxHash.String(), nil
}

// parsePeginQuote parses a quote.PeginQuote into a bindings.QuotesPeginQuote. All BTC address fields support all address types
// except for FedBtcAddress which must be a P2SH address.
func parsePeginQuote(peginQuote quote.PeginQuote) (bindings.QuotesPeginQuote, error) {
Expand Down
4 changes: 3 additions & 1 deletion internal/entities/blockchain/lbc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"context"
"encoding/hex"
"fmt"
"math/big"

"github.com/rsksmart/liquidity-provider-server/internal/entities"
"github.com/rsksmart/liquidity-provider-server/internal/entities/liquidity_provider"
"github.com/rsksmart/liquidity-provider-server/internal/entities/quote"
"math/big"
)

const (
Expand Down Expand Up @@ -100,6 +101,7 @@ type LiquidityBridgeContract interface {
GetPeginPunishmentEvents(ctx context.Context, fromBlock uint64, toBlock *uint64) ([]liquidity_provider.PunishmentEvent, error)
IsPegOutQuoteCompleted(quoteHash string) (bool, error)
UpdateProvider(name, url string) (string, error)
RefundUserPegOut(quoteHash string) (string, error)
}

type FeeCollector interface {
Expand Down
2 changes: 1 addition & 1 deletion test/mocks/abstract_factory_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/bitcoin_wallet_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/client_adapter_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/collection_binding_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/db_binding_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/db_client_binding_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/default_credentials_provider_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/event_iterator_adapter_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/mocks/http_client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 313b25c

Please sign in to comment.