Skip to content
This repository was archived by the owner on Feb 16, 2024. It is now read-only.

Commit 978a27f

Browse files
committed
add cuckoo implementation for aeternity
1 parent 95e83cf commit 978a27f

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

cuckoo/client.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package cuckoo
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/binary"
6+
7+
"github.com/sencha-dev/powkit/internal/crypto"
8+
)
9+
10+
type Config struct {
11+
proofSize int
12+
edgeBits int
13+
edgeMask uint64
14+
nodeBits int
15+
nodeMask uint64
16+
}
17+
18+
func New(edgeBits, nodeBits, proofSize int) *Config {
19+
edgeNum := uint64(1) << edgeBits
20+
nodeNum := uint64(1) << nodeBits
21+
22+
cfg := &Config{
23+
proofSize: proofSize,
24+
edgeBits: edgeBits,
25+
edgeMask: edgeNum - 1,
26+
nodeBits: nodeBits,
27+
nodeMask: nodeNum - 1,
28+
}
29+
30+
return cfg
31+
}
32+
33+
func NewAeternity() *Config {
34+
return New(29, 29, 42)
35+
}
36+
37+
func (cfg *Config) Verify(hash []byte, nonce uint64, sols []uint64) bool {
38+
// encode header
39+
nonceBytes := make([]uint8, 8)
40+
binary.LittleEndian.PutUint64(nonceBytes, nonce)
41+
hashEncoded := []byte(base64.StdEncoding.EncodeToString(hash))
42+
nonceEncoded := []byte(base64.StdEncoding.EncodeToString(nonceBytes))
43+
header := append(hashEncoded, append(nonceEncoded, make([]byte, 24)...)...)
44+
45+
// create siphash keys
46+
h := crypto.Blake2b256(header)
47+
keys := [4]uint64{
48+
binary.LittleEndian.Uint64(h[0:8]),
49+
binary.LittleEndian.Uint64(h[8:16]),
50+
binary.LittleEndian.Uint64(h[16:24]),
51+
binary.LittleEndian.Uint64(h[24:32]),
52+
}
53+
54+
return cfg.verify(keys, sols)
55+
}

cuckoo/cuckoo.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) 2013-2020 John Tromp
2+
3+
package cuckoo
4+
5+
import (
6+
"github.com/sencha-dev/powkit/internal/crypto"
7+
)
8+
9+
func (cfg *Config) sipnode(siphashKeys [4]uint64, edge, uorv uint64) uint64 {
10+
hasher := crypto.NewSipHasher(siphashKeys[0], siphashKeys[1], siphashKeys[2], siphashKeys[3])
11+
hasher.Hash24(2*edge + uorv)
12+
13+
value := hasher.XorLanes()
14+
value = value<<17 | value>>47
15+
16+
return value & cfg.edgeMask
17+
}
18+
19+
func (cfg *Config) verify(siphashKeys [4]uint64, edges []uint64) bool {
20+
uvs := make([]uint64, 2*cfg.proofSize)
21+
var xor0, xor1 uint64
22+
23+
for n := 0; n < cfg.proofSize; n++ {
24+
if edges[n] > cfg.edgeMask {
25+
return false // POW_TOO_BIG
26+
} else if n < 0 && edges[n] <= edges[n-1] {
27+
return false // POW_TOO_SMALL
28+
}
29+
30+
uvs[2*n] = cfg.sipnode(siphashKeys, edges[n], 0)
31+
xor0 ^= uvs[2*n]
32+
33+
uvs[2*n+1] = cfg.sipnode(siphashKeys, edges[n], 1)
34+
xor1 ^= uvs[2*n+1]
35+
}
36+
37+
if xor0|xor1 != 0 {
38+
return false // POW_NON_MATCHING
39+
}
40+
41+
var i, j, n int
42+
for {
43+
j = i
44+
k := j
45+
46+
for {
47+
k = (k + 2) % (2 * cfg.proofSize)
48+
if k == i {
49+
break
50+
}
51+
52+
if uvs[k] == uvs[i] {
53+
if j != i {
54+
return false // POW_BRANCH
55+
}
56+
57+
j = k
58+
}
59+
}
60+
61+
if j == i {
62+
return false // POW_DEAD_END
63+
}
64+
65+
i = j ^ 1
66+
n++
67+
68+
if i == 0 {
69+
break
70+
}
71+
}
72+
73+
return n == cfg.proofSize // POW_SHORT_CYCLE
74+
}

cuckoo/cuckoo_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package cuckoo
2+
3+
import (
4+
"testing"
5+
6+
"github.com/sencha-dev/powkit/internal/common/testutil"
7+
)
8+
9+
func TestAeternity(t *testing.T) {
10+
tests := []struct {
11+
hash []byte
12+
nonce uint64
13+
sols []uint64
14+
}{
15+
{
16+
hash: testutil.MustDecodeHex("0x8825085f881ee78f0c9cd94ca8a72ae930e025fd43931e4760e320a4f031d88e"),
17+
nonce: 0x0000fc7e000001a8,
18+
sols: []uint64{
19+
0x003b5d47, 0x00a70508, 0x00d0aa4a, 0x0238a16a, 0x038653bf, 0x03e91d96, 0x03f4baa8, 0x062ef17e,
20+
0x065d7b41, 0x066fbb1e, 0x079af861, 0x08bd2cf2, 0x0956b89d, 0x0b56fb7f, 0x0c098553, 0x0c6d2c27,
21+
0x0d8c0fd9, 0x0ddcbb1d, 0x0e3eccde, 0x0e464bef, 0x0fb09bef, 0x1267ebb1, 0x129ef8e6, 0x138432b5,
22+
0x144d428b, 0x1484e6b6, 0x14efcfba, 0x158d5352, 0x159f3551, 0x15a07563, 0x160a3efd, 0x17c9b61e,
23+
0x184499bc, 0x1844f434, 0x1919053a, 0x197a9095, 0x1aa04947, 0x1bc3f6e5, 0x1d8b4029, 0x1e6a1fe0,
24+
0x1e7e4380, 0x1f5a2a50,
25+
},
26+
},
27+
{
28+
hash: testutil.MustDecodeHex("0x6166a5eae59cd13e37e6682e1bcc07fcc0b1e4d11ba231fb5e6f13aaf68df1b5"),
29+
nonce: 0x000028721b7b4e56,
30+
sols: []uint64{
31+
0x007791c9, 0x0197cfec, 0x025166a5, 0x025e30b8, 0x03d6aedc, 0x04311736, 0x0516b291, 0x05179816,
32+
0x056ac771, 0x068b92d8, 0x06aee4f8, 0x0817c500, 0x08c1a751, 0x08f1999c, 0x08f46654, 0x092b8770,
33+
0x096034f8, 0x09cfcb6f, 0x0d4f1cdb, 0x0d5b8793, 0x0d5c09a8, 0x0da0e97b, 0x0db38171, 0x0dc09c91,
34+
0x0e34aeaf, 0x0f0251df, 0x10486aa7, 0x14477924, 0x1544daf9, 0x1623e36e, 0x16b55699, 0x16fa6905,
35+
0x177125c6, 0x17f88a4d, 0x19243b3c, 0x19ebdaa7, 0x1b5383f1, 0x1c614cbf, 0x1de144f9, 0x1df596a7,
36+
0x1f629453, 0x1f6b9406,
37+
},
38+
},
39+
{
40+
hash: testutil.MustDecodeHex("0x8ced411cbb09c197aaeadd04bcfc8cbfd2435f6b7ca9b5876179c86aa20cd84c"),
41+
nonce: 0x0000287296efe45f,
42+
sols: []uint64{
43+
0x008a9cad, 0x008ec69d, 0x00dbf03e, 0x020337ed, 0x0211161e, 0x02ee6658, 0x045d4488, 0x0534f601,
44+
0x0542e80c, 0x062d2002, 0x078ac9b5, 0x07b7112a, 0x07cb2202, 0x07fbc7e7, 0x088827a7, 0x093e3139,
45+
0x0a7af764, 0x0a92c274, 0x0ad8dcb9, 0x0bf8f10f, 0x0c0f0de0, 0x0cc6670d, 0x0dad83b3, 0x104f3b5e,
46+
0x11817d4c, 0x123a7b96, 0x12b9fbdd, 0x1337ce2b, 0x13432b38, 0x15b1b455, 0x16784bf4, 0x1693250e,
47+
0x169d804f, 0x16e811de, 0x195d114e, 0x1b77f0cb, 0x1c55fca6, 0x1cab0177, 0x1cb0a8c3, 0x1d38ea27,
48+
0x1d9aa6dc, 0x1f713d1f,
49+
},
50+
},
51+
}
52+
53+
for i, tt := range tests {
54+
valid := NewAeternity().Verify(tt.hash, tt.nonce, tt.sols)
55+
if !valid {
56+
t.Errorf("failed on %d: invalid solution", i)
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)