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

     1  package migtest
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/decred/dcrlnd/kvdb"
    11  )
    12  
    13  // DumpDB dumps go code describing the contents of the database to stdout. This
    14  // function is only intended for use during development.
    15  //
    16  // Example output:
    17  //
    18  //	map[string]interface{}{
    19  //		hex("1234"): map[string]interface{}{
    20  //			"human-readable": hex("102030"),
    21  //			hex("1111"): hex("5783492373"),
    22  //		},
    23  //	}
    24  func DumpDB(tx kvdb.RTx, rootKey []byte) error {
    25  	bucket := tx.ReadBucket(rootKey)
    26  	if bucket == nil {
    27  		return fmt.Errorf("bucket %v not found", string(rootKey))
    28  	}
    29  
    30  	return dumpBucket(bucket)
    31  }
    32  
    33  func dumpBucket(bucket kvdb.RBucket) error {
    34  	fmt.Printf("map[string]interface{} {\n")
    35  	err := bucket.ForEach(func(k, v []byte) error {
    36  		key := toString(k)
    37  		fmt.Printf("%v: ", key)
    38  
    39  		subBucket := bucket.NestedReadBucket(k)
    40  		if subBucket != nil {
    41  			err := dumpBucket(subBucket)
    42  			if err != nil {
    43  				return err
    44  			}
    45  		} else {
    46  			fmt.Print(toHex(v))
    47  		}
    48  		fmt.Printf(",\n")
    49  
    50  		return nil
    51  	})
    52  	if err != nil {
    53  		return err
    54  	}
    55  	fmt.Printf("}")
    56  
    57  	return nil
    58  }
    59  
    60  // RestoreDB primes the database with the given data set.
    61  func RestoreDB(tx kvdb.RwTx, rootKey []byte, data map[string]interface{}) error {
    62  	bucket, err := tx.CreateTopLevelBucket(rootKey)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	return restoreDB(bucket, data)
    68  }
    69  
    70  func restoreDB(bucket kvdb.RwBucket, data map[string]interface{}) error {
    71  	for k, v := range data {
    72  		key := []byte(k)
    73  
    74  		switch value := v.(type) {
    75  
    76  		// Key contains value.
    77  		case string:
    78  			err := bucket.Put(key, []byte(value))
    79  			if err != nil {
    80  				return err
    81  			}
    82  
    83  		// Key contains a sub-bucket.
    84  		case map[string]interface{}:
    85  			subBucket, err := bucket.CreateBucket(key)
    86  			if err != nil {
    87  				return err
    88  			}
    89  
    90  			if err := restoreDB(subBucket, value); err != nil {
    91  				return err
    92  			}
    93  
    94  		default:
    95  			return errors.New("invalid type")
    96  		}
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  // VerifyDB verifies the database against the given data set.
   103  func VerifyDB(tx kvdb.RTx, rootKey []byte, data map[string]interface{}) error {
   104  	bucket := tx.ReadBucket(rootKey)
   105  	if bucket == nil {
   106  		return fmt.Errorf("bucket %v not found", string(rootKey))
   107  	}
   108  
   109  	return verifyDB(bucket, data)
   110  }
   111  
   112  func verifyDB(bucket kvdb.RBucket, data map[string]interface{}) error {
   113  	for k, v := range data {
   114  		key := []byte(k)
   115  
   116  		switch value := v.(type) {
   117  
   118  		// Key contains value.
   119  		case string:
   120  			expectedValue := []byte(value)
   121  			dbValue := bucket.Get(key)
   122  
   123  			if !bytes.Equal(dbValue, expectedValue) {
   124  				return errors.New("value mismatch")
   125  			}
   126  
   127  		// Key contains a sub-bucket.
   128  		case map[string]interface{}:
   129  			subBucket := bucket.NestedReadBucket(key)
   130  			if subBucket == nil {
   131  				return fmt.Errorf("bucket %v not found", k)
   132  			}
   133  
   134  			err := verifyDB(subBucket, value)
   135  			if err != nil {
   136  				return err
   137  			}
   138  
   139  		default:
   140  			return errors.New("invalid type")
   141  		}
   142  	}
   143  
   144  	keyCount := 0
   145  	err := bucket.ForEach(func(k, v []byte) error {
   146  		keyCount++
   147  		return nil
   148  	})
   149  	if err != nil {
   150  		return err
   151  	}
   152  	if keyCount != len(data) {
   153  		return errors.New("unexpected keys in database")
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  func toHex(v []byte) string {
   160  	if len(v) == 0 {
   161  		return "nil"
   162  	}
   163  
   164  	return "hex(\"" + hex.EncodeToString(v) + "\")"
   165  }
   166  
   167  func toString(v []byte) string {
   168  	readableChars := "abcdefghijklmnopqrstuvwxyz0123456789-"
   169  
   170  	for _, c := range v {
   171  		if !strings.Contains(readableChars, string(c)) {
   172  			return toHex(v)
   173  		}
   174  	}
   175  
   176  	return "\"" + string(v) + "\""
   177  }
   178  
   179  // Hex is a test helper function to convert readable hex arrays to raw byte
   180  // strings.
   181  func Hex(value string) string {
   182  	b, err := hex.DecodeString(value)
   183  	if err != nil {
   184  		panic(err)
   185  	}
   186  	return string(b)
   187  }