github.com/ava-labs/avalanchego@v1.11.11/vms/example/xsvm/execute/tx.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package execute
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  
    10  	"github.com/ava-labs/avalanchego/database"
    11  	"github.com/ava-labs/avalanchego/ids"
    12  	"github.com/ava-labs/avalanchego/snow"
    13  	"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
    14  	"github.com/ava-labs/avalanchego/utils/hashing"
    15  	"github.com/ava-labs/avalanchego/utils/wrappers"
    16  	"github.com/ava-labs/avalanchego/vms/example/xsvm/state"
    17  	"github.com/ava-labs/avalanchego/vms/example/xsvm/tx"
    18  	"github.com/ava-labs/avalanchego/vms/platformvm/warp"
    19  )
    20  
    21  const (
    22  	QuorumNumerator   = 2
    23  	QuorumDenominator = 3
    24  )
    25  
    26  var (
    27  	_ tx.Visitor = (*Tx)(nil)
    28  
    29  	errFeeTooHigh          = errors.New("fee too high")
    30  	errWrongChainID        = errors.New("wrong chainID")
    31  	errMissingBlockContext = errors.New("missing block context")
    32  	errDuplicateImport     = errors.New("duplicate import")
    33  )
    34  
    35  type Tx struct {
    36  	Context      context.Context
    37  	ChainContext *snow.Context
    38  	Database     database.KeyValueReaderWriterDeleter
    39  
    40  	SkipVerify   bool
    41  	BlockContext *block.Context
    42  
    43  	TxID        ids.ID
    44  	Sender      ids.ShortID
    45  	TransferFee uint64
    46  	ExportFee   uint64
    47  	ImportFee   uint64
    48  }
    49  
    50  func (t *Tx) Transfer(tf *tx.Transfer) error {
    51  	if tf.MaxFee < t.TransferFee {
    52  		return errFeeTooHigh
    53  	}
    54  	if tf.ChainID != t.ChainContext.ChainID {
    55  		return errWrongChainID
    56  	}
    57  
    58  	return errors.Join(
    59  		state.IncrementNonce(t.Database, t.Sender, tf.Nonce),
    60  		state.DecreaseBalance(t.Database, t.Sender, tf.ChainID, t.TransferFee),
    61  		state.DecreaseBalance(t.Database, t.Sender, tf.AssetID, tf.Amount),
    62  		state.IncreaseBalance(t.Database, tf.To, tf.AssetID, tf.Amount),
    63  	)
    64  }
    65  
    66  func (t *Tx) Export(e *tx.Export) error {
    67  	if e.MaxFee < t.ExportFee {
    68  		return errFeeTooHigh
    69  	}
    70  	if e.ChainID != t.ChainContext.ChainID {
    71  		return errWrongChainID
    72  	}
    73  
    74  	payload, err := tx.NewPayload(
    75  		t.Sender,
    76  		e.Nonce,
    77  		e.IsReturn,
    78  		e.Amount,
    79  		e.To,
    80  	)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	message, err := warp.NewUnsignedMessage(
    86  		t.ChainContext.NetworkID,
    87  		e.ChainID,
    88  		payload.Bytes(),
    89  	)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	var errs wrappers.Errs
    95  	errs.Add(
    96  		state.IncrementNonce(t.Database, t.Sender, e.Nonce),
    97  		state.DecreaseBalance(t.Database, t.Sender, e.ChainID, t.ExportFee),
    98  	)
    99  
   100  	if e.IsReturn {
   101  		errs.Add(
   102  			state.DecreaseBalance(t.Database, t.Sender, e.PeerChainID, e.Amount),
   103  		)
   104  	} else {
   105  		errs.Add(
   106  			state.DecreaseBalance(t.Database, t.Sender, e.ChainID, e.Amount),
   107  			state.IncreaseLoan(t.Database, e.PeerChainID, e.Amount),
   108  		)
   109  	}
   110  
   111  	errs.Add(
   112  		state.SetMessage(t.Database, t.TxID, message),
   113  	)
   114  	return errs.Err
   115  }
   116  
   117  func (t *Tx) Import(i *tx.Import) error {
   118  	if i.MaxFee < t.ImportFee {
   119  		return errFeeTooHigh
   120  	}
   121  	if t.BlockContext == nil {
   122  		return errMissingBlockContext
   123  	}
   124  
   125  	message, err := warp.ParseMessage(i.Message)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	var errs wrappers.Errs
   131  	errs.Add(
   132  		state.IncrementNonce(t.Database, t.Sender, i.Nonce),
   133  		state.DecreaseBalance(t.Database, t.Sender, t.ChainContext.ChainID, t.ImportFee),
   134  	)
   135  
   136  	payload, err := tx.ParsePayload(message.Payload)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	if payload.IsReturn {
   142  		errs.Add(
   143  			state.IncreaseBalance(t.Database, payload.To, t.ChainContext.ChainID, payload.Amount),
   144  			state.DecreaseLoan(t.Database, message.SourceChainID, payload.Amount),
   145  		)
   146  	} else {
   147  		errs.Add(
   148  			state.IncreaseBalance(t.Database, payload.To, message.SourceChainID, payload.Amount),
   149  		)
   150  	}
   151  
   152  	var loanID ids.ID = hashing.ComputeHash256Array(message.UnsignedMessage.Bytes())
   153  	hasLoanID, err := state.HasLoanID(t.Database, message.SourceChainID, loanID)
   154  	if hasLoanID {
   155  		return errDuplicateImport
   156  	}
   157  
   158  	errs.Add(
   159  		err,
   160  		state.AddLoanID(t.Database, message.SourceChainID, loanID),
   161  	)
   162  
   163  	if t.SkipVerify || errs.Errored() {
   164  		return errs.Err
   165  	}
   166  
   167  	return message.Signature.Verify(
   168  		t.Context,
   169  		&message.UnsignedMessage,
   170  		t.ChainContext.NetworkID,
   171  		t.ChainContext.ValidatorState,
   172  		t.BlockContext.PChainHeight,
   173  		QuorumNumerator,
   174  		QuorumDenominator,
   175  	)
   176  }