// fetchExpired is a ticket database specific function which iterates over the // entire treap and finds tickets that are equal or less than the given height. // These are returned as a slice of pointers to keys, which can be recast as // []*chainhash.Hash. func fetchExpired(height uint32, t *tickettreap.Immutable) []*tickettreap.Key { var expired []*tickettreap.Key t.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool { if v.Height <= height { expired = append(expired, &k) } return true }) return expired }
// compareTreap dumps two treaps into maps and compares them with deep equal. func compareTreap(a *tickettreap.Immutable, b *tickettreap.Immutable) bool { aMap := make(map[tickettreap.Key]tickettreap.Value) a.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool { aMap[k] = *v return true }) bMap := make(map[tickettreap.Key]tickettreap.Value) b.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool { bMap[k] = *v return true }) return reflect.DeepEqual(aMap, bMap) }
// findDifferences finds individual differences in two treaps and prints // them. For use in debugging. func findDifferences(a *tickettreap.Immutable, b *tickettreap.Immutable) { aMap := make(map[tickettreap.Key]*tickettreap.Value) a.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool { aMap[k] = v return true }) bMap := make(map[tickettreap.Key]*tickettreap.Value) b.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool { bMap[k] = v return true }) for k, v := range aMap { h := chainhash.Hash(k) vB := bMap[k] if vB == nil { fmt.Printf("Second map missing key %v\n", h) } else { if *v != *vB { fmt.Printf("Second map val for %v is %v, first map %v\n", h, vB, v) } } } for k, v := range bMap { h := chainhash.Hash(k) vA := aMap[k] if vA == nil { fmt.Printf("First map missing key %v\n", h) } else { if *v != *vA { fmt.Printf("First map val for %v is %v, second map %v\n", h, vA, v) } } } }
// fetchWinners is a ticket database specific function which iterates over the // entire treap and finds winners at selected indexes. These are returned // as a slice of pointers to keys, which can be recast as []*chainhash.Hash. // Importantly, it maintains the list of winners in the same order as specified // in the original idxs passed to the function. func fetchWinners(idxs []int, t *tickettreap.Immutable) ([]*tickettreap.Key, error) { if idxs == nil { return nil, fmt.Errorf("empty idxs list") } if t == nil || t.Len() == 0 { return nil, fmt.Errorf("missing or empty treap") } // maxInt returns the maximum integer from a list of integers. maxInt := func(idxs []int) int { max := math.MinInt32 for _, i := range idxs { if i > max { max = i } } return max } max := maxInt(idxs) if max >= t.Len() { return nil, fmt.Errorf("idx %v out of bounds", max) } minInt := func(idxs []int) int { min := math.MaxInt32 for _, i := range idxs { if i < min { min = i } } return min } min := minInt(idxs) if min < 0 { return nil, fmt.Errorf("idx %v out of bounds", min) } originalIdxs := make([]int, len(idxs)) copy(originalIdxs[:], idxs[:]) sortedIdxs := sort.IntSlice(idxs) sort.Sort(sortedIdxs) // originalIdx returns the original index of the lucky // number in the idxs slice, so that the order is correct. originalIdx := func(idx int) int { for i := range originalIdxs { if idx == originalIdxs[i] { return i } } // This will cause a panic. It should never, ever // happen because the investigated index will always // be in the original indexes. return -1 } idx := 0 winnerIdx := 0 winners := make([]*tickettreap.Key, len(idxs)) t.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool { if idx > max { return false } if idx == sortedIdxs[winnerIdx] { winners[originalIdx(idx)] = &k if winnerIdx+1 < len(sortedIdxs) { winnerIdx++ } } idx++ return true }) return winners, nil }
// TestLiveDatabase tests various functions that require a live database. func TestLiveDatabase(t *testing.T) { // Create a new database to store the accepted stake node data into. dbName := "ffldb_ticketdb_test" dbPath := filepath.Join(testDbRoot, dbName) _ = os.RemoveAll(dbPath) testDb, err := database.Create(testDbType, dbPath, chaincfg.SimNetParams.Net) if err != nil { t.Fatalf("error creating db: %v", err) } // Setup a teardown. defer os.RemoveAll(dbPath) defer os.RemoveAll(testDbRoot) defer testDb.Close() // Initialize the database, then try to read the version. err = testDb.Update(func(dbTx database.Tx) error { return DbCreate(dbTx) }) if err != nil { t.Fatalf("%v", err.Error()) } var dbi *DatabaseInfo err = testDb.View(func(dbTx database.Tx) error { dbi, err = DbFetchDatabaseInfo(dbTx) if err != nil { return err } return nil }) if err != nil { t.Fatalf("%v", err.Error()) } if dbi.Version != currentDatabaseVersion { t.Fatalf("bad version after reading from DB; want %v, got %v", currentDatabaseVersion, dbi.Version) } // Test storing arbitrary ticket treaps. ticketMap := make(map[tickettreap.Key]*tickettreap.Value) tickets := make([]chainhash.Hash, 5) for i := 0; i < 4; i++ { h := chainhash.HashFuncH(bytes.Repeat([]byte{0x01}, i)) ticketMap[tickettreap.Key(h)] = &tickettreap.Value{ Height: 12345 + uint32(i), Missed: i%2 == 0, Revoked: i%2 != 0, Spent: i%2 == 0, Expired: i%2 != 0, } tickets[i] = h } err = testDb.Update(func(dbTx database.Tx) error { for k, v := range ticketMap { h := chainhash.Hash(k) err = DbPutTicket(dbTx, dbnamespace.LiveTicketsBucketName, &h, v.Height, v.Missed, v.Revoked, v.Spent, v.Expired) if err != nil { return err } } return nil }) if err != nil { t.Fatalf("%v", err.Error()) } var treap *tickettreap.Immutable ticketMap2 := make(map[tickettreap.Key]*tickettreap.Value) err = testDb.View(func(dbTx database.Tx) error { treap, err = DbLoadAllTickets(dbTx, dbnamespace.LiveTicketsBucketName) if err != nil { return err } return nil }) if err != nil { t.Fatalf("%v", err.Error()) } treap.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool { ticketMap2[k] = v return true }) if !reflect.DeepEqual(ticketMap, ticketMap2) { t.Fatalf("not same ticket maps") } }