feat: added functionality to ask a client to sign a transaction and an endpoint to continue sending using provided signature
This functionality is needed in case the user wants to send a transaction and signs it using the signature provided by the keycard (or any other compatible way).
This commit is contained in:
parent
abac55c778
commit
b348cca15c
|
@ -1897,7 +1897,7 @@ func (b *GethStatusBackend) SendTransactionWithChainID(chainID uint64, sendArgs
|
|||
}
|
||||
|
||||
func (b *GethStatusBackend) SendTransactionWithSignature(sendArgs transactions.SendTxArgs, sig []byte) (hash types.Hash, err error) {
|
||||
hash, err = b.transactor.SendTransactionWithSignature(sendArgs, sig)
|
||||
hash, err = b.transactor.SendTransactionWithSignature(b.transactor.NetworkID(), sendArgs, sig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -2,7 +2,7 @@ module github.com/status-im/status-go
|
|||
|
||||
go 1.19
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.7
|
||||
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.9
|
||||
|
||||
replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1
|
||||
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1982,8 +1982,8 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
|
|||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/status-im/doubleratchet v3.0.0+incompatible h1:aJ1ejcSERpSzmWZBgtfYtiU2nF0Q8ZkGyuEPYETXkCY=
|
||||
github.com/status-im/doubleratchet v3.0.0+incompatible/go.mod h1:1sqR0+yhiM/bd+wrdX79AOt2csZuJOni0nUDzKNuqOU=
|
||||
github.com/status-im/go-ethereum v1.10.25-status.7 h1:egCCdvUQSdfnnDcv2vP1RCuIDr184eYlBvyucxuKJj8=
|
||||
github.com/status-im/go-ethereum v1.10.25-status.7/go.mod h1:Dt4K5JYMhJRdtXJwBEyGZLZn9iz/chSOZyjVmt5ZhwQ=
|
||||
github.com/status-im/go-ethereum v1.10.25-status.9 h1:NDuRs5TC4JjqPcYE8/sUtspdA+OwV1JRy3bbRLdIcL0=
|
||||
github.com/status-im/go-ethereum v1.10.25-status.9/go.mod h1:Dt4K5JYMhJRdtXJwBEyGZLZn9iz/chSOZyjVmt5ZhwQ=
|
||||
github.com/status-im/go-multiaddr-ethv4 v1.2.5 h1:pN+ey6wYKbvNNu5/xq9+VL0N8Yq0pZUTbZp0URg+Yn4=
|
||||
github.com/status-im/go-multiaddr-ethv4 v1.2.5/go.mod h1:Fhe/18yWU5QwlAYiOO3Bb1BLe0bn5YobcNBHsjRr4kk=
|
||||
github.com/status-im/go-sqlcipher/v4 v4.5.4-status.2 h1:Oi9JTAI2DZEe5UKlpUcvKBCCSn3ULsLIrix7jPnEoPE=
|
||||
|
|
|
@ -553,6 +553,11 @@ func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionComm
|
|||
return api.s.transactionManager.CreateMultiTransactionFromCommand(ctx, multiTransactionCommand, data, api.router.bridges, password)
|
||||
}
|
||||
|
||||
func (api *API) ProceedWithTransactionsSignatures(ctx context.Context, signatures map[string]transfer.SignatureDetails) (*transfer.MultiTransactionCommandResult, error) {
|
||||
log.Debug("[WalletAPI:: ProceedWithTransactionsSignatures] sign with signatures and send multi transaction")
|
||||
return api.s.transactionManager.ProceedWithTransactionsSignatures(ctx, signatures)
|
||||
}
|
||||
|
||||
func (api *API) GetMultiTransactions(ctx context.Context, transactionIDs []transfer.MultiTransactionIDType) ([]*transfer.MultiTransaction, error) {
|
||||
log.Debug("wallet.api.GetMultiTransactions", "IDs.len", len(transactionIDs))
|
||||
return api.s.transactionManager.GetMultiTransactions(ctx, transactionIDs)
|
||||
|
|
|
@ -94,4 +94,5 @@ type Bridge interface {
|
|||
CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error)
|
||||
Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
|
||||
GetContractAddress(network *params.Network, token *token.Token) *common.Address
|
||||
BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
@ -244,33 +245,32 @@ func (s *CBridge) GetContractAddress(network *params.Network, token *token.Token
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *CBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
|
||||
func (s *CBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (*ethTypes.Transaction, error) {
|
||||
fromNetwork := s.rpcClient.NetworkManager.Find(sendArgs.ChainID)
|
||||
if fromNetwork == nil {
|
||||
return types.HexToHash(""), errors.New("network not found")
|
||||
return nil, errors.New("network not found")
|
||||
}
|
||||
tk := s.tokenManager.FindToken(fromNetwork, sendArgs.CbridgeTx.Symbol)
|
||||
if tk == nil {
|
||||
return types.HexToHash(""), errors.New("token not found")
|
||||
return nil, errors.New("token not found")
|
||||
}
|
||||
addrs := s.GetContractAddress(fromNetwork, nil)
|
||||
if addrs == nil {
|
||||
return types.HexToHash(""), errors.New("contract not found")
|
||||
return nil, errors.New("contract not found")
|
||||
}
|
||||
|
||||
backend, err := s.rpcClient.EthClient(sendArgs.ChainID)
|
||||
if err != nil {
|
||||
return types.HexToHash(""), err
|
||||
return nil, err
|
||||
}
|
||||
contract, err := celer.NewCeler(*addrs, backend)
|
||||
if err != nil {
|
||||
return types.HexToHash(""), err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txOpts := sendArgs.CbridgeTx.ToTransactOpts(getSigner(sendArgs.ChainID, sendArgs.CbridgeTx.From, verifiedAccount))
|
||||
var tx *ethTypes.Transaction
|
||||
txOpts := sendArgs.CbridgeTx.ToTransactOpts(signerFn)
|
||||
if tk.IsNative() {
|
||||
tx, err = contract.SendNative(
|
||||
return contract.SendNative(
|
||||
txOpts,
|
||||
sendArgs.CbridgeTx.Recipient,
|
||||
(*big.Int)(sendArgs.CbridgeTx.Amount),
|
||||
|
@ -278,8 +278,9 @@ func (s *CBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.Sel
|
|||
uint64(time.Now().UnixMilli()),
|
||||
500,
|
||||
)
|
||||
} else {
|
||||
tx, err = contract.Send(
|
||||
}
|
||||
|
||||
return contract.Send(
|
||||
txOpts,
|
||||
sendArgs.CbridgeTx.Recipient,
|
||||
tk.Address,
|
||||
|
@ -289,6 +290,9 @@ func (s *CBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.Sel
|
|||
500,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *CBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
|
||||
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.CbridgeTx.From, verifiedAccount))
|
||||
if err != nil {
|
||||
return types.HexToHash(""), err
|
||||
}
|
||||
|
@ -296,6 +300,10 @@ func (s *CBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.Sel
|
|||
return types.Hash(tx.Hash()), nil
|
||||
}
|
||||
|
||||
func (s *CBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
|
||||
return s.sendOrBuild(sendArgs, nil)
|
||||
}
|
||||
|
||||
func (s *CBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
|
||||
amt, err := s.estimateAmt(from, to, amountIn, symbol)
|
||||
if err != nil {
|
||||
|
|
|
@ -3,8 +3,10 @@ package bridge
|
|||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts/community-tokens/collectibles"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
|
@ -74,32 +76,43 @@ func (s *ERC721TransferBridge) EstimateGas(from, to *params.Network, account com
|
|||
return 80000, nil
|
||||
}
|
||||
|
||||
func (s *ERC721TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
|
||||
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
|
||||
contract, err := collectibles.NewCollectibles(common.Address(*sendArgs.ERC721TransferTx.To), ethClient)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
|
||||
nonce, unlock, err := s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC721TransferTx.From)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
defer func() {
|
||||
unlock(err == nil, nonce)
|
||||
}()
|
||||
argNonce := hexutil.Uint64(nonce)
|
||||
sendArgs.ERC721TransferTx.Nonce = &argNonce
|
||||
txOpts := sendArgs.ERC721TransferTx.ToTransactOpts(getSigner(sendArgs.ChainID, sendArgs.ERC721TransferTx.From, verifiedAccount))
|
||||
tx, err := contract.SafeTransferFrom(txOpts, common.Address(sendArgs.ERC721TransferTx.From), sendArgs.ERC721TransferTx.Recipient, sendArgs.ERC721TransferTx.TokenID.ToInt())
|
||||
txOpts := sendArgs.ERC721TransferTx.ToTransactOpts(signerFn)
|
||||
return contract.SafeTransferFrom(txOpts, common.Address(sendArgs.ERC721TransferTx.From), sendArgs.ERC721TransferTx.Recipient,
|
||||
sendArgs.ERC721TransferTx.TokenID.ToInt())
|
||||
}
|
||||
|
||||
func (s *ERC721TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC721TransferTx.From, verifiedAccount))
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
return types.Hash(tx.Hash()), nil
|
||||
}
|
||||
|
||||
func (s *ERC721TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
|
||||
return s.sendOrBuild(sendArgs, nil)
|
||||
}
|
||||
|
||||
func (s *ERC721TransferBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
|
||||
return amountIn, nil
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
"github.com/status-im/status-go/contracts/hop"
|
||||
|
@ -173,15 +174,15 @@ func (h *HopBridge) GetContractAddress(network *params.Network, token *token.Tok
|
|||
return &address
|
||||
}
|
||||
|
||||
func (h *HopBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
func (h *HopBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
|
||||
fromNetwork := h.contractMaker.RPCClient.NetworkManager.Find(sendArgs.ChainID)
|
||||
if fromNetwork == nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
|
||||
nonce, unlock, err := h.transactor.NextNonce(h.contractMaker.RPCClient, sendArgs.ChainID, sendArgs.HopTx.From)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
defer func() {
|
||||
unlock(err == nil, nonce)
|
||||
|
@ -191,25 +192,37 @@ func (h *HopBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.S
|
|||
|
||||
token := h.tokenManager.FindToken(fromNetwork, sendArgs.HopTx.Symbol)
|
||||
if fromNetwork.Layer == 1 {
|
||||
hash, err = h.sendToL2(sendArgs.ChainID, sendArgs.HopTx, verifiedAccount, token)
|
||||
return hash, err
|
||||
tx, err = h.sendToL2(sendArgs.ChainID, sendArgs.HopTx, signerFn, token)
|
||||
return tx, err
|
||||
}
|
||||
hash, err = h.swapAndSend(sendArgs.ChainID, sendArgs.HopTx, verifiedAccount, token)
|
||||
return hash, err
|
||||
tx, err = h.swapAndSend(sendArgs.ChainID, sendArgs.HopTx, signerFn, token)
|
||||
return tx, err
|
||||
}
|
||||
|
||||
func (h *HopBridge) sendToL2(chainID uint64, hopArgs *HopTxArgs, verifiedAccount *account.SelectedExtKey, token *token.Token) (hash types.Hash, err error) {
|
||||
func (h *HopBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
tx, err := h.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.HopTx.From, verifiedAccount))
|
||||
if err != nil {
|
||||
return types.Hash{}, err
|
||||
}
|
||||
return types.Hash(tx.Hash()), nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
|
||||
return h.sendOrBuild(sendArgs, nil)
|
||||
}
|
||||
|
||||
func (h *HopBridge) sendToL2(chainID uint64, hopArgs *HopTxArgs, signerFn bind.SignerFn, token *token.Token) (tx *ethTypes.Transaction, err error) {
|
||||
bridge, err := h.contractMaker.NewHopL1Bridge(chainID, hopArgs.Symbol)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
txOpts := hopArgs.ToTransactOpts(getSigner(chainID, hopArgs.From, verifiedAccount))
|
||||
txOpts := hopArgs.ToTransactOpts(signerFn)
|
||||
if token.IsNative() {
|
||||
txOpts.Value = (*big.Int)(hopArgs.Amount)
|
||||
}
|
||||
now := time.Now()
|
||||
deadline := big.NewInt(now.Unix() + 604800)
|
||||
tx, err := bridge.SendToL2(
|
||||
tx, err = bridge.SendToL2(
|
||||
txOpts,
|
||||
big.NewInt(int64(hopArgs.ChainID)),
|
||||
hopArgs.Recipient,
|
||||
|
@ -220,25 +233,22 @@ func (h *HopBridge) sendToL2(chainID uint64, hopArgs *HopTxArgs, verifiedAccount
|
|||
big.NewInt(0),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
return types.Hash(tx.Hash()), nil
|
||||
return tx, err
|
||||
}
|
||||
|
||||
func (h *HopBridge) swapAndSend(chainID uint64, hopArgs *HopTxArgs, verifiedAccount *account.SelectedExtKey, token *token.Token) (hash types.Hash, err error) {
|
||||
func (h *HopBridge) swapAndSend(chainID uint64, hopArgs *HopTxArgs, signerFn bind.SignerFn, token *token.Token) (tx *ethTypes.Transaction, err error) {
|
||||
ammWrapper, err := h.contractMaker.NewHopL2AmmWrapper(chainID, hopArgs.Symbol)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
|
||||
txOpts := hopArgs.ToTransactOpts(getSigner(chainID, hopArgs.From, verifiedAccount))
|
||||
txOpts := hopArgs.ToTransactOpts(signerFn)
|
||||
if token.IsNative() {
|
||||
txOpts.Value = (*big.Int)(hopArgs.Amount)
|
||||
}
|
||||
now := time.Now()
|
||||
deadline := big.NewInt(now.Unix() + 604800)
|
||||
tx, err := ammWrapper.SwapAndSend(
|
||||
tx, err = ammWrapper.SwapAndSend(
|
||||
txOpts,
|
||||
big.NewInt(int64(hopArgs.ChainID)),
|
||||
hopArgs.Recipient,
|
||||
|
@ -250,11 +260,7 @@ func (h *HopBridge) swapAndSend(chainID uint64, hopArgs *HopTxArgs, verifiedAcco
|
|||
deadline,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
return types.Hash(tx.Hash()), nil
|
||||
return tx, err
|
||||
}
|
||||
|
||||
// CalculateBonderFees logics come from: https://docs.hop.exchange/fee-calculation
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
|
@ -44,6 +45,10 @@ func (s *TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *acco
|
|||
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
|
||||
}
|
||||
|
||||
func (s *TransferBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) {
|
||||
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx)
|
||||
}
|
||||
|
||||
func (s *TransferBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
|
||||
return amountIn, nil
|
||||
}
|
||||
|
|
|
@ -715,7 +715,7 @@ func TestFindBlocksCommand(t *testing.T) {
|
|||
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil}
|
||||
tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
|
||||
wdb := NewDB(db)
|
||||
tc := &TestClient{
|
||||
|
|
|
@ -3,17 +3,21 @@ package transfer
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
|
@ -21,6 +25,7 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
"github.com/status-im/status-go/signal"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
|
@ -33,6 +38,18 @@ const (
|
|||
EventMTTransactionUpdate walletevent.EventType = "multi-transaction-update"
|
||||
)
|
||||
|
||||
type SignatureDetails struct {
|
||||
R string `json:"r"`
|
||||
S string `json:"s"`
|
||||
V string `json:"v"`
|
||||
}
|
||||
|
||||
type TransactionDescription struct {
|
||||
chainID uint64
|
||||
builtTx *ethTypes.Transaction
|
||||
signature []byte
|
||||
}
|
||||
|
||||
type TransactionManager struct {
|
||||
db *sql.DB
|
||||
gethManager *account.GethManager
|
||||
|
@ -41,6 +58,10 @@ type TransactionManager struct {
|
|||
accountsDB *accounts.Database
|
||||
pendingTracker *transactions.PendingTxTracker
|
||||
eventFeed *event.Feed
|
||||
|
||||
multiTransactionForKeycardSigning *MultiTransaction
|
||||
transactionsBridgeData []*bridge.TransactionBridge
|
||||
transactionsForKeycardSingning map[common.Hash]*TransactionDescription
|
||||
}
|
||||
|
||||
func NewTransactionManager(
|
||||
|
@ -271,6 +292,7 @@ func (tm *TransactionManager) UpdateMultiTransaction(multiTransaction *MultiTran
|
|||
return updateMultiTransaction(tm.db, multiTransaction)
|
||||
}
|
||||
|
||||
// In case of keycard account, password should be empty
|
||||
func (tm *TransactionManager) CreateMultiTransactionFromCommand(ctx context.Context, command *MultiTransactionCommand,
|
||||
data []*bridge.TransactionBridge, bridges map[string]bridge.Bridge, password string) (*MultiTransactionCommandResult, error) {
|
||||
|
||||
|
@ -286,6 +308,33 @@ func (tm *TransactionManager) CreateMultiTransactionFromCommand(ctx context.Cont
|
|||
}
|
||||
|
||||
multiTransaction.ID = uint(multiTransactionID)
|
||||
if password == "" {
|
||||
acc, err := tm.accountsDB.GetAccountByAddress(types.Address(multiTransaction.FromAddress))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kp, err := tm.accountsDB.GetKeypairByKeyUID(acc.KeyUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !kp.MigratedToKeycard() {
|
||||
return nil, fmt.Errorf("account being used is not migrated to a keycard, password is required")
|
||||
}
|
||||
|
||||
tm.multiTransactionForKeycardSigning = multiTransaction
|
||||
tm.transactionsBridgeData = data
|
||||
hashes, err := tm.buildTransactions(bridges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signal.SendTransactionsForSigningEvent(hashes)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
hashes, err := tm.sendTransactions(multiTransaction, data, bridges, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -302,6 +351,61 @@ func (tm *TransactionManager) CreateMultiTransactionFromCommand(ctx context.Cont
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) ProceedWithTransactionsSignatures(ctx context.Context, signatures map[string]SignatureDetails) (*MultiTransactionCommandResult, error) {
|
||||
if tm.multiTransactionForKeycardSigning == nil {
|
||||
return nil, errors.New("no multi transaction to proceed with")
|
||||
}
|
||||
if len(tm.transactionsBridgeData) == 0 {
|
||||
return nil, errors.New("no transactions bridge data to proceed with")
|
||||
}
|
||||
if len(tm.transactionsForKeycardSingning) == 0 {
|
||||
return nil, errors.New("no transactions to proceed with")
|
||||
}
|
||||
if len(signatures) != len(tm.transactionsForKeycardSingning) {
|
||||
return nil, errors.New("not all transactions have been signed")
|
||||
}
|
||||
|
||||
// check if all transactions have been signed
|
||||
for hash, desc := range tm.transactionsForKeycardSingning {
|
||||
sigDetails, ok := signatures[hash.String()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing signature for transaction %s", hash)
|
||||
}
|
||||
|
||||
rBytes, _ := hex.DecodeString(sigDetails.R)
|
||||
sBytes, _ := hex.DecodeString(sigDetails.S)
|
||||
vByte := byte(0)
|
||||
if sigDetails.V == "1" {
|
||||
vByte = 1
|
||||
}
|
||||
|
||||
desc.signature = make([]byte, crypto.SignatureLength)
|
||||
copy(desc.signature[32-len(rBytes):32], rBytes)
|
||||
copy(desc.signature[64-len(rBytes):64], sBytes)
|
||||
desc.signature[64] = vByte
|
||||
}
|
||||
|
||||
// send transactions
|
||||
hashes := make(map[uint64][]types.Hash)
|
||||
for _, desc := range tm.transactionsForKeycardSingning {
|
||||
hash, err := tm.transactor.SendBuiltTransactionWithSignature(desc.chainID, desc.builtTx, desc.signature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashes[desc.chainID] = append(hashes[desc.chainID], hash)
|
||||
}
|
||||
|
||||
err := tm.storePendingTransactions(tm.multiTransactionForKeycardSigning, hashes, tm.transactionsBridgeData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MultiTransactionCommandResult{
|
||||
ID: int64(tm.multiTransactionForKeycardSigning.ID),
|
||||
Hashes: hashes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) storePendingTransactions(multiTransaction *MultiTransaction,
|
||||
hashes map[uint64][]types.Hash, data []*bridge.TransactionBridge) error {
|
||||
|
||||
|
@ -359,6 +463,29 @@ func multiTransactionFromCommand(command *MultiTransactionCommand) *MultiTransac
|
|||
return multiTransaction
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) buildTransactions(bridges map[string]bridge.Bridge) ([]string, error) {
|
||||
tm.transactionsForKeycardSingning = make(map[common.Hash]*TransactionDescription)
|
||||
var hashes []string
|
||||
for _, bridgeTx := range tm.transactionsBridgeData {
|
||||
builtTx, err := bridges[bridgeTx.BridgeName].BuildTransaction(bridgeTx)
|
||||
if err != nil {
|
||||
return hashes, err
|
||||
}
|
||||
|
||||
signer := ethTypes.NewLondonSigner(big.NewInt(int64(bridgeTx.ChainID)))
|
||||
txHash := signer.Hash(builtTx)
|
||||
|
||||
tm.transactionsForKeycardSingning[txHash] = &TransactionDescription{
|
||||
chainID: bridgeTx.ChainID,
|
||||
builtTx: builtTx,
|
||||
}
|
||||
|
||||
hashes = append(hashes, txHash.String())
|
||||
}
|
||||
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) sendTransactions(multiTransaction *MultiTransaction,
|
||||
data []*bridge.TransactionBridge, bridges map[string]bridge.Bridge, password string) (
|
||||
map[uint64][]types.Hash, error) {
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
func setupTestTransactionDB(t *testing.T) (*TransactionManager, func()) {
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
return &TransactionManager{db, nil, nil, nil, nil, nil, nil}, func() {
|
||||
return &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil}, func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,16 @@ const (
|
|||
walletEvent = "wallet"
|
||||
)
|
||||
|
||||
type UnsignedTransactions struct {
|
||||
Type string `json:"type"`
|
||||
Transactions []string `json:"transactions"`
|
||||
}
|
||||
|
||||
// SendWalletEvent sends event from services/wallet/events.
|
||||
func SendWalletEvent(event interface{}) {
|
||||
send(walletEvent, event)
|
||||
}
|
||||
|
||||
func SendTransactionsForSigningEvent(transactions []string) {
|
||||
send(walletEvent, UnsignedTransactions{Type: "sing-transactions", Transactions: transactions})
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ const (
|
|||
|
||||
defaultGas = 90000
|
||||
|
||||
validSignatureSize = 65
|
||||
ValidSignatureSize = 65
|
||||
)
|
||||
|
||||
// ErrInvalidSignatureSize is returned if a signature is not 65 bytes to avoid panic from go-ethereum
|
||||
|
@ -93,23 +93,50 @@ func (t *Transactor) SendTransactionWithChainID(chainID uint64, sendArgs SendTxA
|
|||
return
|
||||
}
|
||||
|
||||
func (t *Transactor) ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs) (tx *gethtypes.Transaction, err error) {
|
||||
wrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
||||
tx, err = t.validateAndBuildTransaction(wrapper, sendArgs)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Transactor) SendBuiltTransactionWithSignature(chainID uint64, tx *gethtypes.Transaction, sig []byte) (hash types.Hash, err error) {
|
||||
if len(sig) != ValidSignatureSize {
|
||||
return hash, ErrInvalidSignatureSize
|
||||
}
|
||||
|
||||
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
||||
chID := big.NewInt(int64(rpcWrapper.chainID))
|
||||
|
||||
signer := gethtypes.NewLondonSigner(chID)
|
||||
signedTx, err := tx.WithSignature(signer, sig)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := rpcWrapper.SendTransaction(ctx, signedTx); err != nil {
|
||||
return hash, err
|
||||
}
|
||||
return types.Hash(signedTx.Hash()), nil
|
||||
}
|
||||
|
||||
// SendTransactionWithSignature receive a transaction and a signature, serialize them together and propage it to the network.
|
||||
// It's different from eth_sendRawTransaction because it receives a signature and not a serialized transaction with signature.
|
||||
// Since the transactions is already signed, we assume it was validated and used the right nonce.
|
||||
func (t *Transactor) SendTransactionWithSignature(args SendTxArgs, sig []byte) (hash types.Hash, err error) {
|
||||
func (t *Transactor) SendTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (hash types.Hash, err error) {
|
||||
if !args.Valid() {
|
||||
return hash, ErrInvalidSendTxArgs
|
||||
}
|
||||
|
||||
if len(sig) != validSignatureSize {
|
||||
if len(sig) != ValidSignatureSize {
|
||||
return hash, ErrInvalidSignatureSize
|
||||
}
|
||||
|
||||
chainID := big.NewInt(int64(t.networkID))
|
||||
signer := gethtypes.NewLondonSigner(chainID)
|
||||
|
||||
tx := t.buildTransaction(args)
|
||||
expectedNonce, unlock, err := t.nonce.Next(t.rpcWrapper, args.From)
|
||||
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
||||
expectedNonce, unlock, err := t.nonce.Next(rpcWrapper, args.From)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
@ -121,18 +148,7 @@ func (t *Transactor) SendTransactionWithSignature(args SendTxArgs, sig []byte) (
|
|||
return hash, &ErrBadNonce{tx.Nonce(), expectedNonce}
|
||||
}
|
||||
|
||||
signedTx, err := tx.WithSignature(signer, sig)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := t.rpcWrapper.SendTransaction(ctx, signedTx); err != nil {
|
||||
return hash, err
|
||||
}
|
||||
return types.Hash(signedTx.Hash()), nil
|
||||
return t.SendBuiltTransactionWithSignature(chainID, tx, sig)
|
||||
}
|
||||
|
||||
func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs, hash types.Hash, err error) {
|
||||
|
@ -235,18 +251,14 @@ func (t *Transactor) validateAccount(args SendTxArgs, selectedAccount *account.S
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccount *account.SelectedExtKey, args SendTxArgs) (hash types.Hash, err error) {
|
||||
if err = t.validateAccount(args, selectedAccount); err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args SendTxArgs) (tx *gethtypes.Transaction, err error) {
|
||||
if !args.Valid() {
|
||||
return hash, ErrInvalidSendTxArgs
|
||||
return tx, ErrInvalidSendTxArgs
|
||||
}
|
||||
|
||||
nonce, unlock, err := t.nonce.Next(rpcWrapper, args.From)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
if args.Nonce != nil {
|
||||
nonce = uint64(*args.Nonce)
|
||||
|
@ -261,10 +273,10 @@ func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccoun
|
|||
if !args.IsDynamicFeeTx() && args.GasPrice == nil {
|
||||
gasPrice, err = rpcWrapper.SuggestGasPrice(ctx)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
}
|
||||
chainID := big.NewInt(int64(rpcWrapper.chainID))
|
||||
|
||||
value := (*big.Int)(args.Value)
|
||||
var gas uint64
|
||||
if args.Gas != nil {
|
||||
|
@ -289,20 +301,34 @@ func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccoun
|
|||
Data: args.GetInput(),
|
||||
})
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return tx, err
|
||||
}
|
||||
if gas < defaultGas {
|
||||
t.log.Info("default gas will be used because estimated is lower", "estimated", gas, "default", defaultGas)
|
||||
gas = defaultGas
|
||||
}
|
||||
}
|
||||
tx := t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
||||
tx = t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccount *account.SelectedExtKey, args SendTxArgs) (hash types.Hash, err error) {
|
||||
if err = t.validateAccount(args, selectedAccount); err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
tx, err := t.validateAndBuildTransaction(rpcWrapper, args)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
chainID := big.NewInt(int64(rpcWrapper.chainID))
|
||||
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
// ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
// defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := rpcWrapper.SendTransaction(ctx, signedTx); err != nil {
|
||||
return hash, err
|
||||
|
|
|
@ -374,7 +374,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
|||
Return(common.Hash{}, nil)
|
||||
}
|
||||
|
||||
_, err = s.manager.SendTransactionWithSignature(args, sig)
|
||||
_, err = s.manager.SendTransactionWithSignature(s.nodeConfig.NetworkID, args, sig)
|
||||
if scenario.expectError {
|
||||
s.Error(err)
|
||||
// local nonce should not be incremented
|
||||
|
@ -393,7 +393,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
|||
|
||||
func (s *TransactorSuite) TestSendTransactionWithSignature_InvalidSignature() {
|
||||
args := SendTxArgs{}
|
||||
_, err := s.manager.SendTransactionWithSignature(args, []byte{})
|
||||
_, err := s.manager.SendTransactionWithSignature(1, args, []byte{})
|
||||
s.Equal(ErrInvalidSignatureSize, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -106,6 +106,11 @@ func (args SendTxArgs) ToTransactOpts(signerFn bind.SignerFn) *bind.TransactOpts
|
|||
gasLimit = uint64(*args.Gas)
|
||||
}
|
||||
|
||||
var noSign = false
|
||||
if signerFn == nil {
|
||||
noSign = true
|
||||
}
|
||||
|
||||
return &bind.TransactOpts{
|
||||
From: common.Address(args.From),
|
||||
Signer: signerFn,
|
||||
|
@ -114,6 +119,7 @@ func (args SendTxArgs) ToTransactOpts(signerFn bind.SignerFn) *bind.TransactOpts
|
|||
GasFeeCap: gasFeeCap,
|
||||
GasTipCap: gasTipCap,
|
||||
Nonce: nonce,
|
||||
NoSign: noSign,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ type TransactOpts struct {
|
|||
|
||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
|
||||
NoSign bool // Do all transact steps but do not sign or send the transaction
|
||||
NoSend bool // Do all transact steps but do not send the transaction
|
||||
}
|
||||
|
||||
|
@ -387,6 +388,9 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.NoSign {
|
||||
return rawTx, nil
|
||||
}
|
||||
// Sign the transaction and schedule it for execution
|
||||
if opts.Signer == nil {
|
||||
return nil, errors.New("no signer to authorize the transaction with")
|
||||
|
|
|
@ -208,7 +208,7 @@ github.com/edsrzf/mmap-go
|
|||
## explicit; go 1.14
|
||||
github.com/elastic/gosigar
|
||||
github.com/elastic/gosigar/sys/windows
|
||||
# github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.7
|
||||
# github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.9
|
||||
## explicit; go 1.17
|
||||
github.com/ethereum/go-ethereum
|
||||
github.com/ethereum/go-ethereum/accounts
|
||||
|
|
Loading…
Reference in New Issue