github.com/decred/dcrlnd@v0.7.6/channeldb/witness_cache.go (about)

     1  package channeldb
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/decred/dcrlnd/kvdb"
     7  	"github.com/decred/dcrlnd/lntypes"
     8  )
     9  
    10  var (
    11  	// ErrNoWitnesses is an error that's returned when no new witnesses have
    12  	// been added to the WitnessCache.
    13  	ErrNoWitnesses = fmt.Errorf("no witnesses")
    14  
    15  	// ErrUnknownWitnessType is returned if a caller attempts to
    16  	ErrUnknownWitnessType = fmt.Errorf("unknown witness type")
    17  )
    18  
    19  // WitnessType is enum that denotes what "type" of witness is being
    20  // stored/retrieved. As the WitnessCache itself is agnostic and doesn't enforce
    21  // any structure on added witnesses, we use this type to partition the
    22  // witnesses on disk, and also to know how to map a witness to its look up key.
    23  type WitnessType uint8
    24  
    25  var (
    26  	// Sha256HashWitness is a witness that is simply the pre image to a
    27  	// hash image. In order to map to its key, we'll use sha256.
    28  	Sha256HashWitness WitnessType = 1
    29  )
    30  
    31  // toDBKey is a helper method that maps a witness type to the key that we'll
    32  // use to store it within the database.
    33  func (w WitnessType) toDBKey() ([]byte, error) {
    34  	switch w {
    35  
    36  	case Sha256HashWitness:
    37  		return []byte{byte(w)}, nil
    38  
    39  	default:
    40  		return nil, ErrUnknownWitnessType
    41  	}
    42  }
    43  
    44  var (
    45  	// witnessBucketKey is the name of the bucket that we use to store all
    46  	// witnesses encountered. Within this bucket, we'll create a sub-bucket for
    47  	// each witness type.
    48  	witnessBucketKey = []byte("byte")
    49  )
    50  
    51  // WitnessCache is a persistent cache of all witnesses we've encountered on the
    52  // network. In the case of multi-hop, multi-step contracts, a cache of all
    53  // witnesses can be useful in the case of partial contract resolution. If
    54  // negotiations break down, we may be forced to locate the witness for a
    55  // portion of the contract on-chain. In this case, we'll then add that witness
    56  // to the cache so the incoming contract can fully resolve witness.
    57  // Additionally, as one MUST always use a unique witness on the network, we may
    58  // use this cache to detect duplicate witnesses.
    59  //
    60  // TODO(roasbeef): need expiry policy?
    61  //   - encrypt?
    62  type WitnessCache struct {
    63  	db *DB
    64  }
    65  
    66  // NewWitnessCache returns a new instance of the witness cache.
    67  func (d *DB) NewWitnessCache() *WitnessCache {
    68  	return &WitnessCache{
    69  		db: d,
    70  	}
    71  }
    72  
    73  // witnessEntry is a key-value struct that holds each key -> witness pair, used
    74  // when inserting records into the cache.
    75  type witnessEntry struct {
    76  	key     []byte
    77  	witness []byte
    78  }
    79  
    80  // AddSha256Witnesses adds a batch of new sha256 preimages into the witness
    81  // cache. This is an alias for AddWitnesses that uses Sha256HashWitness as the
    82  // preimages' witness type.
    83  func (w *WitnessCache) AddSha256Witnesses(preimages ...lntypes.Preimage) error {
    84  	// Optimistically compute the preimages' hashes before attempting to
    85  	// start the db transaction.
    86  	entries := make([]witnessEntry, 0, len(preimages))
    87  	for i := range preimages {
    88  		hash := preimages[i].Hash()
    89  		entries = append(entries, witnessEntry{
    90  			key:     hash[:],
    91  			witness: preimages[i][:],
    92  		})
    93  	}
    94  
    95  	return w.addWitnessEntries(Sha256HashWitness, entries)
    96  }
    97  
    98  // addWitnessEntries inserts the witnessEntry key-value pairs into the cache,
    99  // using the appropriate witness type to segment the namespace of possible
   100  // witness types.
   101  func (w *WitnessCache) addWitnessEntries(wType WitnessType,
   102  	entries []witnessEntry) error {
   103  
   104  	// Exit early if there are no witnesses to add.
   105  	if len(entries) == 0 {
   106  		return nil
   107  	}
   108  
   109  	return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error {
   110  		witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey)
   111  		if err != nil {
   112  			return err
   113  		}
   114  
   115  		witnessTypeBucketKey, err := wType.toDBKey()
   116  		if err != nil {
   117  			return err
   118  		}
   119  		witnessTypeBucket, err := witnessBucket.CreateBucketIfNotExists(
   120  			witnessTypeBucketKey,
   121  		)
   122  		if err != nil {
   123  			return err
   124  		}
   125  
   126  		for _, entry := range entries {
   127  			err = witnessTypeBucket.Put(entry.key, entry.witness)
   128  			if err != nil {
   129  				return err
   130  			}
   131  		}
   132  
   133  		return nil
   134  	})
   135  }
   136  
   137  // LookupSha256Witness attempts to lookup the preimage for a sha256 hash. If
   138  // the witness isn't found, ErrNoWitnesses will be returned.
   139  func (w *WitnessCache) LookupSha256Witness(hash lntypes.Hash) (lntypes.Preimage, error) {
   140  	witness, err := w.lookupWitness(Sha256HashWitness, hash[:])
   141  	if err != nil {
   142  		return lntypes.Preimage{}, err
   143  	}
   144  
   145  	return lntypes.MakePreimage(witness)
   146  }
   147  
   148  // lookupWitness attempts to lookup a witness according to its type and also
   149  // its witness key. In the case that the witness isn't found, ErrNoWitnesses
   150  // will be returned.
   151  func (w *WitnessCache) lookupWitness(wType WitnessType, witnessKey []byte) ([]byte, error) {
   152  	var witness []byte
   153  	err := kvdb.View(w.db, func(tx kvdb.RTx) error {
   154  		witnessBucket := tx.ReadBucket(witnessBucketKey)
   155  		if witnessBucket == nil {
   156  			return ErrNoWitnesses
   157  		}
   158  
   159  		witnessTypeBucketKey, err := wType.toDBKey()
   160  		if err != nil {
   161  			return err
   162  		}
   163  		witnessTypeBucket := witnessBucket.NestedReadBucket(witnessTypeBucketKey)
   164  		if witnessTypeBucket == nil {
   165  			return ErrNoWitnesses
   166  		}
   167  
   168  		dbWitness := witnessTypeBucket.Get(witnessKey)
   169  		if dbWitness == nil {
   170  			return ErrNoWitnesses
   171  		}
   172  
   173  		witness = make([]byte, len(dbWitness))
   174  		copy(witness, dbWitness)
   175  
   176  		return nil
   177  	}, func() {
   178  		witness = nil
   179  	})
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	return witness, nil
   185  }
   186  
   187  // DeleteSha256Witness attempts to delete a sha256 preimage identified by hash.
   188  func (w *WitnessCache) DeleteSha256Witness(hash lntypes.Hash) error {
   189  	return w.deleteWitness(Sha256HashWitness, hash[:])
   190  }
   191  
   192  // deleteWitness attempts to delete a particular witness from the database.
   193  func (w *WitnessCache) deleteWitness(wType WitnessType, witnessKey []byte) error {
   194  	return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error {
   195  		witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey)
   196  		if err != nil {
   197  			return err
   198  		}
   199  
   200  		witnessTypeBucketKey, err := wType.toDBKey()
   201  		if err != nil {
   202  			return err
   203  		}
   204  		witnessTypeBucket, err := witnessBucket.CreateBucketIfNotExists(
   205  			witnessTypeBucketKey,
   206  		)
   207  		if err != nil {
   208  			return err
   209  		}
   210  
   211  		return witnessTypeBucket.Delete(witnessKey)
   212  	})
   213  }
   214  
   215  // DeleteWitnessClass attempts to delete an *entire* class of witnesses. After
   216  // this function return with a non-nil error,
   217  func (w *WitnessCache) DeleteWitnessClass(wType WitnessType) error {
   218  	return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error {
   219  		witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey)
   220  		if err != nil {
   221  			return err
   222  		}
   223  
   224  		witnessTypeBucketKey, err := wType.toDBKey()
   225  		if err != nil {
   226  			return err
   227  		}
   228  
   229  		return witnessBucket.DeleteNestedBucket(witnessTypeBucketKey)
   230  	})
   231  }