BSC 出块慢slash惩罚机制

最近遇到验证者老是被惩罚的问题,当然我把出块时间改成了1秒:

1. validatorMisdemeanor | 23007 | 0x3bade2d948437c739ead715226f18df150caa7d576ec5cf3c7f5bbbf23916d81 | 0xB17A1bD545B45a918bf6b0450E88B9cafc0914a9 | 35438812500000000000
				2. validatorMisdemeanor | 28188 | 0xd406cfc149516801eca3b3d2ec8a08eccd404455a9e0d3235b67a847d5aa3edc | 0xb6CB6ecb011C7a72b1926eD3Bf4ed04e6E2e67E3 | 46123726610000000000
				3. validatorMisdemeanor | 29375 | 0xcfaf727488be9825dfc96d42e36bf9c6a2308c3640214423ec5f4eb1fbf32845 | 0x7E4B00584651118e05F2d74897166902C63c75B7 | 55306424675000000000
				1. validatorMisdemeanor | 35329 | 0x4c9755958b42e3ade46d7ac1b14da49aaf9916557a5f7c0703fa3a026a8d620f | 0xC456b3BCC41DA5eA6d2917C00a0df39948bC07D0 | 63357592737500000000
				2. validatorMisdemeanor | 35355 | 0x5dadf2ff91a73ee0a1a405684e2ab7a392ba8ed027a5a41d1cf4e3782aa7c60d | 0x7E4B00584651118e05F2d74897166902C63c75B7 | 15839398184375000000
				3. validatorMisdemeanor | 36421 | 0x49007036447cdaefdd5e64653d18d2e8e85aee4d495af28f13f83c8893ace605 | 0xA0754ecDD8C0867986e462Ddc97089BD21A3cC1e | 87471386454218750000
				1. validatorFelony | 69175 | 0x0310fb4753f974a8299fedb07222366b918e31231067c928d209af5dc60004b9 | 0x7E4B00584651118e05F2d74897166902C63c75B7 | 64490270928004760741

image

验证者改成1秒以后,很容易因为出块慢被惩罚:
出块间隔过短: 1秒的出块间隔给验证者留下的处理时间很有限
网络延迟: 验证者之间的网络通信延迟可能导致无法及时出块
系统负载: 验证者节点的系统负载过高,导致出块处理时间延长
共识机制严格: Parlia共识机制对时间要求很严格,稍有延迟就会触发惩罚

看下共识代码,节点插入区块的时候会判断出块是否及时,是否需要惩罚

func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, txs *[]*types.Transaction,
	if header.Difficulty.Cmp(diffInTurn) != 0 {
		spoiledVal := snap.inturnValidator()
		signedRecently := false
		if p.chainConfig.IsPlato(header.Number) {
			signedRecently = snap.SignRecently(spoiledVal)
		} else {
			for _, recent := range snap.Recents {
				if recent == spoiledVal {
					signedRecently = true
					break
				}
			}
		}

		if !signedRecently {
			log.Trace("slash validator", "block hash", header.Hash(), "address", spoiledVal)
			err = p.slash(spoiledVal, state, header, cx, txs, receipts, systemTxs, usedGas, false, tracer)
			if err != nil {
				// it is possible that slash validator failed because of the slash channel is disabled.
				log.Error("slash validator failed", "block hash", header.Hash(), "address", spoiledVal, "err", err)
			}
		}
	}

看下SignRecently实现


func (s *Snapshot) minerHistoryCheckLen() uint64 {
	return (uint64(len(s.Validators))/2+1)*uint64(s.TurnLength) - 1
}

func (s *Snapshot) countRecents() map[common.Address]uint8 {
	leftHistoryBound := uint64(0) // the bound is excluded
	checkHistoryLength := s.minerHistoryCheckLen()
	if s.Number > checkHistoryLength {
		leftHistoryBound = s.Number - checkHistoryLength
	}
	counts := make(map[common.Address]uint8, len(s.Validators))
	for seen, recent := range s.Recents {
		if seen <= leftHistoryBound || recent == (common.Address{}) /*when seen == `epochKey`*/ {
			continue
		}
		counts[recent] += 1
	}
	return counts
}

func (s *Snapshot) signRecentlyByCounts(validator common.Address, counts map[common.Address]uint8) bool {
	if seenTimes, ok := counts[validator]; ok && seenTimes >= s.TurnLength {
		if seenTimes > s.TurnLength {
			log.Warn("produce more blocks than expected!", "validator", validator, "seenTimes", seenTimes)
		}
		return true
	}

	return false
}

func (s *Snapshot) SignRecently(validator common.Address) bool {
	return s.signRecentlyByCounts(validator, s.countRecents())
}

历史检查窗口长度,如果是5个验证者的话,等于(5/2+1) * 1 - 1 = 2, 也即是检查最近的2个区块

TurnLength,默认为1,也就是说,在最近2个区块中,验证者必须要出块,否则就面临惩罚,

接下来看下惩罚的系统合约实现:


contract SlashIndicator is ISlashIndicator, System, IParamSubscriber, IApplication {
    using RLPEncode for *;

    uint256 public constant MISDEMEANOR_THRESHOLD = 50;
    uint256 public constant FELONY_THRESHOLD = 150;

    /*----------------- External func -----------------*/
    /**
     * @dev Slash the validator who should have produced the current block
     *
     * @param validator The validator who should have produced the current block
     */
    function slash(address validator) external onlyCoinbase onlyInit oncePerBlock onlyZeroGasPrice {
        if (!IBSCValidatorSet(VALIDATOR_CONTRACT_ADDR).isCurrentValidator(validator)) {
            return;
        }
        Indicator memory indicator = indicators[validator];
        if (indicator.exist) {
            ++indicator.count;
        } else {
            indicator.exist = true;
            indicator.count = 1;
            validators.push(validator);
        }
        indicator.height = block.number;
        if (indicator.count % felonyThreshold == 0) {
            indicator.count = 0;
            IBSCValidatorSet(VALIDATOR_CONTRACT_ADDR).felony(validator);
            _downtimeSlash(validator, indicator.count, false);
        } else if (indicator.count % misdemeanorThreshold == 0) {
            IBSCValidatorSet(VALIDATOR_CONTRACT_ADDR).misdemeanor(validator);
        }
        indicators[validator] = indicator;
        emit validatorSlashed(validator);
    }

从SlashDicator合约可以看出,达到50次slash就面临misdemeanor轻微惩罚,达到150次slash就面临felony删除验证者资格的惩罚

posted @ 2025-08-14 10:58  若-飞  阅读(18)  评论(0)    收藏  举报