code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_send_raw_transaction.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package api
    17  
    18  import (
    19  	"context"
    20  	"encoding/base64"
    21  	"fmt"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/libs/jsonrpc"
    25  	"code.vegaprotocol.io/vega/libs/proto"
    26  	apipb "code.vegaprotocol.io/vega/protos/vega/api/v1"
    27  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    28  
    29  	"github.com/mitchellh/mapstructure"
    30  )
    31  
    32  type AdminSendRawTransactionParams struct {
    33  	Network                string        `json:"network"`
    34  	NodeAddress            string        `json:"nodeAddress"`
    35  	Retries                uint64        `json:"retries"`
    36  	MaximumRequestDuration time.Duration `json:"maximumRequestDuration"`
    37  	SendingMode            string        `json:"sendingMode"`
    38  	EncodedTransaction     string        `json:"encodedTransaction"`
    39  }
    40  
    41  type ParsedAdminSendRawTransactionParams struct {
    42  	Network                string
    43  	NodeAddress            string
    44  	Retries                uint64
    45  	MaximumRequestDuration time.Duration
    46  	SendingMode            apipb.SubmitTransactionRequest_Type
    47  	RawTransaction         string
    48  }
    49  
    50  type AdminSendRawTransactionResult struct {
    51  	ReceivedAt      time.Time                         `json:"receivedAt"`
    52  	SentAt          time.Time                         `json:"sentAt"`
    53  	TransactionHash string                            `json:"transactionHash"`
    54  	Transaction     *commandspb.Transaction           `json:"transaction"`
    55  	Node            AdminSendRawTransactionNodeResult `json:"node"`
    56  }
    57  
    58  type AdminSendRawTransactionNodeResult struct {
    59  	Host string `json:"host"`
    60  }
    61  
    62  type AdminSendRawTransaction struct {
    63  	networkStore        NetworkStore
    64  	nodeSelectorBuilder NodeSelectorBuilder
    65  }
    66  
    67  func (h *AdminSendRawTransaction) Handle(ctx context.Context, rawParams jsonrpc.Params) (jsonrpc.Result, *jsonrpc.ErrorDetails) {
    68  	receivedAt := time.Now()
    69  
    70  	params, err := validateAdminSendRawTransactionParams(rawParams)
    71  	if err != nil {
    72  		return nil, InvalidParams(err)
    73  	}
    74  
    75  	tx := &commandspb.Transaction{}
    76  	if err := proto.Unmarshal([]byte(params.RawTransaction), tx); err != nil {
    77  		return nil, InvalidParams(ErrRawTransactionIsNotValidVegaTransaction)
    78  	}
    79  
    80  	hosts := []string{params.NodeAddress}
    81  	if len(params.Network) != 0 {
    82  		exists, err := h.networkStore.NetworkExists(params.Network)
    83  		if err != nil {
    84  			return nil, InternalError(fmt.Errorf("could not determine if the network exists: %w", err))
    85  		} else if !exists {
    86  			return nil, InvalidParams(ErrNetworkDoesNotExist)
    87  		}
    88  
    89  		n, err := h.networkStore.GetNetwork(params.Network)
    90  		if err != nil {
    91  			return nil, InternalError(fmt.Errorf("could not retrieve the network configuration: %w", err))
    92  		}
    93  
    94  		if err := n.EnsureCanConnectGRPCNode(); err != nil {
    95  			return nil, InternalError(ErrNetworkConfigurationDoesNotHaveGRPCNodes)
    96  		}
    97  		hosts = n.API.GRPC.Hosts
    98  	}
    99  
   100  	nodeSelector, err := h.nodeSelectorBuilder(hosts, params.Retries, params.MaximumRequestDuration)
   101  	if err != nil {
   102  		return nil, InternalError(fmt.Errorf("could not initialize the node selector: %w", err))
   103  	}
   104  
   105  	currentNode, err := nodeSelector.Node(ctx, noNodeSelectionReporting)
   106  	if err != nil {
   107  		return nil, NodeCommunicationError(ErrNoHealthyNodeAvailable)
   108  	}
   109  
   110  	sentAt := time.Now()
   111  	txHash, err := currentNode.SendTransaction(ctx, tx, params.SendingMode)
   112  	if err != nil {
   113  		return nil, NetworkErrorFromTransactionError(err)
   114  	}
   115  
   116  	return AdminSendRawTransactionResult{
   117  		ReceivedAt:      receivedAt,
   118  		SentAt:          sentAt,
   119  		TransactionHash: txHash,
   120  		Transaction:     tx,
   121  		Node: AdminSendRawTransactionNodeResult{
   122  			Host: currentNode.Host(),
   123  		},
   124  	}, nil
   125  }
   126  
   127  func NewAdminSendRawTransaction(networkStore NetworkStore, nodeSelectorBuilder NodeSelectorBuilder) *AdminSendRawTransaction {
   128  	return &AdminSendRawTransaction{
   129  		networkStore:        networkStore,
   130  		nodeSelectorBuilder: nodeSelectorBuilder,
   131  	}
   132  }
   133  
   134  func validateAdminSendRawTransactionParams(rawParams jsonrpc.Params) (ParsedAdminSendRawTransactionParams, error) {
   135  	if rawParams == nil {
   136  		return ParsedAdminSendRawTransactionParams{}, ErrParamsRequired
   137  	}
   138  
   139  	params := AdminSendRawTransactionParams{}
   140  	if err := mapstructure.Decode(rawParams, &params); err != nil {
   141  		return ParsedAdminSendRawTransactionParams{}, ErrParamsDoNotMatch
   142  	}
   143  
   144  	if params.Network == "" && params.NodeAddress == "" {
   145  		return ParsedAdminSendRawTransactionParams{}, ErrNetworkOrNodeAddressIsRequired
   146  	}
   147  
   148  	if params.Network != "" && params.NodeAddress != "" {
   149  		return ParsedAdminSendRawTransactionParams{}, ErrSpecifyingNetworkAndNodeAddressIsNotSupported
   150  	}
   151  
   152  	if params.SendingMode == "" {
   153  		return ParsedAdminSendRawTransactionParams{}, ErrSendingModeIsRequired
   154  	}
   155  
   156  	isValidSendingMode := false
   157  	var sendingMode apipb.SubmitTransactionRequest_Type
   158  	for tp, sm := range apipb.SubmitTransactionRequest_Type_value {
   159  		if tp == params.SendingMode {
   160  			isValidSendingMode = true
   161  			sendingMode = apipb.SubmitTransactionRequest_Type(sm)
   162  		}
   163  	}
   164  	if !isValidSendingMode {
   165  		return ParsedAdminSendRawTransactionParams{}, fmt.Errorf("the sending mode %q is not a valid one", params.SendingMode)
   166  	}
   167  
   168  	if sendingMode == apipb.SubmitTransactionRequest_TYPE_UNSPECIFIED {
   169  		return ParsedAdminSendRawTransactionParams{}, ErrSendingModeCannotBeTypeUnspecified
   170  	}
   171  
   172  	if params.EncodedTransaction == "" {
   173  		return ParsedAdminSendRawTransactionParams{}, ErrEncodedTransactionIsRequired
   174  	}
   175  
   176  	tx, err := base64.StdEncoding.DecodeString(params.EncodedTransaction)
   177  	if err != nil {
   178  		return ParsedAdminSendRawTransactionParams{}, ErrEncodedTransactionIsNotValidBase64String
   179  	}
   180  
   181  	return ParsedAdminSendRawTransactionParams{
   182  		Network:                params.Network,
   183  		NodeAddress:            params.NodeAddress,
   184  		RawTransaction:         string(tx),
   185  		SendingMode:            sendingMode,
   186  		Retries:                params.Retries,
   187  		MaximumRequestDuration: params.MaximumRequestDuration,
   188  	}, nil
   189  }