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 }