// TransactionPoolSubscribe adds a subscriber to the transaction pool. // Subscribers will receive all consensus set changes as well as transaction // pool changes, and should not subscribe to both. func (tp *TransactionPool) TransactionPoolSubscribe(subscriber modules.TransactionPoolSubscriber) { lockID := tp.mu.Lock() tp.subscribers = append(tp.subscribers, subscriber) for i := 0; i <= tp.consensusChangeIndex; i++ { cc, err := tp.consensusSet.ConsensusChange(i) if err != nil && build.DEBUG { panic(err) } subscriber.ProcessConsensusChange(cc) // Release the lock between iterations to smooth out performance a bit // - tpool does not need to hold the lock for 15,000 consensus change // objects. tp.mu.Unlock(lockID) runtime.Gosched() lockID = tp.mu.Lock() } // Send the new subscriber the transaction pool set. var txns []types.Transaction var cc modules.ConsensusChange for _, tSet := range tp.transactionSets { txns = append(txns, tSet...) } for _, tSetDiff := range tp.transactionSetDiffs { cc = cc.Append(tSetDiff) } subscriber.ReceiveUpdatedUnconfirmedTransactions(txns, cc) tp.mu.Unlock(lockID) }
// updateSubscribersTransactions sends a new transaction pool update to all // subscribers. func (tp *TransactionPool) updateSubscribersTransactions() { var txns []types.Transaction var cc modules.ConsensusChange for _, tSet := range tp.transactionSets { txns = append(txns, tSet...) } for _, tSetDiff := range tp.transactionSetDiffs { cc = cc.Append(tSetDiff) } for _, subscriber := range tp.subscribers { subscriber.ReceiveUpdatedUnconfirmedTransactions(txns, cc) } }
// TransactionPoolSubscribe adds a subscriber to the transaction pool. // Subscribers will receive the full transaction set every time there is a // signficant change to the transaction pool. func (tp *TransactionPool) TransactionPoolSubscribe(subscriber modules.TransactionPoolSubscriber) { tp.mu.Lock() defer tp.mu.Unlock() // Add the subscriber to the subscriber list. tp.subscribers = append(tp.subscribers, subscriber) // Send the new subscriber the transaction pool set. var txns []types.Transaction for _, tSet := range tp.transactionSets { txns = append(txns, tSet...) } var cc modules.ConsensusChange for _, tSetDiff := range tp.transactionSetDiffs { cc = cc.Append(tSetDiff) } subscriber.ReceiveUpdatedUnconfirmedTransactions(txns, cc) }
// TransactionPoolSubscribe adds a subscriber to the transaction pool. // Subscribers will receive all consensus set changes as well as transaction // pool changes, and should not subscribe to both. func (tp *TransactionPool) TransactionPoolSubscribe(subscriber modules.TransactionPoolSubscriber) { id := tp.mu.Lock() tp.subscribers = append(tp.subscribers, subscriber) for i := 0; i <= tp.consensusChangeIndex; i++ { cc, err := tp.consensusSet.ConsensusChange(i) if err != nil && build.DEBUG { panic(err) } subscriber.ProcessConsensusChange(cc) } // Send the new subscriber the transaction pool set. var txns []types.Transaction var cc modules.ConsensusChange for _, tSet := range tp.transactionSets { txns = append(txns, tSet...) } for _, tSetDiff := range tp.transactionSetDiffs { cc = cc.Append(tSetDiff) } subscriber.ReceiveUpdatedUnconfirmedTransactions(txns, cc) tp.mu.Unlock(id) }
// computeConsensusChange computes the consensus change from the change entry // at index 'i' in the change log. If i is out of bounds, an error is returned. func (cs *ConsensusSet) computeConsensusChange(tx *bolt.Tx, ce changeEntry) (modules.ConsensusChange, error) { cc := modules.ConsensusChange{ ID: ce.ID(), } for _, revertedBlockID := range ce.RevertedBlocks { revertedBlock, err := getBlockMap(tx, revertedBlockID) if err != nil { cs.log.Critical("getBlockMap failed in computeConsensusChange:", err) return modules.ConsensusChange{}, err } // Because the direction is 'revert', the order of the diffs needs to // be flipped and the direction of the diffs also needs to be flipped. cc.RevertedBlocks = append(cc.RevertedBlocks, revertedBlock.Block) for i := len(revertedBlock.SiacoinOutputDiffs) - 1; i >= 0; i-- { scod := revertedBlock.SiacoinOutputDiffs[i] scod.Direction = !scod.Direction cc.SiacoinOutputDiffs = append(cc.SiacoinOutputDiffs, scod) } for i := len(revertedBlock.FileContractDiffs) - 1; i >= 0; i-- { fcd := revertedBlock.FileContractDiffs[i] fcd.Direction = !fcd.Direction cc.FileContractDiffs = append(cc.FileContractDiffs, fcd) } for i := len(revertedBlock.SiafundOutputDiffs) - 1; i >= 0; i-- { sfod := revertedBlock.SiafundOutputDiffs[i] sfod.Direction = !sfod.Direction cc.SiafundOutputDiffs = append(cc.SiafundOutputDiffs, sfod) } for i := len(revertedBlock.DelayedSiacoinOutputDiffs) - 1; i >= 0; i-- { dscod := revertedBlock.DelayedSiacoinOutputDiffs[i] dscod.Direction = !dscod.Direction cc.DelayedSiacoinOutputDiffs = append(cc.DelayedSiacoinOutputDiffs, dscod) } for i := len(revertedBlock.SiafundPoolDiffs) - 1; i >= 0; i-- { sfpd := revertedBlock.SiafundPoolDiffs[i] sfpd.Direction = modules.DiffRevert cc.SiafundPoolDiffs = append(cc.SiafundPoolDiffs, sfpd) } } for _, appliedBlockID := range ce.AppliedBlocks { appliedBlock, err := getBlockMap(tx, appliedBlockID) if err != nil { cs.log.Critical("getBlockMap failed in computeConsensusChange:", err) return modules.ConsensusChange{}, err } cc.AppliedBlocks = append(cc.AppliedBlocks, appliedBlock.Block) for _, scod := range appliedBlock.SiacoinOutputDiffs { cc.SiacoinOutputDiffs = append(cc.SiacoinOutputDiffs, scod) } for _, fcd := range appliedBlock.FileContractDiffs { cc.FileContractDiffs = append(cc.FileContractDiffs, fcd) } for _, sfod := range appliedBlock.SiafundOutputDiffs { cc.SiafundOutputDiffs = append(cc.SiafundOutputDiffs, sfod) } for _, dscod := range appliedBlock.DelayedSiacoinOutputDiffs { cc.DelayedSiacoinOutputDiffs = append(cc.DelayedSiacoinOutputDiffs, dscod) } for _, sfpd := range appliedBlock.SiafundPoolDiffs { cc.SiafundPoolDiffs = append(cc.SiafundPoolDiffs, sfpd) } } // Grab the child target and the minimum valid child timestamp. recentBlock := ce.AppliedBlocks[len(ce.AppliedBlocks)-1] pb, err := getBlockMap(tx, recentBlock) if err != nil { cs.log.Critical("could not find process block for known block") } cc.ChildTarget = pb.ChildTarget cc.MinimumValidChildTimestamp = cs.blockRuleHelper.minimumValidChildTimestamp(tx.Bucket(BlockMap), pb) currentBlock := currentBlockID(tx) if cs.synced && recentBlock == currentBlock { cc.Synced = true } return cc, nil }
// computeConsensusChange computes the consensus change from the change entry // at index 'i' in the change log. If i is out of bounds, an error is returned. func (cs *ConsensusSet) computeConsensusChange(tx *bolt.Tx, ce changeEntry) (modules.ConsensusChange, error) { cc := modules.ConsensusChange{ ID: ce.ID(), } for _, revertedBlockID := range ce.RevertedBlocks { revertedBlock, err := getBlockMap(tx, revertedBlockID) if err != nil { cs.log.Critical("getBlockMap failed in computeConsensusChange:", err) return modules.ConsensusChange{}, err } // Because the direction is 'revert', the order of the diffs needs to // be flipped and the direction of the diffs also needs to be flipped. cc.RevertedBlocks = append(cc.RevertedBlocks, revertedBlock.Block) for i := len(revertedBlock.SiacoinOutputDiffs) - 1; i >= 0; i-- { scod := revertedBlock.SiacoinOutputDiffs[i] scod.Direction = !scod.Direction cc.SiacoinOutputDiffs = append(cc.SiacoinOutputDiffs, scod) } for i := len(revertedBlock.FileContractDiffs) - 1; i >= 0; i-- { fcd := revertedBlock.FileContractDiffs[i] fcd.Direction = !fcd.Direction cc.FileContractDiffs = append(cc.FileContractDiffs, fcd) } for i := len(revertedBlock.SiafundOutputDiffs) - 1; i >= 0; i-- { sfod := revertedBlock.SiafundOutputDiffs[i] sfod.Direction = !sfod.Direction cc.SiafundOutputDiffs = append(cc.SiafundOutputDiffs, sfod) } for i := len(revertedBlock.DelayedSiacoinOutputDiffs) - 1; i >= 0; i-- { dscod := revertedBlock.DelayedSiacoinOutputDiffs[i] dscod.Direction = !dscod.Direction cc.DelayedSiacoinOutputDiffs = append(cc.DelayedSiacoinOutputDiffs, dscod) } for i := len(revertedBlock.SiafundPoolDiffs) - 1; i >= 0; i-- { sfpd := revertedBlock.SiafundPoolDiffs[i] sfpd.Direction = modules.DiffRevert cc.SiafundPoolDiffs = append(cc.SiafundPoolDiffs, sfpd) } } for _, appliedBlockID := range ce.AppliedBlocks { appliedBlock, err := getBlockMap(tx, appliedBlockID) if err != nil { cs.log.Critical("getBlockMap failed in computeConsensusChange:", err) return modules.ConsensusChange{}, err } cc.AppliedBlocks = append(cc.AppliedBlocks, appliedBlock.Block) for _, scod := range appliedBlock.SiacoinOutputDiffs { cc.SiacoinOutputDiffs = append(cc.SiacoinOutputDiffs, scod) } for _, fcd := range appliedBlock.FileContractDiffs { cc.FileContractDiffs = append(cc.FileContractDiffs, fcd) } for _, sfod := range appliedBlock.SiafundOutputDiffs { cc.SiafundOutputDiffs = append(cc.SiafundOutputDiffs, sfod) } for _, dscod := range appliedBlock.DelayedSiacoinOutputDiffs { cc.DelayedSiacoinOutputDiffs = append(cc.DelayedSiacoinOutputDiffs, dscod) } for _, sfpd := range appliedBlock.SiafundPoolDiffs { cc.SiafundPoolDiffs = append(cc.SiafundPoolDiffs, sfpd) } } cc.Synced = cs.synced return cc, nil }
// updateSubscribers will inform all subscribers of the new update to the // consensus set. func (s *State) updateSubscribers(revertedNodes []*blockNode, appliedNodes []*blockNode) { // Sanity check - len(appliedNodes) should never be 0. if build.DEBUG { if len(appliedNodes) == 0 { panic("cannot have len(appliedNodes) = 0 in consensus set - blockchain must always get heavier") } } // Take the nodes and condense them into a consensusChange object. var cc modules.ConsensusChange for _, rn := range revertedNodes { // Because the direction is 'revert', the order of the diffs needs to // be flipped and the direction of the diffs also needs to be flipped. cc.RevertedBlocks = append(cc.RevertedBlocks, rn.block) for i := len(rn.siacoinOutputDiffs) - 1; i >= 0; i-- { scod := rn.siacoinOutputDiffs[i] scod.Direction = !scod.Direction cc.SiacoinOutputDiffs = append(cc.SiacoinOutputDiffs, scod) } for i := len(rn.fileContractDiffs) - 1; i >= 0; i-- { fcd := rn.fileContractDiffs[i] fcd.Direction = !fcd.Direction cc.FileContractDiffs = append(cc.FileContractDiffs, fcd) } for i := len(rn.siafundOutputDiffs) - 1; i >= 0; i-- { sfod := rn.siafundOutputDiffs[i] sfod.Direction = !sfod.Direction cc.SiafundOutputDiffs = append(cc.SiafundOutputDiffs, sfod) } for i := len(rn.delayedSiacoinOutputDiffs) - 1; i >= 0; i-- { dscod := rn.delayedSiacoinOutputDiffs[i] dscod.Direction = !dscod.Direction cc.DelayedSiacoinOutputDiffs = append(cc.DelayedSiacoinOutputDiffs, dscod) } for i := len(rn.siafundPoolDiffs) - 1; i >= 0; i-- { sfpd := rn.siafundPoolDiffs[i] cc.SiafundPoolDiffs = append(cc.SiafundPoolDiffs, sfpd) } } for _, an := range appliedNodes { cc.AppliedBlocks = append(cc.AppliedBlocks, an.block) for _, scod := range an.siacoinOutputDiffs { cc.SiacoinOutputDiffs = append(cc.SiacoinOutputDiffs, scod) } for _, fcd := range an.fileContractDiffs { cc.FileContractDiffs = append(cc.FileContractDiffs, fcd) } for _, sfod := range an.siafundOutputDiffs { cc.SiafundOutputDiffs = append(cc.SiafundOutputDiffs, sfod) } for _, dscod := range an.delayedSiacoinOutputDiffs { cc.DelayedSiacoinOutputDiffs = append(cc.DelayedSiacoinOutputDiffs, dscod) } for _, sfpd := range an.siafundPoolDiffs { cc.SiafundPoolDiffs = append(cc.SiafundPoolDiffs, sfpd) } } // Add the changes to the change set. s.consensusChanges = append(s.consensusChanges, cc) // Notify each update channel that a new update is ready. for _, subscriber := range s.subscriptions { // If the channel is already full, don't block. select { case subscriber <- struct{}{}: default: } } }