Example #1
0
// safeDelete is used to delete a value in an immutable ticket treap, returning
// the mutated, immutable treap given as a result.  It first checks to see if
// the key exists in the treap. If it does not, it returns an error.
func safeDelete(t *tickettreap.Immutable, k tickettreap.Key) (*tickettreap.Immutable, error) {
	if !t.Has(k) {
		return nil, stakeRuleError(ErrMissingTicket, fmt.Sprintf("attempted to "+
			"delete non-existing key %v from treap", chainhash.Hash(k)))
	}

	return t.Delete(k), nil
}
Example #2
0
// safePut is used to put a value into an immutable ticket treap, returning
// the mutated, immutable treap given as a result.  It first checks to see if
// there is already this key in the treap. If there is, it returns an error.
// TODO This function could also check to make sure the states of the ticket
//       treap value are valid.
func safePut(t *tickettreap.Immutable, k tickettreap.Key, v *tickettreap.Value) (*tickettreap.Immutable, error) {
	if t.Has(k) {
		return nil, stakeRuleError(ErrDuplicateTicket, fmt.Sprintf("attempted "+
			"to insert duplicate key %v into treap", chainhash.Hash(k)))
	}

	return t.Put(k, v), nil
}
Example #3
0
// 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
}
Example #4
0
// 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)
}
Example #5
0
// safeGet fetches a pointer to the data for the key in the treap, then
// copies the value so that the original pointer to the key is never written
// to accidentally later.
// TODO This function could also check to make sure the states of the ticket
//       treap value are valid.
func safeGet(t *tickettreap.Immutable, k tickettreap.Key) (*tickettreap.Value, error) {
	v := t.Get(k)
	if v == nil {
		h := chainhash.Hash(k)
		return nil, stakeRuleError(ErrMissingTicket, fmt.Sprintf(
			"ticket %v was supposed to be in the passed "+
				"treap, but could not be found", h))
	}

	return &tickettreap.Value{
		Height:  v.Height,
		Missed:  v.Missed,
		Revoked: v.Revoked,
		Spent:   v.Spent,
		Expired: v.Expired,
	}, nil
}
Example #6
0
func TestFetchWinnersErrors(t *testing.T) {
	treap := new(tickettreap.Immutable)
	for i := 0; i < 0xff; i++ {
		h := chainhash.HashFuncH([]byte{byte(i)})
		v := &tickettreap.Value{
			Height:  uint32(i),
			Missed:  i%2 == 0,
			Revoked: i%2 != 0,
			Spent:   i%2 == 0,
			Expired: i%2 != 0,
		}
		treap = treap.Put(tickettreap.Key(h), v)
	}

	// No indexes.
	_, err := fetchWinners(nil, treap)
	if err == nil {
		t.Errorf("Expected nil slice error")
	}

	// No treap.
	_, err = fetchWinners([]int{1, 2, 3, 4, -1}, nil)
	if err == nil {
		t.Errorf("Expected nil treap error")
	}

	// Bad index too small.
	_, err = fetchWinners([]int{1, 2, 3, 4, -1}, treap)
	if err == nil {
		t.Errorf("Expected index too small error")
	}

	// Bad index too big.
	_, err = fetchWinners([]int{1, 2, 3, 4, 256}, treap)
	if err == nil {
		t.Errorf("Expected index too big error")
	}
}
Example #7
0
// 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)
			}
		}
	}
}
Example #8
0
// copyNode copies a stake node so that it can be manipulated for tests.
func copyNode(n *Node) *Node {
	liveTickets := new(tickettreap.Immutable)
	n.liveTickets.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool {
		liveTickets.Put(k, v)
		return true
	})
	missedTickets := new(tickettreap.Immutable)
	n.missedTickets.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool {
		missedTickets.Put(k, v)
		return true
	})
	revokedTickets := new(tickettreap.Immutable)
	n.revokedTickets.ForEach(func(k tickettreap.Key, v *tickettreap.Value) bool {
		revokedTickets.Put(k, v)
		return true
	})
	databaseUndoUpdate := make(UndoTicketDataSlice, len(n.databaseUndoUpdate))
	copy(databaseUndoUpdate[:], n.databaseUndoUpdate[:])
	databaseBlockTickets := make([]chainhash.Hash, len(n.databaseBlockTickets))
	copy(databaseBlockTickets[:], n.databaseBlockTickets[:])
	nextWinners := make([]chainhash.Hash, len(n.nextWinners))
	copy(nextWinners[:], n.nextWinners[:])
	var finalState [6]byte
	copy(finalState[:], n.finalState[:])

	return &Node{
		height:               n.height,
		liveTickets:          liveTickets,
		missedTickets:        missedTickets,
		revokedTickets:       revokedTickets,
		databaseUndoUpdate:   databaseUndoUpdate,
		databaseBlockTickets: databaseBlockTickets,
		nextWinners:          nextWinners,
		finalState:           finalState,
		params:               n.params,
	}
}
Example #9
0
// 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
}
Example #10
0
// 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")
	}
}