区块链时间戳

区块链中如何防止时间戳攻击? 在比特币和以太坊都有经典的实现

Tendermint 使用 MedianTime 获取"带权中位时间"作为新的区块的时间戳

// MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the
// corresponding validator set. The computed time is always between timestamps of
// the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the
// computed value.
func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time {

	weightedTimes := make([]*tmtime.WeightedTime, len(commit.Precommits))
	totalVotingPower := int64(0)

	for i, vote := range commit.Precommits {
		if vote != nil {
			_, validator := validators.GetByIndex(vote.ValidatorIndex)
			totalVotingPower += validator.VotingPower
			weightedTimes[i] = tmtime.NewWeightedTime(vote.Timestamp, validator.VotingPower)
		}
	}

	return tmtime.WeightedMedian(weightedTimes, totalVotingPower)
}


// ==== WeightedMedian 实现
// WeightedMedian computes weighted median time for a given array of WeightedTime and the total voting power.
func WeightedMedian(weightedTimes []*WeightedTime, totalVotingPower int64) (res time.Time) {
	median := totalVotingPower / 2

	// 根据时间从小到大排序
	sort.Slice(weightedTimes, func(i, j int) bool {
		if weightedTimes[i] == nil {
			return false
		}
		if weightedTimes[j] == nil {
			return true
		}
		return weightedTimes[i].Time.UnixNano() < weightedTimes[j].Time.UnixNano()
	})

	for _, weightedTime := range weightedTimes {
		if weightedTime != nil {
			if median <= weightedTime.Weight {
				res = weightedTime.Time
				break
			}
			median -= weightedTime.Weight
		}
	}
	return
}

单元测试:


func TestWeightedMedian(t *testing.T) {
	m := make([]*WeightedTime, 3)

	t1 := Now()
	t2 := t1.Add(5 * time.Second)
	t3 := t1.Add(10 * time.Second)

	m[2] = NewWeightedTime(t1, 33) // faulty processes
	m[0] = NewWeightedTime(t2, 40) // correct processes
	m[1] = NewWeightedTime(t3, 27) // correct processes
	totalVotingPower := int64(100)

	median := WeightedMedian(m, totalVotingPower)
	assert.Equal(t, t2, median)
	// median always returns value between values of correct processes
	assert.Equal(t, true, (median.After(t1) || median.Equal(t1)) &&
		(median.Before(t3) || median.Equal(t3)))

	m[1] = NewWeightedTime(t1, 40) // correct processes
	m[2] = NewWeightedTime(t2, 27) // correct processes
	m[0] = NewWeightedTime(t3, 33) // faulty processes
	totalVotingPower = int64(100)

	median = WeightedMedian(m, totalVotingPower)
	assert.Equal(t, t2, median)
	// median always returns value between values of correct processes
	assert.Equal(t, true, (median.After(t1) || median.Equal(t1)) &&
		(median.Before(t2) || median.Equal(t2)))

	m = make([]*WeightedTime, 8)
	t4 := t1.Add(15 * time.Second)
	t5 := t1.Add(60 * time.Second)

	m[3] = NewWeightedTime(t1, 10) // correct processes
	m[1] = NewWeightedTime(t2, 10) // correct processes
	m[5] = NewWeightedTime(t2, 10) // correct processes
	m[4] = NewWeightedTime(t3, 23) // faulty processes
	m[0] = NewWeightedTime(t4, 20) // correct processes
	m[7] = NewWeightedTime(t5, 10) // faulty processes
	totalVotingPower = int64(83)

	median = WeightedMedian(m, totalVotingPower)
	assert.Equal(t, t3, median)
	// median always returns value between values of correct processes
	assert.Equal(t, true, (median.After(t1) || median.Equal(t1)) &&
		(median.Before(t4) || median.Equal(t4)))
}

你可能感兴趣的:(区块链,区块链,golang,比特币)