github.com/MetalBlockchain/subnet-evm@v0.4.9/warp/backend.go (about)

     1  // (c) 2023, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package warp
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/MetalBlockchain/metalgo/cache"
    11  	"github.com/MetalBlockchain/metalgo/database"
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/snow"
    14  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    15  	"github.com/MetalBlockchain/metalgo/utils/hashing"
    16  	"github.com/MetalBlockchain/metalgo/vms/platformvm/teleporter"
    17  )
    18  
    19  var _ WarpBackend = &warpBackend{}
    20  
    21  // WarpBackend tracks signature eligible warp messages and provides an interface to fetch them.
    22  // The backend is also used to query for warp message signatures by the signature request handler.
    23  type WarpBackend interface {
    24  	// AddMessage signs [unsignedMessage] and adds it to the warp backend database
    25  	AddMessage(ctx context.Context, unsignedMessage *teleporter.UnsignedMessage) error
    26  
    27  	// GetSignature returns the signature of the requested message hash.
    28  	GetSignature(ctx context.Context, messageHash ids.ID) ([bls.SignatureLen]byte, error)
    29  }
    30  
    31  // warpBackend implements WarpBackend, keeps track of warp messages, and generates message signatures.
    32  type warpBackend struct {
    33  	db             database.Database
    34  	snowCtx        *snow.Context
    35  	signatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte]
    36  }
    37  
    38  // NewWarpBackend creates a new WarpBackend, and initializes the signature cache and message tracking database.
    39  func NewWarpBackend(snowCtx *snow.Context, db database.Database, signatureCacheSize int) WarpBackend {
    40  	return &warpBackend{
    41  		db:             db,
    42  		snowCtx:        snowCtx,
    43  		signatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: signatureCacheSize},
    44  	}
    45  }
    46  
    47  func (w *warpBackend) AddMessage(ctx context.Context, unsignedMessage *teleporter.UnsignedMessage) error {
    48  	messageID := hashing.ComputeHash256Array(unsignedMessage.Bytes())
    49  
    50  	// In the case when a node restarts, and possibly changes its bls key, the cache gets emptied but the database does not.
    51  	// So to avoid having incorrect signatures saved in the database after a bls key change, we save the full message in the database.
    52  	// Whereas for the cache, after the node restart, the cache would be emptied so we can directly save the signatures.
    53  	if err := w.db.Put(messageID[:], unsignedMessage.Bytes()); err != nil {
    54  		return fmt.Errorf("failed to put warp signature in db: %w", err)
    55  	}
    56  
    57  	var signature [bls.SignatureLen]byte
    58  	sig, err := w.snowCtx.TeleporterSigner.Sign(unsignedMessage)
    59  	if err != nil {
    60  		return fmt.Errorf("failed to sign warp message: %w", err)
    61  	}
    62  
    63  	copy(signature[:], sig)
    64  	w.signatureCache.Put(messageID, signature)
    65  	return nil
    66  }
    67  
    68  func (w *warpBackend) GetSignature(ctx context.Context, messageID ids.ID) ([bls.SignatureLen]byte, error) {
    69  	if sig, ok := w.signatureCache.Get(messageID); ok {
    70  		return sig, nil
    71  	}
    72  
    73  	unsignedMessageBytes, err := w.db.Get(messageID[:])
    74  	if err != nil {
    75  		return [bls.SignatureLen]byte{}, fmt.Errorf("failed to get warp message %s from db: %w", messageID.String(), err)
    76  	}
    77  
    78  	unsignedMessage, err := teleporter.ParseUnsignedMessage(unsignedMessageBytes)
    79  	if err != nil {
    80  		return [bls.SignatureLen]byte{}, fmt.Errorf("failed to parse unsigned message %s: %w", messageID.String(), err)
    81  	}
    82  
    83  	var signature [bls.SignatureLen]byte
    84  	sig, err := w.snowCtx.TeleporterSigner.Sign(unsignedMessage)
    85  	if err != nil {
    86  		return [bls.SignatureLen]byte{}, fmt.Errorf("failed to sign warp message: %w", err)
    87  	}
    88  
    89  	copy(signature[:], sig)
    90  	w.signatureCache.Put(messageID, signature)
    91  	return signature, nil
    92  }