github.com/MetalBlockchain/metalgo@v1.11.9/tests/antithesis/xsvm/main.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"crypto/rand"
     9  	"log"
    10  	"math/big"
    11  	"os"
    12  	"time"
    13  
    14  	"github.com/antithesishq/antithesis-sdk-go/assert"
    15  	"github.com/antithesishq/antithesis-sdk-go/lifecycle"
    16  
    17  	"github.com/MetalBlockchain/metalgo/genesis"
    18  	"github.com/MetalBlockchain/metalgo/ids"
    19  	"github.com/MetalBlockchain/metalgo/tests/antithesis"
    20  	"github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1"
    21  	"github.com/MetalBlockchain/metalgo/utils/set"
    22  	"github.com/MetalBlockchain/metalgo/utils/units"
    23  	"github.com/MetalBlockchain/metalgo/vms/example/xsvm/api"
    24  	"github.com/MetalBlockchain/metalgo/vms/example/xsvm/cmd/issue/status"
    25  	"github.com/MetalBlockchain/metalgo/vms/example/xsvm/cmd/issue/transfer"
    26  )
    27  
    28  const (
    29  	NumKeys         = 5
    30  	PollingInterval = 50 * time.Millisecond
    31  )
    32  
    33  func main() {
    34  	c, err := antithesis.NewConfig(os.Args)
    35  	if err != nil {
    36  		log.Fatalf("invalid config: %s", err)
    37  	}
    38  
    39  	ctx := context.Background()
    40  	if err := antithesis.AwaitHealthyNodes(ctx, c.URIs); err != nil {
    41  		log.Fatalf("failed to await healthy nodes: %s", err)
    42  	}
    43  
    44  	if len(c.ChainIDs) != 1 {
    45  		log.Fatalf("expected 1 chainID, saw %d", len(c.ChainIDs))
    46  	}
    47  	chainID, err := ids.FromString(c.ChainIDs[0])
    48  	if err != nil {
    49  		log.Fatalf("failed to parse chainID: %s", err)
    50  	}
    51  
    52  	genesisWorkload := &workload{
    53  		id:      0,
    54  		chainID: chainID,
    55  		key:     genesis.VMRQKey,
    56  		addrs:   set.Of(genesis.VMRQKey.Address()),
    57  		uris:    c.URIs,
    58  	}
    59  
    60  	workloads := make([]*workload, NumKeys)
    61  	workloads[0] = genesisWorkload
    62  
    63  	initialAmount := 100 * units.KiloAvax
    64  	for i := 1; i < NumKeys; i++ {
    65  		key, err := secp256k1.NewPrivateKey()
    66  		if err != nil {
    67  			log.Fatalf("failed to generate key: %s", err)
    68  		}
    69  
    70  		var (
    71  			addr          = key.Address()
    72  			baseStartTime = time.Now()
    73  		)
    74  		transferTxStatus, err := transfer.Transfer(
    75  			ctx,
    76  			&transfer.Config{
    77  				URI:        c.URIs[0],
    78  				ChainID:    chainID,
    79  				AssetID:    chainID,
    80  				Amount:     initialAmount,
    81  				To:         addr,
    82  				PrivateKey: genesisWorkload.key,
    83  			},
    84  		)
    85  		if err != nil {
    86  			log.Fatalf("failed to issue initial funding transfer: %s", err)
    87  		}
    88  		log.Printf("issued initial funding transfer %s in %s", transferTxStatus.TxID, time.Since(baseStartTime))
    89  
    90  		genesisWorkload.confirmTransferTx(ctx, transferTxStatus)
    91  
    92  		workloads[i] = &workload{
    93  			id:      i,
    94  			chainID: chainID,
    95  			key:     key,
    96  			addrs:   set.Of(addr),
    97  			uris:    c.URIs,
    98  		}
    99  	}
   100  
   101  	lifecycle.SetupComplete(map[string]any{
   102  		"msg":        "initialized workers",
   103  		"numWorkers": NumKeys,
   104  	})
   105  
   106  	for _, w := range workloads[1:] {
   107  		go w.run(ctx)
   108  	}
   109  	genesisWorkload.run(ctx)
   110  }
   111  
   112  type workload struct {
   113  	id      int
   114  	chainID ids.ID
   115  	key     *secp256k1.PrivateKey
   116  	addrs   set.Set[ids.ShortID]
   117  	uris    []string
   118  }
   119  
   120  func (w *workload) run(ctx context.Context) {
   121  	timer := time.NewTimer(0)
   122  	if !timer.Stop() {
   123  		<-timer.C
   124  	}
   125  
   126  	uri := w.uris[w.id%len(w.uris)]
   127  
   128  	client := api.NewClient(uri, w.chainID.String())
   129  	balance, err := client.Balance(ctx, w.key.Address(), w.chainID)
   130  	if err != nil {
   131  		log.Fatalf("failed to fetch balance: %s", err)
   132  	}
   133  	log.Printf("worker %d starting with a balance of %d", w.id, balance)
   134  	assert.Reachable("worker starting", map[string]any{
   135  		"worker":  w.id,
   136  		"balance": balance,
   137  	})
   138  
   139  	for {
   140  		log.Printf("worker %d executing transfer", w.id)
   141  		destAddress, _ := w.addrs.Peek()
   142  		txStatus, err := transfer.Transfer(
   143  			ctx,
   144  			&transfer.Config{
   145  				URI:        uri,
   146  				ChainID:    w.chainID,
   147  				AssetID:    w.chainID,
   148  				Amount:     units.Schmeckle,
   149  				To:         destAddress,
   150  				PrivateKey: w.key,
   151  			},
   152  		)
   153  		if err != nil {
   154  			log.Printf("worker %d failed to issue transfer: %s", w.id, err)
   155  		} else {
   156  			log.Printf("worker %d issued transfer %s in %s", w.id, txStatus.TxID, time.Since(txStatus.StartTime))
   157  			w.confirmTransferTx(ctx, txStatus)
   158  		}
   159  
   160  		val, err := rand.Int(rand.Reader, big.NewInt(int64(time.Second)))
   161  		if err != nil {
   162  			log.Fatalf("failed to read randomness: %s", err)
   163  		}
   164  
   165  		timer.Reset(time.Duration(val.Int64()))
   166  		select {
   167  		case <-ctx.Done():
   168  			return
   169  		case <-timer.C:
   170  		}
   171  	}
   172  }
   173  
   174  func (w *workload) confirmTransferTx(ctx context.Context, tx *status.TxIssuance) {
   175  	for _, uri := range w.uris {
   176  		client := api.NewClient(uri, w.chainID.String())
   177  		if err := api.AwaitTxAccepted(ctx, client, w.key.Address(), tx.Nonce, PollingInterval); err != nil {
   178  			log.Printf("worker %d failed to confirm transaction %s on %s: %s", w.id, tx.TxID, uri, err)
   179  			return
   180  		}
   181  	}
   182  	log.Printf("worker %d confirmed transaction %s on all nodes", w.id, tx.TxID)
   183  }