Skip to content

Commit

Permalink
Reject too low stake difficulty transactions and cache difficulty (#154)
Browse files Browse the repository at this point in the history
The mempool would allow low stake difficulty tickets and then remove
them after the next block was added. This prevents too low difficulty
tickets from being added in the first place. It also refactors some of
the old code in mempool.go, blockmanager.go, and mining.go for
efficiency.
  • Loading branch information
cjepson authored and alexlyp committed May 5, 2016
1 parent e33e2b9 commit 65999dc
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 63 deletions.
104 changes: 56 additions & 48 deletions blockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,15 +469,16 @@ type headerNode struct {
// is inserted and protecting it with a mutex.
type chainState struct {
sync.Mutex
newestHash *chainhash.Hash
newestHeight int64
nextFinalState [6]byte
nextPoolSize uint32
winningTickets []chainhash.Hash
missedTickets []chainhash.Hash
curBlockHeader *wire.BlockHeader
pastMedianTime time.Time
pastMedianTimeErr error
newestHash *chainhash.Hash
newestHeight int64
nextFinalState [6]byte
nextPoolSize uint32
nextStakeDifficulty int64
winningTickets []chainhash.Hash
missedTickets []chainhash.Hash
curBlockHeader *wire.BlockHeader
pastMedianTime time.Time
pastMedianTimeErr error
}

// Best returns the block hash and height known for the tip of the best known
Expand Down Expand Up @@ -609,6 +610,7 @@ func (b *blockManager) updateChainState(newestHash *chainhash.Hash,
newestHeight int64,
finalState [6]byte,
poolSize uint32,
nextStakeDiff int64,
winningTickets []chainhash.Hash,
missedTickets []chainhash.Hash,
curBlockHeader *wire.BlockHeader) {
Expand All @@ -627,6 +629,7 @@ func (b *blockManager) updateChainState(newestHash *chainhash.Hash,

b.chainState.nextFinalState = finalState
b.chainState.nextPoolSize = poolSize
b.chainState.nextStakeDifficulty = nextStakeDiff
b.chainState.winningTickets = winningTickets
b.chainState.missedTickets = missedTickets
b.chainState.curBlockHeader = curBlockHeader
Expand Down Expand Up @@ -1330,32 +1333,31 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
// Retrieve the current block header.
curBlockHeader := b.blockChain.GetCurrentBlockHeader()

if r != nil {
nextStakeDiff, errSDiff :=
b.blockChain.CalcNextRequiredStakeDifficulty()
if errSDiff != nil {
bmgrLog.Warnf("Failed to get next stake difficulty "+
"calculation: %v", err)
}
if r != nil && errSDiff == nil {
// Update registered websocket clients on the
// current stake difficulty.
nextStakeDiff, err :=
b.blockChain.CalcNextRequiredStakeDifficulty()
if err != nil {
bmgrLog.Warnf("Failed to get next stake difficulty "+
"calculation: %v", err)

} else {
r.ntfnMgr.NotifyStakeDifficulty(
&StakeDifficultyNtfnData{
*newestSha,
newestHeight,
nextStakeDiff,
})
b.server.txMemPool.PruneStakeTx(nextStakeDiff,
b.chainState.newestHeight)
b.server.txMemPool.PruneExpiredTx(b.chainState.newestHeight)
}
r.ntfnMgr.NotifyStakeDifficulty(
&StakeDifficultyNtfnData{
*newestSha,
newestHeight,
nextStakeDiff,
})
b.server.txMemPool.PruneStakeTx(nextStakeDiff,
b.chainState.newestHeight)
b.server.txMemPool.PruneExpiredTx(b.chainState.newestHeight)
}

b.updateChainState(newestSha,
newestHeight,
lotteryData.finalState,
lotteryData.poolSize,
nextStakeDiff,
lotteryData.ntfnData.Tickets,
missedTickets,
curBlockHeader)
Expand Down Expand Up @@ -1888,27 +1890,26 @@ out:
}
b.blockLotteryDataCacheMutex.Unlock()

// Update registered websocket clients on the
// current stake difficulty.
nextStakeDiff, errSDiff :=
b.blockChain.CalcNextRequiredStakeDifficulty()
if err != nil {
bmgrLog.Warnf("Failed to get next stake difficulty "+
"calculation: %v", err)
}
r := b.server.rpcServer
if r != nil {
// Update registered websocket clients on the
// current stake difficulty.
nextStakeDiff, err :=
b.blockChain.CalcNextRequiredStakeDifficulty()
if err != nil {
bmgrLog.Warnf("Failed to get next stake difficulty "+
"calculation: %v", err)
} else {
r.ntfnMgr.NotifyStakeDifficulty(
&StakeDifficultyNtfnData{
*newestSha,
newestHeight,
nextStakeDiff,
})
b.server.txMemPool.PruneStakeTx(nextStakeDiff,
b.chainState.newestHeight)
b.server.txMemPool.PruneExpiredTx(
b.chainState.newestHeight)
}
if r != nil && errSDiff == nil {
r.ntfnMgr.NotifyStakeDifficulty(
&StakeDifficultyNtfnData{
*newestSha,
newestHeight,
nextStakeDiff,
})
b.server.txMemPool.PruneStakeTx(nextStakeDiff,
b.chainState.newestHeight)
b.server.txMemPool.PruneExpiredTx(
b.chainState.newestHeight)
}

missedTickets := b.blockChain.GetMissedTickets()
Expand All @@ -1919,6 +1920,7 @@ out:
newestHeight,
lotteryData.finalState,
lotteryData.poolSize,
nextStakeDiff,
lotteryData.ntfnData.Tickets,
missedTickets,
curBlockHeader)
Expand Down Expand Up @@ -2058,6 +2060,7 @@ out:
newestHeight,
lotteryData.finalState,
lotteryData.poolSize,
nextStakeDiff,
lotteryData.ntfnData.Tickets,
missedTickets,
curBlockHeader)
Expand Down Expand Up @@ -2928,13 +2931,18 @@ func newBlockManager(s *server) (*blockManager, error) {
return nil, err
}

// Retrieve the current block header.
// Retrieve the current block header and next stake difficulty.
curBlockHeader := bm.blockChain.GetCurrentBlockHeader()
nextStakeDiff, err := bm.blockChain.CalcNextRequiredStakeDifficulty()
if err != nil {
return nil, err
}

bm.updateChainState(newestHash,
height,
fs,
uint32(ps),
nextStakeDiff,
wt,
missedTickets,
curBlockHeader)
Expand Down
29 changes: 22 additions & 7 deletions mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1260,10 +1260,24 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew,
}
}

isSSGen, _ := stake.IsSSGen(tx)
isSSRtx, _ := stake.IsSSRtx(tx)
if isSSGen || isSSRtx {
if isSSGen {
// If the transaction is a ticket, ensure that it meets the next
// stake difficulty.
if txType == stake.TxTypeSStx {
mp.server.blockManager.chainState.Lock()
sDiff := mp.server.blockManager.chainState.nextStakeDifficulty
mp.server.blockManager.chainState.Unlock()

if tx.MsgTx().TxOut[0].Value < sDiff {
str := fmt.Sprintf("transaction %v has not enough funds "+
"to meet stake difficuly (ticket diff %v < next diff %v)",
txHash, tx.MsgTx().TxOut[0].Value, sDiff)
return nil, txRuleError(wire.RejectInsufficientFee, str)
}
}

// Handle stake transaction double spending exceptions.
if (txType == stake.TxTypeSSGen) || (txType == stake.TxTypeSSRtx) {
if txType == stake.TxTypeSSGen {
ssGenAlreadyFound := 0
for _, mpTx := range mp.pool {
if mpTx.GetType() == stake.TxTypeSSGen {
Expand All @@ -1282,7 +1296,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew,
}
}

if isSSRtx {
if txType == stake.TxTypeSSRtx {
for _, mpTx := range mp.pool {
if mpTx.GetType() == stake.TxTypeSSRtx {
if mpTx.Tx.MsgTx().TxIn[0].PreviousOutPoint ==
Expand Down Expand Up @@ -1388,15 +1402,16 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew,
// the coinbase address itself can contain signature operations, the
// maximum allowed signature operations per transaction is less than
// the maximum allowed signature operations per block.
numSigOps, err := blockchain.CountP2SHSigOps(tx, false, isSSGen, txStore)
numSigOps, err := blockchain.CountP2SHSigOps(tx, false,
(txType == stake.TxTypeSSGen), txStore)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
}
return nil, err
}

numSigOps += blockchain.CountSigOps(tx, false, isSSGen)
numSigOps += blockchain.CountSigOps(tx, false, (txType == stake.TxTypeSSGen))
if numSigOps > maxSigOpsPerTx {
str := fmt.Sprintf("transaction %v has too many sigops: %d > %d",
txHash, numSigOps, maxSigOpsPerTx)
Expand Down
9 changes: 1 addition & 8 deletions mining.go
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,7 @@ func NewBlockTemplate(mempool *txMemPool,
prevHash := chainState.newestHash
nextBlockHeight := chainState.newestHeight + 1
poolSize := chainState.nextPoolSize
requiredStakeDifficulty := chainState.nextStakeDifficulty
finalState := chainState.nextFinalState
winningTickets := make([]chainhash.Hash, len(chainState.winningTickets),
len(chainState.winningTickets))
Expand Down Expand Up @@ -1208,14 +1209,6 @@ func NewBlockTemplate(mempool *txMemPool,
}
}

// Get the next required stake difficulty so we can determine SStx
// eligibility.
requiredStakeDifficulty, err := blockManager.CalcNextRequiredStakeDifficulty()
if err != nil {
return nil, miningRuleError(ErrGetStakeDifficulty, "couldn't get "+
"stake difficulty")
}

// Get the current memory pool transactions and create a priority queue
// to hold the transactions which are ready for inclusion into a block
// along with some priority related and fee metadata. Reserve the same
Expand Down

0 comments on commit 65999dc

Please sign in to comment.