Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HF3 and Cuckarooz logic #17

Merged
merged 2 commits into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions core/consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,22 +125,49 @@ const FloonetFirstHardFork uint64 = 185040
// FloonetSecondHardFork is the Floonet second hard fork height, set to happen around 2019-12-19
const FloonetSecondHardFork uint64 = 298080

// FloonetThirdHardFork is the Floonet second hard fork height, set to happen around 2020-06-20
const FloonetThirdHardFork uint64 = 552960

// TestingFirstHardFork is the AutomatedTesting and UserTesting HF1 height
const TestingFirstHardFork uint64 = 3

// TestingSecondHardFork is the AutomatedTesting and UserTesting HF2 height
const TestingSecondHardFork uint64 = 6

// TestingThirdHardFork is the AutomatedTesting and UserTesting HF3 height
const TestingThirdHardFork uint64 = 9

// HeaderVersion compute possible block version at a given height, implements
// 6 months interval scheduled hard forks for the first 2 years.
func HeaderVersion(chainType ChainType, height uint64) uint16 {
hfInterval := uint16(1 + height/HardForkInterval)
switch chainType {
case Mainnet:
return hfInterval
case Floonet:
if height < FloonetFirstHardFork {
return 1
} else if height < FloonetSecondHardFork {
return 2
} else if height < 3*HardForkInterval {
} else if height < FloonetThirdHardFork {
return 3
} else if height < 3*HardForkInterval {
return 4
} else {
return hfInterval
}
// everything else just like mainnet
case AutomatedTesting, UserTesting:
if height < TestingFirstHardFork {
return 1
} else if height < TestingSecondHardFork {
return 2
} else if height < TestingThirdHardFork {
return 3
} else if height < 4*HardForkInterval {
return 4
} else {
return 5
}
default:
return hfInterval
}
Expand All @@ -149,7 +176,7 @@ func HeaderVersion(chainType ChainType, height uint64) uint16 {
// ValidHeaderVersion check whether the block version is valid at a given height, implements
// 6 months interval scheduled hard forks for the first 2 years.
func ValidHeaderVersion(chainType ChainType, height uint64, version uint16) bool {
return height < 3*HardForkInterval && version == HeaderVersion(chainType, height)
return height < 4*HardForkInterval && version == HeaderVersion(chainType, height)
}

// DifficultyAdjustWindow is the number of blocks used to calculate difficulty adjustments
Expand All @@ -169,8 +196,7 @@ const DifficultyDampFactor uint64 = 3
const ARScaleDampFactor uint64 = 13

// GraphWeight compute weight of a graph as number of siphash bits defining the graph
// Must be made dependent on height to phase out C31 in early 2020
// Later phase outs are on hold for now
// The height dependence allows a 30-week linear transition from C31+ to C32+ starting after 1 year
func GraphWeight(chainType ChainType, height uint64, edgeBits uint8) uint64 {
xprEdgeBits := uint64(edgeBits)
expiryHeight := YearHeight
Expand Down
79 changes: 45 additions & 34 deletions core/consensus/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,28 +98,34 @@ func TestSecondaryPoWRatio(t *testing.T) {
assert.Equal(t, SecondaryPoWRatio(twoYears+1), uint64(0))
}

func TestValidHeaderVersion(t *testing.T) {
func TestHardForks(t *testing.T) {
// Tests for Mainnet
{
assert.True(t, ValidHeaderVersion(Mainnet, YearHeight/2, 2))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight/2, 1))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight, 1))
assert.True(t, ValidHeaderVersion(Mainnet, YearHeight/2+1, 2))
assert.True(t, ValidHeaderVersion(Mainnet, YearHeight/2-1, 1))

assert.True(t, ValidHeaderVersion(Mainnet, YearHeight-1, 2))
assert.True(t, ValidHeaderVersion(Mainnet, YearHeight, 3))
assert.True(t, ValidHeaderVersion(Mainnet, YearHeight+1, 3))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight, 2))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight*3/2, 2))

// v4 not active yet
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight*3/2, 4))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight*3/2, 3))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight*3/2, 2))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight*3/2, 1))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight*2, 3))
assert.False(t, ValidHeaderVersion(Mainnet, YearHeight*3/2+1, 3))
assert.True(t, ValidHeaderVersion(Mainnet, 0, 1))
assert.True(t, ValidHeaderVersion(Mainnet, 10, 1))
assert.False(t, ValidHeaderVersion(Mainnet, 10, 2))

assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval-1, 1))
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval, 1))
assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval, 2))
assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval+1, 2))

assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval*2-1, 2))
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval*2, 2))
assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval*2, 3))
assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval*2+1, 3))

assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval*3-1, 3))
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval*3, 3))
assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval*3, 4))
assert.True(t, ValidHeaderVersion(Mainnet, HardForkInterval*3+1, 4))

// v5 not active yet
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval*4, 5))
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval*4, 4))
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval*4, 3))
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval*4, 2))
assert.False(t, ValidHeaderVersion(Mainnet, HardForkInterval*4, 1))
}
// Tests for Floonet
{
Expand All @@ -132,25 +138,30 @@ func TestValidHeaderVersion(t *testing.T) {
assert.True(t, ValidHeaderVersion(Floonet, FloonetFirstHardFork+1, 2))
assert.False(t, ValidHeaderVersion(Floonet, FloonetFirstHardFork, 1))

assert.False(t, ValidHeaderVersion(Floonet, YearHeight, 1))

assert.True(t, ValidHeaderVersion(Floonet, FloonetSecondHardFork-1, 2))
assert.True(t, ValidHeaderVersion(Floonet, FloonetSecondHardFork, 3))
assert.True(t, ValidHeaderVersion(Floonet, FloonetSecondHardFork+1, 3))
assert.False(t, ValidHeaderVersion(Floonet, FloonetSecondHardFork, 2))
assert.False(t, ValidHeaderVersion(Floonet, FloonetSecondHardFork, 1))

assert.False(t, ValidHeaderVersion(Floonet, YearHeight-1, 2))
assert.True(t, ValidHeaderVersion(Floonet, YearHeight-1, 3))
assert.True(t, ValidHeaderVersion(Floonet, YearHeight, 3))
assert.True(t, ValidHeaderVersion(Floonet, YearHeight+1, 3))

// v4 not active yet
assert.False(t, ValidHeaderVersion(Floonet, YearHeight*3/2, 4))
assert.False(t, ValidHeaderVersion(Floonet, YearHeight*3/2, 3))
assert.False(t, ValidHeaderVersion(Floonet, YearHeight*3/2, 2))
assert.False(t, ValidHeaderVersion(Floonet, YearHeight*3/2, 1))
assert.False(t, ValidHeaderVersion(Floonet, YearHeight*2, 3))
assert.False(t, ValidHeaderVersion(Floonet, YearHeight*3/2+1, 3))
assert.True(t, ValidHeaderVersion(Floonet, FloonetThirdHardFork-1, 3))
assert.True(t, ValidHeaderVersion(Floonet, FloonetThirdHardFork, 4))
assert.True(t, ValidHeaderVersion(Floonet, FloonetThirdHardFork+1, 4))
assert.False(t, ValidHeaderVersion(Floonet, FloonetThirdHardFork, 3))
assert.False(t, ValidHeaderVersion(Floonet, FloonetThirdHardFork, 2))
assert.False(t, ValidHeaderVersion(Floonet, FloonetThirdHardFork, 1))

assert.False(t, ValidHeaderVersion(Floonet, HardForkInterval*2-1, 1))
assert.False(t, ValidHeaderVersion(Floonet, HardForkInterval*2-1, 2))
assert.True(t, ValidHeaderVersion(Floonet, HardForkInterval*2-1, 3))
assert.True(t, ValidHeaderVersion(Floonet, HardForkInterval*2, 3))
assert.True(t, ValidHeaderVersion(Floonet, HardForkInterval*2+1, 3))

// v5 not active yet
assert.False(t, ValidHeaderVersion(Floonet, HardForkInterval*4, 5))
assert.False(t, ValidHeaderVersion(Floonet, HardForkInterval*4, 4))
assert.False(t, ValidHeaderVersion(Floonet, HardForkInterval*4, 3))
assert.False(t, ValidHeaderVersion(Floonet, HardForkInterval*4, 2))
assert.False(t, ValidHeaderVersion(Floonet, HardForkInterval*4, 1))
}
}
4 changes: 4 additions & 0 deletions core/pow.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ func createPoWContext(chainType consensus.ChainType, height uint64, edgeBits uin
// Mainnet has Cuckaroo29 for AR and Cuckatoo30+ for AF
case consensus.Mainnet <= chainType && edgeBits > 29:
return pow.NewCuckatooCtx(chainType, edgeBits, proofSize, maxSols)
case consensus.Mainnet <= chainType && consensus.ValidHeaderVersion(chainType, height, 4):
return pow.NewCuckaroozCtx(chainType, edgeBits, proofSize)
case consensus.Mainnet <= chainType && consensus.ValidHeaderVersion(chainType, height, 3):
return pow.NewCuckaroomCtx(chainType, edgeBits, proofSize)
case consensus.Mainnet <= chainType && consensus.ValidHeaderVersion(chainType, height, 2):
Expand All @@ -34,6 +36,8 @@ func createPoWContext(chainType consensus.ChainType, height uint64, edgeBits uin
return pow.NewCuckarooCtx(chainType, edgeBits, proofSize)
case consensus.Floonet <= chainType && edgeBits > 29:
return pow.NewCuckatooCtx(chainType, edgeBits, proofSize, maxSols)
case consensus.Floonet <= chainType && consensus.ValidHeaderVersion(chainType, height, 4):
return pow.NewCuckaroozCtx(chainType, edgeBits, proofSize)
case consensus.Floonet <= chainType && consensus.ValidHeaderVersion(chainType, height, 3):
return pow.NewCuckaroomCtx(chainType, edgeBits, proofSize)
case consensus.Floonet <= chainType && consensus.ValidHeaderVersion(chainType, height, 2):
Expand Down
5 changes: 3 additions & 2 deletions core/pow/cuckaroo.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func (c *CuckarooContext) Verify(proof Proof) error {
nonces := proof.Nonces
uvs := make([]uint64, 2*proof.proofSize())
var xor0, xor1 uint64
nodeMask := c.params.edgeMask

for n := 0; n < proof.proofSize(); n++ {
if nonces[n] > c.params.edgeMask {
Expand All @@ -58,9 +59,9 @@ func (c *CuckarooContext) Verify(proof Proof) error {
}
// 21 is standard siphash rotation constant
edge := SipHashBlock(c.params.siphashKeys, nonces[n], 21, false)
uvs[2*n] = edge & c.params.edgeMask
uvs[2*n+1] = (edge >> 32) & c.params.edgeMask
uvs[2*n] = edge & nodeMask
xor0 ^= uvs[2*n]
uvs[2*n+1] = (edge >> 32) & nodeMask
xor1 ^= uvs[2*n+1]
}

Expand Down
7 changes: 4 additions & 3 deletions core/pow/cuckarood.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (c *CuckaroodContext) Verify(proof Proof) error {
uvs := make([]uint64, 2*proof.proofSize())
ndir := make([]uint64, 2)
var xor0, xor1 uint64
nodemask := c.params.edgeMask >> 1
nodeMask := c.params.edgeMask >> 1

for n := 0; n < proof.proofSize(); n++ {
dir := uint(nonces[n] & 1)
Expand All @@ -62,11 +62,12 @@ func (c *CuckaroodContext) Verify(proof Proof) error {
if n > 0 && nonces[n] <= nonces[n-1] {
return errors.New("edges not ascending")
}
// cuckarood uses a non-standard siphash rotation constant 25 as anti-ASIC tweak
edge := SipHashBlock(c.params.siphashKeys, nonces[n], 25, false)
idx := 4*ndir[dir] + 2*uint64(dir)
uvs[idx] = edge & nodemask
uvs[idx+1] = (edge >> 32) & nodemask
uvs[idx] = edge & nodeMask
xor0 ^= uvs[idx]
uvs[idx+1] = (edge >> 32) & nodeMask
xor1 ^= uvs[idx+1]
ndir[dir]++
}
Expand Down
15 changes: 8 additions & 7 deletions core/pow/cuckaroom.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ func (c *CuckaroomContext) Verify(proof Proof) error {
return errors.New("wrong cycle length")
}
nonces := proof.Nonces
from := make([]uint32, proof.proofSize())
to := make([]uint32, proof.proofSize())
var xorFrom uint32 = 0
var xorTo uint32 = 0
nodemask := c.params.edgeMask >> 1
from := make([]uint64, proof.proofSize())
to := make([]uint64, proof.proofSize())
var xorFrom uint64 = 0
var xorTo uint64 = 0
nodeMask := c.params.edgeMask >> 1

for n := 0; n < proof.proofSize(); n++ {
if nonces[n] > c.params.edgeMask {
Expand All @@ -59,10 +59,11 @@ func (c *CuckaroomContext) Verify(proof Proof) error {
if n > 0 && nonces[n] <= nonces[n-1] {
return errors.New("edges not ascending")
}
// 21 is standard siphash rotation constant
edge := SipHashBlock(c.params.siphashKeys, nonces[n], 21, true)
from[n] = uint32(edge & nodemask)
from[n] = edge & nodeMask
xorFrom ^= from[n]
to[n] = uint32((edge >> 32) & nodemask)
to[n] = (edge >> 32) & nodeMask
xorTo ^= to[n]
}
if xorFrom != xorTo {
Expand Down
104 changes: 104 additions & 0 deletions core/pow/cuckarooz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2020 BlockCypher
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pow

import (
"errors"

"github.com/blockcypher/libgrin/core/consensus"
)

// NewCuckaroozCtx instantiates a new CuckaroozContext as a PowContext. Note that this can't
/// be moved in the PoWContext trait as this particular trait needs to be
/// convertible to an object trait.
func NewCuckaroozCtx(chainType consensus.ChainType, edgeBits uint8, proofSize int) *CuckaroomContext {
cp := new(CuckooParams)
params := cp.new(edgeBits, proofSize)
return &CuckaroomContext{chainType, params}
}

// CuckaroozContext is a Cuckarooz cycle context. Only includes the verifier for now.
type CuckaroozContext struct {
chainType consensus.ChainType
params CuckooParams
}

// SetHeaderNonce sets the header nonce.
func (c *CuckaroozContext) SetHeaderNonce(header []uint8, nonce *uint32) {
c.params.resetHeaderNonce(header, nonce)
}

// Verify verifies the Cuckaroom context.
func (c *CuckaroozContext) Verify(proof Proof) error {
if proof.proofSize() != consensus.ChainTypeProofSize(c.chainType) {
return errors.New("wrong cycle length")
}
nonces := proof.Nonces
uvs := make([]uint64, 2*proof.proofSize())
var xoruv uint64 = 0
nodeMask := c.params.edgeMask<<1 | 1

for n := 0; n < proof.proofSize(); n++ {
if nonces[n] > c.params.edgeMask {
return errors.New("edge too big")
}
if n > 0 && nonces[n] <= nonces[n-1] {
return errors.New("edges not ascending")
}
// 21 is standard siphash rotation constant
edge := SipHashBlock(c.params.siphashKeys, nonces[n], 21, true)
uvs[2*n] = edge & nodeMask
uvs[2*n+1] = edge >> 32 & nodeMask
xoruv ^= uvs[2*n] ^ uvs[2*n+1]
}
if xoruv != 0 {
return errors.New("endpoints don't match up")
}

n := 0
i := 0
j := 0
for {
// follow cycle
j = i
k := j
for {
k = (j + 1) % (2 * c.params.proofSize)
if k == i {
break
}
}
if uvs[k] == uvs[i] {
// find other edge endpoint matching one at i
if j != i {
return errors.New("branch in cycle")
}
j = k
}
if j == i {
return errors.New("cycle dead ends")
}
i = j ^ 1
n++
if i == 0 {
break
}
}
if n == c.params.proofSize {
return nil
}
return errors.New("cycle too short")

}
Loading