Commit df717abc authored by Guillaume Ballet's avatar Guillaume Ballet Committed by Felix Lange

whisper/whisperv6: fix PoW calculations to match the spec (#19330)

This PR fixes two issues in the PoW calculation of a Whisper envelope,
compared to the spec (see PoW Requirements):

- The pow is supposed to take the leading number of zeroes (i.e. most
  significant zeroes) and what it did was to take the number of trailing
  zeroes (i.e. least significant zeroes). It has been fixed to match what
  the spec and Parity does.
- The spec expects to use the size of the RLP encoded envelope, and it took
  something else, as described in #18070.
parent b8b4fb00
......@@ -27,7 +27,6 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/rlp"
......@@ -82,7 +81,7 @@ func (e *Envelope) Seal(options *MessageParams) error {
return nil
}
var target, bestBit int
var target, bestLeadingZeros int
if options.PoW < 0 {
// target is not set - the function should run for a period
// of time specified in WorkTime param. Since we can predict
......@@ -101,10 +100,10 @@ func (e *Envelope) Seal(options *MessageParams) error {
for i := 0; i < 1024; i++ {
binary.BigEndian.PutUint64(buf[56:], nonce)
d := new(big.Int).SetBytes(crypto.Keccak256(buf))
firstBit := math.FirstBitSet(d)
if firstBit > bestBit {
e.Nonce, bestBit = nonce, firstBit
if target > 0 && bestBit >= target {
leadingZeros := 256 - d.BitLen()
if leadingZeros > bestLeadingZeros {
e.Nonce, bestLeadingZeros = nonce, leadingZeros
if target > 0 && bestLeadingZeros >= target {
return nil
}
}
......@@ -112,7 +111,7 @@ func (e *Envelope) Seal(options *MessageParams) error {
}
}
if target > 0 && bestBit < target {
if target > 0 && bestLeadingZeros < target {
return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime)
}
......@@ -130,13 +129,14 @@ func (e *Envelope) PoW() float64 {
func (e *Envelope) calculatePoW(diff uint32) {
buf := make([]byte, 64)
h := crypto.Keccak256(e.rlpWithoutNonce())
rlp := e.rlpWithoutNonce()
h := crypto.Keccak256(rlp)
copy(buf[:32], h)
binary.BigEndian.PutUint64(buf[56:], e.Nonce)
d := new(big.Int).SetBytes(crypto.Keccak256(buf))
firstBit := math.FirstBitSet(d)
x := gmath.Pow(2, float64(firstBit))
x /= float64(e.size())
powHash := new(big.Int).SetBytes(crypto.Keccak256(buf))
leadingZeroes := 256 - powHash.BitLen()
x := gmath.Pow(2, float64(leadingZeroes))
x /= float64(len(rlp))
x /= float64(e.TTL + diff)
e.pow = x
}
......
......@@ -25,6 +25,33 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)
func TestPoWCalculationsWithNoLeadingZeros(t *testing.T) {
e := Envelope{
TTL: 1,
Data: []byte{0xde, 0xad, 0xbe, 0xef},
Nonce: 100000,
}
e.calculatePoW(0)
if e.pow != 0.07692307692307693 {
t.Fatalf("invalid PoW calculation. Expected 0.07692307692307693, got %v", e.pow)
}
}
func TestPoWCalculationsWith8LeadingZeros(t *testing.T) {
e := Envelope{
TTL: 1,
Data: []byte{0xde, 0xad, 0xbe, 0xef},
Nonce: 48159,
}
e.calculatePoW(0)
if e.pow != 40329.846153846156 {
t.Fatalf("invalid PoW calculation. Expected 0.07692307692307693, got %v", e.pow)
}
}
func TestEnvelopeOpenAcceptsOnlyOneKeyTypeInFilter(t *testing.T) {
symKey := make([]byte, aesKeyLength)
mrand.Read(symKey)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment