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

RelayedTxV3 builder #151

Open
wants to merge 10 commits into
base: rc/v1.7.next1
Choose a base branch
from
6 changes: 6 additions & 0 deletions builders/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ var ErrMissingGuardianOption = errors.New("guardian flag is missing in the optio

// ErrGuardianDoesNotMatch signals a mismatch between the configured guardian in tx and the signing guardian address
var ErrGuardianDoesNotMatch = errors.New("configured guardian does not match signing guardian")

// ErrEmptyRelayerOnInnerTransaction signals that the inner transaction has an empty relayer
var ErrEmptyRelayerOnInnerTransaction = errors.New("empty relayer on inner transaction")

// ErrEmptyInnerTransactions signals that the inner transactions are empty
var ErrEmptyInnerTransactions = errors.New("empty inner transactions")
16 changes: 9 additions & 7 deletions builders/relayedTxV1Builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,51 @@ import (
"math/big"

"github.com/multiversx/mx-chain-core-go/data/transaction"

"github.com/multiversx/mx-sdk-go/core"
"github.com/multiversx/mx-sdk-go/data"
)

type relayedTxV1Builder struct {
// RelayedTxV1Builder is a builder for relayed transaction v1
type RelayedTxV1Builder struct {
innerTransaction *transaction.FrontendTransaction
relayerAccount *data.Account
networkConfig *data.NetworkConfig
}

// NewRelayedTxV1Builder creates a new relayed transaction v1 builder
func NewRelayedTxV1Builder() *relayedTxV1Builder {
return &relayedTxV1Builder{
func NewRelayedTxV1Builder() *RelayedTxV1Builder {
return &RelayedTxV1Builder{
innerTransaction: nil,
relayerAccount: nil,
networkConfig: nil,
}
}

// SetInnerTransaction sets the inner transaction to be relayed
func (rtb *relayedTxV1Builder) SetInnerTransaction(tx *transaction.FrontendTransaction) *relayedTxV1Builder {
func (rtb *RelayedTxV1Builder) SetInnerTransaction(tx *transaction.FrontendTransaction) *RelayedTxV1Builder {
rtb.innerTransaction = tx

return rtb
}

// SetRelayerAccount sets the relayer account
func (rtb *relayedTxV1Builder) SetRelayerAccount(account *data.Account) *relayedTxV1Builder {
func (rtb *RelayedTxV1Builder) SetRelayerAccount(account *data.Account) *RelayedTxV1Builder {
rtb.relayerAccount = account

return rtb
}

// SetNetworkConfig sets the network config
func (rtb *relayedTxV1Builder) SetNetworkConfig(config *data.NetworkConfig) *relayedTxV1Builder {
func (rtb *RelayedTxV1Builder) SetNetworkConfig(config *data.NetworkConfig) *RelayedTxV1Builder {
rtb.networkConfig = config

return rtb
}

// Build builds the relayed transaction v1
// The returned transaction will not be signed
func (rtb *relayedTxV1Builder) Build() (*transaction.FrontendTransaction, error) {
func (rtb *RelayedTxV1Builder) Build() (*transaction.FrontendTransaction, error) {
if rtb.innerTransaction == nil {
return nil, ErrNilInnerTransaction
}
Expand Down
18 changes: 10 additions & 8 deletions builders/relayedTxV2Builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,59 @@ import (
"math/big"

"github.com/multiversx/mx-chain-core-go/data/transaction"

"github.com/multiversx/mx-sdk-go/core"
"github.com/multiversx/mx-sdk-go/data"
)

type relayedTxV2Builder struct {
// RelayedTxV2Builder is a builder for relayed transaction v2
type RelayedTxV2Builder struct {
innerTransaction *transaction.FrontendTransaction
gasLimitNeededForInnerTransaction uint64
relayerAccount *data.Account
networkConfig *data.NetworkConfig
}

// NewRelayedTxV2Builder creates a new relayed transaction v2 builder
func NewRelayedTxV2Builder() *relayedTxV2Builder {
return &relayedTxV2Builder{
func NewRelayedTxV2Builder() *RelayedTxV2Builder {
return &RelayedTxV2Builder{
innerTransaction: nil,
relayerAccount: nil,
networkConfig: nil,
}
}

// SetInnerTransaction sets the inner transaction to be relayed
func (rtb *relayedTxV2Builder) SetInnerTransaction(tx *transaction.FrontendTransaction) *relayedTxV2Builder {
func (rtb *RelayedTxV2Builder) SetInnerTransaction(tx *transaction.FrontendTransaction) *RelayedTxV2Builder {
rtb.innerTransaction = tx

return rtb
}

// SetRelayerAccount sets the relayer account (that will send the wrapped transaction)
func (rtb *relayedTxV2Builder) SetRelayerAccount(account *data.Account) *relayedTxV2Builder {
func (rtb *RelayedTxV2Builder) SetRelayerAccount(account *data.Account) *RelayedTxV2Builder {
rtb.relayerAccount = account

return rtb
}

// SetGasLimitNeededForInnerTransaction sets the gas limit needed for the inner transaction
func (rtb *relayedTxV2Builder) SetGasLimitNeededForInnerTransaction(gasLimit uint64) *relayedTxV2Builder {
func (rtb *RelayedTxV2Builder) SetGasLimitNeededForInnerTransaction(gasLimit uint64) *RelayedTxV2Builder {
rtb.gasLimitNeededForInnerTransaction = gasLimit

return rtb
}

// SetNetworkConfig sets the network config
func (rtb *relayedTxV2Builder) SetNetworkConfig(config *data.NetworkConfig) *relayedTxV2Builder {
func (rtb *RelayedTxV2Builder) SetNetworkConfig(config *data.NetworkConfig) *RelayedTxV2Builder {
rtb.networkConfig = config

return rtb
}

// Build builds the relayed transaction v1
// The returned transaction will not be signed
func (rtb *relayedTxV2Builder) Build() (*transaction.FrontendTransaction, error) {
func (rtb *RelayedTxV2Builder) Build() (*transaction.FrontendTransaction, error) {
if rtb.innerTransaction == nil {
return nil, ErrNilInnerTransaction
}
Expand Down
89 changes: 89 additions & 0 deletions builders/relayedTxV3Builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package builders

import (
"github.com/multiversx/mx-chain-core-go/data/transaction"

"github.com/multiversx/mx-sdk-go/data"
)

// RelayedTxV3Builder is a builder for relayed transactions v3
type RelayedTxV3Builder struct {
innerTransactions []*transaction.FrontendTransaction
relayerAccount *data.Account
networkConfig *data.NetworkConfig
}

// NewRelayedTxV3Builder creates a new relayed transaction v2 builder
func NewRelayedTxV3Builder() *RelayedTxV3Builder {
return &RelayedTxV3Builder{
innerTransactions: nil,
relayerAccount: nil,
networkConfig: nil,
}
}

// SetInnerTransactions sets the inner transactions to be relayed
func (rtb *RelayedTxV3Builder) SetInnerTransactions(innerTxs []*transaction.FrontendTransaction) *RelayedTxV3Builder {
rtb.innerTransactions = innerTxs

return rtb
}

// SetRelayerAccount sets the relayer account (that will send the wrapped transaction)
func (rtb *RelayedTxV3Builder) SetRelayerAccount(account *data.Account) *RelayedTxV3Builder {
rtb.relayerAccount = account

return rtb
}

// SetNetworkConfig sets the network config
func (rtb *RelayedTxV3Builder) SetNetworkConfig(config *data.NetworkConfig) *RelayedTxV3Builder {
rtb.networkConfig = config

return rtb
}

// Build builds the relayed transaction v3
// The returned transaction will not be signed
func (rtb *RelayedTxV3Builder) Build() (*transaction.FrontendTransaction, error) {
if len(rtb.innerTransactions) == 0 {
return nil, ErrEmptyInnerTransactions
}
innerTxsGasLimit := uint64(0)
for _, innerTx := range rtb.innerTransactions {
if len(innerTx.Signature) == 0 {
return nil, ErrNilInnerTransactionSignature
}
if len(innerTx.Relayer) == 0 {
return nil, ErrEmptyRelayerOnInnerTransaction
}

innerTxsGasLimit += innerTx.GasLimit
}

if rtb.relayerAccount == nil {
return nil, ErrNilRelayerAccount
}
if rtb.networkConfig == nil {
return nil, ErrNilNetworkConfig
}

minGasLimit := rtb.networkConfig.MinGasLimit
moveBalancesGas := minGasLimit * uint64(len(rtb.innerTransactions))
gasLimit := moveBalancesGas + innerTxsGasLimit

relayedTx := &transaction.FrontendTransaction{
Nonce: rtb.relayerAccount.Nonce,
Value: "0",
Receiver: rtb.relayerAccount.Address,
Sender: rtb.relayerAccount.Address,
GasPrice: rtb.innerTransactions[0].GasPrice,
GasLimit: gasLimit,
Data: []byte(""),
ChainID: rtb.networkConfig.ChainID,
Version: rtb.networkConfig.MinTransactionVersion,
InnerTransactions: rtb.innerTransactions,
}

return relayedTx, nil
}
139 changes: 139 additions & 0 deletions builders/relayedTxV3Builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package builders

import (
"encoding/hex"
"encoding/json"
"testing"

"github.com/multiversx/mx-chain-core-go/data/transaction"
"github.com/stretchr/testify/require"

"github.com/multiversx/mx-sdk-go/data"
)

func TestRelayedTxV3Builder(t *testing.T) {
t.Parallel()

netConfig := &data.NetworkConfig{
ChainID: "T",
MinTransactionVersion: 1,
GasPerDataByte: 1500,
MinGasLimit: 50000,
MinGasPrice: 1000000000,
}

relayerAcc, relayerPrivKey := getAccount(t, testRelayerMnemonic)
innerSenderAcc, innerSenderPrivKey := getAccount(t, testInnerSenderMnemonic)

innerTx := &transaction.FrontendTransaction{
Nonce: innerSenderAcc.Nonce,
Value: "100000000",
Receiver: "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
Sender: innerSenderAcc.Address,
GasPrice: netConfig.MinGasPrice,
GasLimit: netConfig.MinGasLimit,
ChainID: netConfig.ChainID,
Version: netConfig.MinTransactionVersion,
Relayer: relayerAcc.Address,
}

innerTxSig := signTx(t, innerSenderPrivKey, innerTx)
innerTx.Signature = hex.EncodeToString(innerTxSig)

innerTxs := []*transaction.FrontendTransaction{innerTx}

t.Run("should work", func(t *testing.T) {
t.Parallel()

innerTxsCopy := copySlice(innerTxs)
relayedV3Builder := NewRelayedTxV3Builder()
relayedV3Builder.SetInnerTransactions(innerTxsCopy)
relayedV3Builder.SetRelayerAccount(relayerAcc)
relayedV3Builder.SetNetworkConfig(netConfig)

relayedTx, err := relayedV3Builder.Build()
require.NoError(t, err)

relayedTxSig := signTx(t, relayerPrivKey, relayedTx)
relayedTx.Signature = hex.EncodeToString(relayedTxSig)

txJson, _ := json.Marshal(relayedTx)
require.Equal(t,
`{"nonce":37,"value":"0","receiver":"erd1h692scsz3um6e5qwzts4yjrewxqxwcwxzavl5n9q8sprussx8fqsu70jf5","sender":"erd1h692scsz3um6e5qwzts4yjrewxqxwcwxzavl5n9q8sprussx8fqsu70jf5",`+
`"gasPrice":1000000000,"gasLimit":100000,"signature":"7c1209358717816092d0dfd6b5448018048520d5af73233bd130276e1e621b3aabbc21bd6c0b501b073c97b3b11036416a0ce1e65280e2e6cebefab6ffab2c05",`+
`"chainID":"T","version":1,"innerTransactions":[{"nonce":37,"value":"100000000","receiver":"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",`+
`"sender":"erd1mlh7q3fcgrjeq0et65vaaxcw6m5ky8jhu296pdxpk9g32zga6uhsemxx2a","gasPrice":1000000000,"gasLimit":50000,`+
`"signature":"907f6dc73f2218c91180be9b027a513e92f669c36bc26300f90f5bf9d7729328eefd7e098e6fbcf34f6b3b13466ee6fb8918f956ca8efd543a8d2bdffc9d680f","chainID":"T","version":1,`+
`"relayer":"erd1h692scsz3um6e5qwzts4yjrewxqxwcwxzavl5n9q8sprussx8fqsu70jf5"}]}`,
string(txJson),
)
})
t.Run("nil inner txs should error", func(t *testing.T) {
t.Parallel()

relayedV3Builder := NewRelayedTxV3Builder()
relayedTx, err := relayedV3Builder.Build()
require.Equal(t, ErrEmptyInnerTransactions, err)
require.Nil(t, relayedTx)
})
t.Run("nil empty inner txs should error", func(t *testing.T) {
t.Parallel()

relayedV3Builder := NewRelayedTxV3Builder()
relayedV3Builder.SetInnerTransactions([]*transaction.FrontendTransaction{})
relayedTx, err := relayedV3Builder.Build()
require.Equal(t, ErrEmptyInnerTransactions, err)
require.Nil(t, relayedTx)
})
t.Run("empty inner tx signature should error", func(t *testing.T) {
t.Parallel()

innerTxsCopy := copySlice(innerTxs)
innerTxsCopy[0].Signature = ""
relayedV3Builder := NewRelayedTxV3Builder()
relayedV3Builder.SetInnerTransactions(innerTxsCopy)
relayedTx, err := relayedV3Builder.Build()
require.Equal(t, ErrNilInnerTransactionSignature, err)
require.Nil(t, relayedTx)
})
t.Run("empty inner tx relayer should error", func(t *testing.T) {
t.Parallel()

innerTxsCopy := copySlice(innerTxs)
innerTxsCopy[0].Relayer = ""
relayedV3Builder := NewRelayedTxV3Builder()
relayedV3Builder.SetInnerTransactions(innerTxsCopy)
relayedTx, err := relayedV3Builder.Build()
require.Equal(t, ErrEmptyRelayerOnInnerTransaction, err)
require.Nil(t, relayedTx)
})
t.Run("nil relayer account should error", func(t *testing.T) {
t.Parallel()

relayedV3Builder := NewRelayedTxV3Builder()
relayedV3Builder.SetInnerTransactions(innerTxs)
relayedTx, err := relayedV3Builder.Build()
require.Equal(t, ErrNilRelayerAccount, err)
require.Nil(t, relayedTx)
})
t.Run("nil network config account should error", func(t *testing.T) {
t.Parallel()

relayedV3Builder := NewRelayedTxV3Builder()
relayedV3Builder.SetInnerTransactions(innerTxs)
relayedV3Builder.SetRelayerAccount(relayerAcc)
relayedTx, err := relayedV3Builder.Build()
require.Equal(t, ErrNilNetworkConfig, err)
require.Nil(t, relayedTx)
})
}

func copySlice(oldSlice []*transaction.FrontendTransaction) []*transaction.FrontendTransaction {
newSlice := make([]*transaction.FrontendTransaction, 0, len(oldSlice))
for _, sliceEntry := range oldSlice {
entryCopy := *sliceEntry
newSlice = append(newSlice, &entryCopy)
}

return newSlice
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ require (
github.com/gin-contrib/cors v1.4.0
github.com/gin-gonic/gin v1.9.1
github.com/gorilla/websocket v1.5.0
github.com/multiversx/mx-chain-core-go v1.2.21-0.20240508071047-fefea5737840
github.com/multiversx/mx-chain-core-go v1.2.21-0.20240515142458-bb09ab417156
github.com/multiversx/mx-chain-crypto-go v1.2.12-0.20240508074452-cc21c1b505df
github.com/multiversx/mx-chain-go v1.7.11-0.20240515132333-ad05efdef13f
github.com/multiversx/mx-chain-go v1.7.11-0.20240517065041-9a3d0a26dbbd
github.com/multiversx/mx-chain-logger-go v1.0.15-0.20240508072523-3f00a726af57
github.com/multiversx/mx-chain-storage-go v1.0.16-0.20240508073549-dcb8e6e0370f
github.com/multiversx/mx-chain-vm-common-go v1.5.13-0.20240509103544-247ce5639c7a
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,13 @@ github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUY
github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o=
github.com/multiversx/mx-chain-communication-go v1.0.15-0.20240508074652-e128a1c05c8e h1:Tsmwhu+UleE+l3buPuqXSKTqfu5FbPmzQ4MjMoUvCWA=
github.com/multiversx/mx-chain-communication-go v1.0.15-0.20240508074652-e128a1c05c8e/go.mod h1:2yXl18wUbuV3cRZr7VHxM1xo73kTaC1WUcu2kx8R034=
github.com/multiversx/mx-chain-core-go v1.2.21-0.20240508071047-fefea5737840 h1:2mCrTUmbbA+Xv4UifZY9xptrGjcJBcJ2wavSb4FwejU=
github.com/multiversx/mx-chain-core-go v1.2.21-0.20240508071047-fefea5737840/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE=
github.com/multiversx/mx-chain-core-go v1.2.21-0.20240515142458-bb09ab417156 h1:Lzm7USVM1b6h1OsizXYjVOiqX9USwaOuNCegkcAlFJM=
github.com/multiversx/mx-chain-core-go v1.2.21-0.20240515142458-bb09ab417156/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE=
github.com/multiversx/mx-chain-crypto-go v1.2.12-0.20240508074452-cc21c1b505df h1:clihfi78bMEOWk/qw6WA4uQbCM2e2NGliqswLAvw19k=
github.com/multiversx/mx-chain-crypto-go v1.2.12-0.20240508074452-cc21c1b505df/go.mod h1:gtJYB4rR21KBSqJlazn+2z6f9gFSqQP3KvAgL7Qgxw4=
github.com/multiversx/mx-chain-es-indexer-go v1.7.1-0.20240509104512-25512675833d h1:GD1D8V0bE6hDLjrduSsMwQwwf6PMq2Zww7FYMfJsuiw=
github.com/multiversx/mx-chain-go v1.7.11-0.20240515132333-ad05efdef13f h1:4vza4oJk5FP53bPXY4gHFLSoQw5B0l01mqUYzcc/xxM=
github.com/multiversx/mx-chain-go v1.7.11-0.20240515132333-ad05efdef13f/go.mod h1:FTvyK57xgP95SwTU6Qje/OfA5tuJ3ol9GofyAgFRs8I=
github.com/multiversx/mx-chain-go v1.7.11-0.20240517065041-9a3d0a26dbbd h1:G0jTEQmU11Ef7TKWe9Z7HkGJYHfQ5+PwqdzA8ccnL80=
github.com/multiversx/mx-chain-go v1.7.11-0.20240517065041-9a3d0a26dbbd/go.mod h1:mdZaZISK+/erBbZUBx2Gcd41PskE+TLEww2odTCypYQ=
github.com/multiversx/mx-chain-logger-go v1.0.15-0.20240508072523-3f00a726af57 h1:g9t410dqjcb7UUptbVd/H6Ua12sEzWU4v7VplyNvRZ0=
github.com/multiversx/mx-chain-logger-go v1.0.15-0.20240508072523-3f00a726af57/go.mod h1:cY6CIXpndW5g5PTPn4WzPwka/UBEf+mgw+PSY5pHGAU=
github.com/multiversx/mx-chain-scenario-go v1.4.4-0.20240509103754-9e8129721f00 h1:hFEcbGBtXu8UyB9BMhmAIH2R8BtV/NOq/rsxespLCN8=
Expand Down
Loading