Пример #1
0
// WriteDisconnectedBestNode writes the newly connected best node to the database
// under an atomic database transaction, performing all the necessary writes to
// reverse the contents of the database buckets for live, missed, and revoked
// tickets.  It does so by using the parent's block undo data to restore the
// original state in the database.  It also drops new ticket and reversion data
// for any nodes that have a height higher than this one after.
func WriteDisconnectedBestNode(dbTx database.Tx, node *Node, hash chainhash.Hash, childUndoData UndoTicketDataSlice) error {
	// Load the last best node and check to see if its height is above the
	// current node. If it is, drop all reversion data above this incoming
	// node.
	formerBest, err := ticketdb.DbFetchBestState(dbTx)
	if err != nil {
		return err
	}

	if formerBest.Height > node.height {
		for i := formerBest.Height; i > node.height; i-- {
			err := ticketdb.DbDropBlockUndoData(dbTx, i)
			if err != nil {
				return err
			}

			err = ticketdb.DbDropNewTickets(dbTx, i)
			if err != nil {
				return err
			}
		}
	}

	// Iterate through the block undo data and write all database
	// changes to the respective on-disk map, reversing all the
	// changes added when the child block was added to the block
	// chain.
	for _, undo := range childUndoData {
		var err error
		switch {
		// All flags are unset; this is a newly added ticket.
		// Remove it from the list of live tickets.
		case !undo.Missed && !undo.Revoked && !undo.Spent:
			err = ticketdb.DbDeleteTicket(dbTx, dbnamespace.LiveTicketsBucketName,
				&undo.TicketHash)
			if err != nil {
				return err
			}

		// The ticket was missed and revoked.  It needs to
		// be moved from the revoked ticket treap to the
		// missed ticket treap.
		case undo.Missed && undo.Revoked:
			err = ticketdb.DbDeleteTicket(dbTx,
				dbnamespace.RevokedTicketsBucketName,
				&undo.TicketHash)
			if err != nil {
				return err
			}
			err = ticketdb.DbPutTicket(dbTx, dbnamespace.MissedTicketsBucketName,
				&undo.TicketHash, undo.TicketHeight, undo.Missed, false,
				undo.Spent, undo.Expired)
			if err != nil {
				return err
			}

		// The ticket was missed and was previously live.
		// Remove it from the missed tickets bucket and
		// move it to the live tickets bucket.  We don't
		// know if it was expired or not, so just set that
		// flag to false.
		case undo.Missed && !undo.Revoked:
			err = ticketdb.DbDeleteTicket(dbTx,
				dbnamespace.MissedTicketsBucketName,
				&undo.TicketHash)
			if err != nil {
				return err
			}
			err = ticketdb.DbPutTicket(dbTx, dbnamespace.LiveTicketsBucketName,
				&undo.TicketHash, undo.TicketHeight, false, undo.Revoked,
				undo.Spent, false)
			if err != nil {
				return err
			}

		// The ticket was spent. Reinsert it into the live
		// tickets treap.
		case undo.Spent:
			err = ticketdb.DbPutTicket(dbTx, dbnamespace.LiveTicketsBucketName,
				&undo.TicketHash, undo.TicketHeight, undo.Missed, undo.Revoked,
				false, undo.Expired)
			if err != nil {
				return err
			}

		default:
			return stakeRuleError(ErrMemoryCorruption,
				"unknown ticket state in undo data")
		}
	}

	// Write the new block undo and new tickets data to the
	// database for the given height, potentially overwriting
	// an old entry with the new data.
	err = ticketdb.DbPutBlockUndoData(dbTx, node.height,
		node.databaseUndoUpdate)
	if err != nil {
		return err
	}

	err = ticketdb.DbPutNewTickets(dbTx, node.height,
		node.databaseBlockTickets)
	if err != nil {
		return err
	}

	// Write the new best state to the database.
	nextWinners := make([]chainhash.Hash, int(node.params.TicketsPerBlock))
	if node.height >= uint32(node.params.StakeValidationHeight-1) {
		for i := range nextWinners {
			nextWinners[i] = node.nextWinners[i]
		}
	}

	err = ticketdb.DbPutBestState(dbTx, ticketdb.BestChainState{
		Hash:        hash,
		Height:      node.height,
		Live:        uint32(node.liveTickets.Len()),
		Missed:      uint64(node.missedTickets.Len()),
		Revoked:     uint64(node.revokedTickets.Len()),
		PerBlock:    node.params.TicketsPerBlock,
		NextWinners: nextWinners,
	})
	if err != nil {
		return err
	}

	return nil
}
Пример #2
0
// WriteConnectedBestNode writes the newly connected best node to the database
// under an atomic database transaction, performing all the necessary writes to
// the database buckets for live, missed, and revoked tickets.
func WriteConnectedBestNode(dbTx database.Tx, node *Node, hash chainhash.Hash) error {
	// Iterate through the block undo data and write all database
	// changes to the respective on-disk map.
	for _, undo := range node.databaseUndoUpdate {
		var err error
		switch {
		// All flags are unset; this is a newly added ticket.
		// Insert it into the live ticket database.
		case !undo.Missed && !undo.Revoked && !undo.Spent:
			err = ticketdb.DbPutTicket(dbTx, dbnamespace.LiveTicketsBucketName,
				&undo.TicketHash, undo.TicketHeight, undo.Missed, undo.Revoked,
				undo.Spent, undo.Expired)
			if err != nil {
				return err
			}

		// The ticket was missed and revoked.  It needs to
		// be moved from the missed ticket bucket to the
		// revoked ticket bucket.
		case undo.Missed && undo.Revoked:
			err = ticketdb.DbDeleteTicket(dbTx,
				dbnamespace.MissedTicketsBucketName,
				&undo.TicketHash)
			if err != nil {
				return err
			}
			err = ticketdb.DbPutTicket(dbTx, dbnamespace.RevokedTicketsBucketName,
				&undo.TicketHash, undo.TicketHeight, undo.Missed, undo.Revoked,
				undo.Spent, undo.Expired)
			if err != nil {
				return err
			}

		// The ticket was missed and was previously live.
		// Move it from the live ticket bucket to the missed
		// ticket bucket.
		case undo.Missed && !undo.Revoked:
			err = ticketdb.DbDeleteTicket(dbTx,
				dbnamespace.LiveTicketsBucketName,
				&undo.TicketHash)
			if err != nil {
				return err
			}
			err = ticketdb.DbPutTicket(dbTx, dbnamespace.MissedTicketsBucketName,
				&undo.TicketHash, undo.TicketHeight, true, undo.Revoked,
				undo.Spent, undo.Expired)
			if err != nil {
				return err
			}

		// The ticket was spent.  Remove it from the live
		// ticket bucket.
		case undo.Spent:
			err = ticketdb.DbDeleteTicket(dbTx, dbnamespace.LiveTicketsBucketName,
				&undo.TicketHash)
			if err != nil {
				return err
			}

		default:
			return stakeRuleError(ErrMemoryCorruption,
				"unknown ticket state in undo data")
		}
	}

	// Write the new block undo and new tickets data to the
	// database for the given height, potentially overwriting
	// an old entry with the new data.
	err := ticketdb.DbPutBlockUndoData(dbTx, node.height,
		node.databaseUndoUpdate)
	if err != nil {
		return err
	}

	err = ticketdb.DbPutNewTickets(dbTx, node.height,
		node.databaseBlockTickets)
	if err != nil {
		return err
	}

	// Write the new best state to the database.
	nextWinners := make([]chainhash.Hash, int(node.params.TicketsPerBlock))
	if node.height >= uint32(node.params.StakeValidationHeight-1) {
		for i := range nextWinners {
			nextWinners[i] = node.nextWinners[i]
		}
	}

	err = ticketdb.DbPutBestState(dbTx, ticketdb.BestChainState{
		Hash:        hash,
		Height:      node.height,
		Live:        uint32(node.liveTickets.Len()),
		Missed:      uint64(node.missedTickets.Len()),
		Revoked:     uint64(node.revokedTickets.Len()),
		PerBlock:    node.params.TicketsPerBlock,
		NextWinners: nextWinners,
	})
	if err != nil {
		return err
	}

	return nil
}