Skip to content
This repository has been archived by the owner on Sep 3, 2019. It is now read-only.

Floonet (grin live testnet) #7

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
39 changes: 39 additions & 0 deletions chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@ package chain

import (
"bytes"
"encoding/hex"
"errors"
"github.com/dblokhin/gringo/consensus"
"github.com/sirupsen/logrus"
"sync"
"time"
)

// mustDecodeHex decodes the hex-encoded string s and panics on failure.
func mustDecodeHex(s string) []byte {
decoded, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return decoded
}

// Testnet1 genesis block
var Testnet1 = consensus.Block{
Header: consensus.BlockHeader{
Expand Down Expand Up @@ -168,6 +178,35 @@ var Mainnet = consensus.Block{
},
}

// Floonet genesis block
var Floonet = consensus.Block{
Header: consensus.BlockHeader{
Version: 1,
Height: 0,
Previous: mustDecodeHex("00000000000000000017ff4903ef366c8f62e3151ba74e41b8332a126542f538"),
Timestamp: time.Date(2018, 12, 28, 20, 48, 4, 0, time.UTC),
TotalDifficulty: 100000,
ScalingDifficulty: 1856,

UTXORoot: mustDecodeHex("73b5e0a05ea9e1e4e33b8f1c723bc5c10d17f07042c2af7644f4dbb61f4bc556"),
RangeProofRoot: mustDecodeHex("667a3ba22f237a875f67c9933037c8564097fa57a3e75be507916de28fc0da26"),
KernelRoot: mustDecodeHex("cfdddfe2d938d0026f8b1304442655bbdddde175ff45ddf44cb03bcb0071a72d"),

Nonce: 23,
POW: consensus.Proof{
EdgeBits: 29,
Nonces: []uint32{
16994232, 22975978, 32664019, 44016212, 50238216, 57272481, 85779161,
124272202, 125203242, 133907662, 140522149, 145870823, 147481297, 164952795,
177186722, 183382201, 197418356, 211393794, 239282197, 239323031, 250757611,
281414565, 305112109, 308151499, 357235186, 374041407, 389924708, 390768911,
401322239, 401886855, 406986280, 416797005, 418935317, 429007407, 439527429,
484809502, 486257104, 495589543, 495892390, 525019296, 529899691, 531685572,
},
},
},
}

type Chain struct {
sync.RWMutex

Expand Down
10 changes: 10 additions & 0 deletions chain/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ func TestGenesisHash(t *testing.T) {
hash, expected, Testnet4.Bytes())
}
}

func TestFloonetGenesisHash(t *testing.T) {
hash := Floonet.Hash()
expected, _ := hex.DecodeString("edc758c1370d43e1d733f70f58cf187c3be8242830429b1676b89fd91ccf2dab")

if bytes.Compare(hash, expected) != 0 {
t.Errorf("Genesis hash was %v wanted %x. Content:\n%x\n",
hash, expected, Floonet.Bytes())
}
}
7 changes: 1 addition & 6 deletions consensus/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -1017,12 +1017,7 @@ func (b *BlockHeader) Validate() error {
return fmt.Errorf("cuckoo size too small: %d", b.POW.EdgeBits)
}

// The primary POW must have a scaling factor of 1.
if isPrimaryPow && b.ScalingDifficulty != 1 {
return fmt.Errorf("invalid scaling difficulty: %d", b.ScalingDifficulty)
}

if err := b.POW.Validate(b, b.POW.EdgeBits); err != nil {
if err := b.POW.Validate(b, b.POW.EdgeBits, isPrimaryPow); err != nil {
return err
}

Expand Down
3 changes: 2 additions & 1 deletion consensus/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
package consensus

// MagicCode is expected in the header of every message
var MagicCode = [2]byte{0x54, 0x34}
var MagicCodeFloonet = [2]byte{83, 59}
var MagicCodeMainnet = [2]byte{97, 61}

const (
// protocolVersion version of grin p2p protocol
Expand Down
17 changes: 12 additions & 5 deletions consensus/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,19 @@ var (
)

// Validate validates the pow
func (p *Proof) Validate(header *BlockHeader, cuckooSize uint8) error {
logrus.Infof("block POW validate for size %d", cuckooSize)
func (p *Proof) Validate(header *BlockHeader, cuckooSize uint8,
isPrimaryPow bool) error {

cuckoo := cuckoo.NewCuckaroo(header.bytesWithoutPOW())
if cuckoo.Verify(header.POW.Nonces, header.POW.EdgeBits) {
return nil
if isPrimaryPow {
verifier := cuckoo.NewCuckatoo(header.bytesWithoutPOW(), header.POW.EdgeBits)
if verifier.Verify(header.POW.Nonces) {
return nil
}
} else {
verifier := cuckoo.NewCuckaroo(header.bytesWithoutPOW())
if verifier.Verify(header.POW.Nonces, header.POW.EdgeBits) {
return nil
}
}

return errInvalidPow
Expand Down
73 changes: 61 additions & 12 deletions cuckoo/cuckoo.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,63 @@ loop:
return cycle
}

// New returns a new instance of a Cuckoo cycle verifier. key is the data used
// findCycleLengthCuckatoo attempts to walk the given edges and returns the
// length of the cycle. This version differs in that it doesn't compare the last
// bit of each vertex.
func findCycleLengthCuckatoo(edges []*Edge) int {
i := 0 // first node
cycle := 0 // the current length of the cycle

loop:
for {
// If the current cycle length is even we need to look for an edge from
// U -> V.
vertexInUPartition := cycle&1 == 0

if vertexInUPartition {
// Find an unused edge that connects to the current vertex.
for j := range edges {
// We ignore the last bit during this comparison.
if j != i && !edges[j].usedU && (edges[i].U>>1) == (edges[j].U>>1) {
edges[i].usedU = true
edges[j].usedU = true

i = j
cycle++

continue loop
}
}
} else {
// Find an unused edge that connects to the current vertex.
for j := range edges {
// We ignore the last bit during this comparison.
if j != i && !edges[j].usedV && (edges[i].V>>1) == (edges[j].V>>1) {
edges[i].usedV = true
edges[j].usedV = true

i = j
cycle++

continue loop
}
}
}

// If we didn't find a match for this vertex then we're done.
break
}

return cycle
}

// NewCuckatoo returns a new instance of a Cuckoo cycle verifier. key is the data used
// to derived the siphash keys from and sizeShift is the number of vertices in
// the cuckoo graph as an exponent of 2.
func New(key []byte, sizeShift uint8) *Cuckoo {
func NewCuckatoo(key []byte, edgeBits uint8) *Cuckoo {
size := uint64(1) << edgeBits
mask := size - 1

bsum := blake2b.Sum256(key)
key = bsum[:]

Expand All @@ -79,11 +132,9 @@ func New(key []byte, sizeShift uint8) *Cuckoo {
v[2] = binary.LittleEndian.Uint64(key[16:24])
v[3] = binary.LittleEndian.Uint64(key[24:32])

numVertices := uint64(1) << sizeShift

return &Cuckoo{
mask: numVertices/2 - 1,
size: numVertices,
mask: mask,
size: size,
v: v,
}
}
Expand All @@ -100,7 +151,7 @@ type Cuckoo struct {
}

func (c *Cuckoo) newNode(nonce uint64, i uint64) uint64 {
return ((siphash24(c.v, 2*nonce+i) & c.mask) << 1) | i
return siphash24(c.v, 2*nonce+i) & c.mask
}

func (c *Cuckoo) NewEdge(nonce uint32) *Edge {
Expand All @@ -112,30 +163,28 @@ func (c *Cuckoo) NewEdge(nonce uint32) *Edge {

// Verify constructs a cuckoo subgraph from the edges generated by nonces and
// checks whether they form a cycle of the correct length.
func (c *Cuckoo) Verify(nonces []uint32, ease uint64) bool {
func (c *Cuckoo) Verify(nonces []uint32) bool {
proofSize := len(nonces)

// zero proof is always invalid
if proofSize == 0 {
return false
}

easiness := ease * c.size / 100

// Create graph edges from the nonces.
edges := make([]*Edge, proofSize)
for i := 0; i < proofSize; i++ {
// Ensure nonces are in ascending order and that they are all of the
// correct easiness.
if uint64(nonces[i]) >= easiness || (i != 0 && nonces[i] <= nonces[i-1]) {
if uint64(nonces[i]) > c.mask || (i != 0 && nonces[i] <= nonces[i-1]) {
logrus.Infof("Nonce %d is invalid", i)
return false
}

edges[i] = c.NewEdge(nonces[i])
}

return findCycleLength(edges) == proofSize
return findCycleLengthCuckatoo(edges) == proofSize
}

// Cuckaroo is the asic-resistant version of the cuckoo-cycle mining algorithm.
Expand Down
57 changes: 45 additions & 12 deletions cuckoo/cuckoo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
package cuckoo

import (
"encoding/binary"
"encoding/hex"
"testing"
)

Expand Down Expand Up @@ -35,19 +37,50 @@ func TestBlock(t *testing.T) {
}
}

var V1 = []uint32{
0x3bbd, 0x4e96, 0x1013b, 0x1172b, 0x1371b, 0x13e6a, 0x1aaa6, 0x1b575,
0x1e237, 0x1ee88, 0x22f94, 0x24223, 0x25b4f, 0x2e9f3, 0x33b49, 0x34063,
0x3454a, 0x3c081, 0x3d08e, 0x3d863, 0x4285a, 0x42f22, 0x43122, 0x4b853,
0x4cd0c, 0x4f280, 0x557d5, 0x562cf, 0x58e59, 0x59a62, 0x5b568, 0x644b9,
0x657e9, 0x66337, 0x6821c, 0x7866f, 0x7e14b, 0x7ec7c, 0x7eed7, 0x80643,
0x8628c, 0x8949e,
}

func TestValidSolution(t *testing.T) {
header := []byte{49}
cuckoo := New(header, 20)
if !cuckoo.Verify(V1, 75) {
header := [80]byte{}

// Replace the last four bytes of the key with the nonce.
nonce := 20
header[len(header)-4] = byte(nonce)
header[len(header)-3] = byte(nonce << 8)
header[len(header)-2] = byte(nonce << 16)
header[len(header)-1] = byte(nonce << 24)

cuckoo := NewCuckatoo(header[:], 29)

k0, _ := hex.DecodeString("27580576fe290177")
k1, _ := hex.DecodeString("f9ea9b2031f4e76e")
k2, _ := hex.DecodeString("1663308c8607868f")
k3, _ := hex.DecodeString("b88839b0fa180d0e")

if binary.BigEndian.Uint64(k0) != cuckoo.v[0] {
t.Errorf("Key derivation failed, got %x expected %x", cuckoo.v[0],
binary.BigEndian.Uint64(k0))
}
if binary.BigEndian.Uint64(k1) != cuckoo.v[1] {
t.Errorf("Key derivation failed, got %x expected %x", cuckoo.v[1],
binary.BigEndian.Uint64(k1))
}
if binary.BigEndian.Uint64(k2) != cuckoo.v[2] {
t.Errorf("Key derivation failed, got %x expected %x", cuckoo.v[2],
binary.BigEndian.Uint64(k2))
}
if binary.BigEndian.Uint64(k3) != cuckoo.v[3] {
t.Errorf("Key derivation failed, got %x expected %x", cuckoo.v[3],
binary.BigEndian.Uint64(k3))
}

var V1 = []uint32{
0x48a9e2, 0x9cf043, 0x155ca30, 0x18f4783, 0x248f86c, 0x2629a64, 0x5bad752, 0x72e3569,
0x93db760, 0x97d3b37, 0x9e05670, 0xa315d5a, 0xa3571a1, 0xa48db46, 0xa7796b6, 0xac43611,
0xb64912f, 0xbb6c71e, 0xbcc8be1, 0xc38a43a, 0xd4faa99, 0xe018a66, 0xe37e49c, 0xfa975fa,
0x11786035, 0x1243b60a, 0x12892da0, 0x141b5453, 0x1483c3a0, 0x1505525e, 0x1607352c,
0x16181fe3, 0x17e3a1da, 0x180b651e, 0x1899d678, 0x1931b0bb, 0x19606448, 0x1b041655,
0x1b2c20ad, 0x1bd7a83c, 0x1c05d5b0, 0x1c0b9caa,
}

if !cuckoo.Verify(V1) {
t.Error("Verify failed")
}
}
Expand Down
3 changes: 2 additions & 1 deletion p2p/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ func shakeByHand(conn net.Conn) (*shake, error) {
SenderAddr: sender,
ReceiverAddr: receiver,
UserAgent: userAgent,
Genesis: chain.Testnet4.Hash(),
// FIXME: This needs to be configurable.
Genesis: chain.Floonet.Hash(),
}

// Send own hand
Expand Down
2 changes: 1 addition & 1 deletion p2p/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (h *Header) Read(r io.Reader) error {

// validateMagic verifies magic code
func (h Header) validateMagic() bool {
return bytes.Equal(h.magic[:], consensus.MagicCode[:])
return bytes.Equal(h.magic[:], consensus.MagicCodeFloonet[:])
}

// Ping request
Expand Down
2 changes: 1 addition & 1 deletion p2p/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func WriteMessage(w io.Writer, msg Message) (uint64, error) {
data := msg.Bytes()

header := Header{
magic: consensus.MagicCode,
magic: consensus.MagicCodeFloonet,
Type: msg.Type(),
Len: uint64(len(data)),
}
Expand Down