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

Commit a0d2b79

Browse files
committed
standardization, error handling, docs updates
1 parent feda42e commit a0d2b79

25 files changed

+295
-319
lines changed

README.md

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Proof of Work Algorithms
22

3-
[![Go Test](https://github.com/sencha-dev/go-pow/actions/workflows/go.yml/badge.svg)](https://github.com/sencha-dev/go-pow/actions/workflows/go.yml)
4-
[![PkgGoDev](https://pkg.go.dev/badge/github.com/sencha-dev/go-pow)](https://pkg.go.dev/github.com/sencha-dev/go-pow?tab=doc)
3+
[![Go Test](https://github.com/sencha-dev/powkit/actions/workflows/go.yml/badge.svg)](https://github.com/sencha-dev/powkit/actions/workflows/go.yml)
4+
[![PkgGoDev](https://pkg.go.dev/badge/github.com/sencha-dev/powkit)](https://pkg.go.dev/github.com/sencha-dev/powkit?tab=doc)
55

66
# Overview
77

88
*Note: This library is still in active development and is
99
subject to breaking changes*
1010

11-
Though there is a wide variety of Proof of Work algorithms, finding the technical details
11+
Though there are a wide variety of Proof of Work algorithms, finding the technical details
1212
for the implementations is quite a task. Both Kawpow and Firopow are variations off of ProgPow,
13-
though finding the exact differences is no easy task. This is meant to be a unified library to
13+
though finding the exact differences is painful. This is meant to be a unified library to
1414
make the implementation of existing Proof of Work algorithms easier.
1515

16-
All DAG-based algorithms only implement a light DAG, which is sufficient for verification
16+
All DAG-based algorithms only implement a light DAG, which is sufficient for validation
1717
but not full nodes or miners. For the DAG-based algorithms and verthash, data is cached in `~/.powcache`.
1818
Ethash will generally be between 40-80Mb per epoch (and generally 3 caches are stored), but verthash
1919
requires a strict 1.2Gb, so be careful if you're using verthash in memory. At the time of writing, running
@@ -38,31 +38,21 @@ requires a strict 1.2Gb, so be careful if you're using verthash in memory. At th
3838

3939
# Things to Note
4040

41-
- There is no validation on hash input sizes - generally it is 32 bytes but it can vary by algorithm. It absolutely *can* panic
42-
if the wrong input size is used. I probably should be stricter about this, or at least define what the expectations are, but for
43-
now the best way to check is in the tests.
44-
- Most of these algorithms are partially optimized, though there surely could be some improvements. However, that is not the goal since
45-
these have never been intended to be used for miners. All of these algorithms far surpass a reasonable threshold for performance and I
41+
- Most of these algorithms are partially optimized but I'm sure they could be improvemed. That being said, that will probably never happen
42+
since these have never been intended to be used for miner clients. All of these algorithms far surpass a reasonable threshold for performance and I
4643
have no intention of hypertuning them.
4744
- Cuckoo Cycle is built specifically for Aeternity. There is a modification of the `sipnode` function in the current version
48-
of tromp's cuckoo algorithms that Aeternity does not use (a Nicehash dev gives more details [here](https://forum.aeternity.com/t/support-aeternity-stratum-implementation/3140/6)). It wouldn't be hard to implement other Cuckoo Cycle algorithms (cuckatoo, cuckaroo),
49-
there just isn't really a need at this point since Grin is fairly annoying. BlockCypher implements the other algorithms [here](https://github.com/blockcypher/libgrin/tree/master/core/pow).
45+
of tromp's cuckoo algorithms that Aeternity does not use (a Nicehash dev gives more details [here](https://forum.aeternity.com/t/support-aeternity-stratum-implementation/3140/6)).
46+
- The base ProgPow implementation ("ProgPow094") exists in the `internal/progpow` package.
5047
- Since ZelHash is such a minor Equihash variant, it is treated as just "twisted Equihash" (in `equihash/`).
5148
- All testing is done on linux, windows support is hazy at best.
5249
- The library assumes the host architecture is little-endian, I'm fairly confident big-endian architectures will not function properly.
53-
- The base ProgPow implementation ("ProgPow094") exists in the `internal/progpow` package.
5450
- As of now, the only other algorithms that are on the list of "maybes" are: [cryptonight](https://github.com/Equim-chan/cryptonight),
55-
[randomx](https://git.dero.io/DERO_Foundation/RandomX), X25X, and the full cuckoo suite (cuckatoo, cuckaroo).
51+
[randomx](https://git.dero.io/DERO_Foundation/RandomX), X25X, and the full
52+
cuckoo suite ([cuckatoo, cuckaroo]((https://github.com/blockcypher/libgrin/tree/master/core/pow)).
5653

5754
# Roadmap
5855

59-
Most profitable Proof of Work chains nowdays use some sort of DAG, and that is almost always the Ethash DAG.
60-
There are then layers on top of the Ethash DAG like ProgPow, which can be considered both a Proof of Work
61-
algorithm and a class of algorithms (Kawpow, Firopow). There isn't really a good way to organize all of these
62-
into a given structure but even if there was, a new algorithm could appear tomorrow and break that structure.
63-
powkit takes the approach of allowing most parameters to be varied and will implement new algorithms on an
64-
as-needed basis.
65-
6656
Currently, though powkit is used in production internally, it probably isn't a good idea to use yourself. The
6757
API is still in flux and each minor version will probably be breaking. Once we do a v1.0.0 release, the structure
6858
will probably be pretty set in stone.

autolykos2/README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
# Autolykos2
2-
3-
This is a rough version of the Autolykos2 algorithm for Ergo. It is quite fast
4-
and passes up-to-date test vectors. `Config` is likely to change in the future.
1+
# Autolykos2

autolykos2/autolykos2.go

Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,11 @@ import (
88
"github.com/sencha-dev/powkit/internal/crypto"
99
)
1010

11-
/* config */
12-
13-
type Config struct {
14-
k int
15-
n int
16-
nBase uint32
17-
increaseStart uint64
18-
increasePeriodForN uint64
19-
nIncreasementHeightMax uint64
20-
}
21-
22-
func New(k, n int) *Config {
23-
cfg := &Config{
24-
k: k,
25-
n: n,
26-
nBase: 1 << n,
27-
increaseStart: 600 * 1024,
28-
increasePeriodForN: 50 * 1024,
29-
nIncreasementHeightMax: 4198400,
30-
}
31-
32-
return cfg
33-
}
34-
35-
func NewErgo() *Config {
36-
return New(32, 26)
37-
}
38-
39-
/* helpers */
11+
const (
12+
increaseStart = 600 * 1024
13+
increasePeriodForN = 50 * 1024
14+
nIncreasementHeightMax = 4198400
15+
)
4016

4117
func concatBytes(a, b []byte) []byte {
4218
c := make([]byte, len(a), len(a)+len(b))
@@ -55,16 +31,14 @@ func generateM(size uint64) []byte {
5531
return m
5632
}
5733

58-
/* algorithm */
59-
60-
func (cfg *Config) calcN(height uint64) uint32 {
61-
if height > cfg.nIncreasementHeightMax {
34+
func calcN(nBase uint32, height uint64) uint32 {
35+
if height > nIncreasementHeightMax {
6236
return 2143944600
6337
}
6438

65-
n := cfg.nBase
66-
if height >= cfg.increaseStart {
67-
iters := int((height-cfg.increaseStart)/cfg.increasePeriodForN + 1)
39+
var n uint32 = nBase
40+
if height >= increaseStart {
41+
iters := int((height-increaseStart)/increasePeriodForN + 1)
6842
for i := 0; i < iters; i++ {
6943
n = (n / 100) * 105
7044
}
@@ -73,24 +47,24 @@ func (cfg *Config) calcN(height uint64) uint32 {
7347
return n
7448
}
7549

76-
func (cfg *Config) genIndexes(seed []byte, n uint32) []uint32 {
50+
func genIndexes(seed []byte, k, n uint32) []uint32 {
7751
hash := crypto.Blake2b256(seed)
7852
extendedHash := append(hash, hash...)
7953

80-
indexes := make([]uint32, cfg.k)
54+
indexes := make([]uint32, k)
8155
for i := range indexes {
8256
indexes[i] = binary.BigEndian.Uint32(extendedHash[i:i+4]) % n
8357
}
8458

8559
return indexes
8660
}
8761

88-
func (cfg *Config) Compute(msg []byte, nonce, height uint64) []byte {
62+
func compute(k, nBase uint32, msg []byte, nonce, height uint64) []byte {
8963
m := generateM(1024)
9064
h := convutil.Uint32ToBytes(uint32(height), binary.BigEndian)
9165
nonceBytes := convutil.Uint64ToBytes(nonce, binary.BigEndian)
9266

93-
n := cfg.calcN(height)
67+
n := calcN(nBase, height)
9468
bigN := new(big.Int).SetUint64(uint64(n))
9569

9670
fullMsg := concatBytes(msg, nonceBytes)
@@ -101,7 +75,7 @@ func (cfg *Config) Compute(msg []byte, nonce, height uint64) []byte {
10175
f := crypto.Blake2b256(concatBytes(i, concatBytes(h, m)))[1:32]
10276

10377
seed := concatBytes(f, concatBytes(msg, nonceBytes))
104-
indexes := cfg.genIndexes(seed, n)
78+
indexes := genIndexes(seed, k, n)
10579

10680
f2 := new(big.Int)
10781
for _, index := range indexes {

autolykos2/autolykos2_test.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,61 @@ import (
77
"github.com/sencha-dev/powkit/internal/common/testutil"
88
)
99

10-
func TestCalcNErgo(t *testing.T) {
10+
func TestCalcN(t *testing.T) {
1111
tests := []struct {
12+
nBase uint32
1213
height uint64
1314
value uint32
1415
}{
1516
{
17+
nBase: 1 << 26,
1618
height: 500000,
1719
value: 67108864,
1820
},
1921
{
22+
nBase: 1 << 26,
2023
height: 600000,
2124
value: 67108864,
2225
},
2326
{
27+
nBase: 1 << 26,
2428
height: 614400,
2529
value: 70464240,
2630
},
2731
{
32+
nBase: 1 << 26,
2833
height: 665600,
2934
value: 73987410,
3035
},
3136
{
37+
nBase: 1 << 26,
3238
height: 700000,
3339
value: 73987410,
3440
},
3541
{
42+
nBase: 1 << 26,
3643
height: 788400,
3744
value: 81571035,
3845
},
3946
{
47+
nBase: 1 << 26,
4048
height: 1051200,
4149
value: 104107290,
4250
},
4351
{
52+
nBase: 1 << 26,
4453
height: 4198400,
4554
value: 2143944600,
4655
},
4756
{
57+
nBase: 1 << 26,
4858
height: 41984000,
4959
value: 2143944600,
5060
},
5161
}
5262

5363
for i, tt := range tests {
54-
value := NewErgo().calcN(tt.height)
64+
value := calcN(tt.nBase, tt.height)
5565
if value != tt.value {
5666
t.Errorf("failed on %d: have %d, want %d", i, value, tt.value)
5767
}
@@ -86,8 +96,10 @@ func TestComputeErgo(t *testing.T) {
8696
}
8797

8898
for i, tt := range tests {
89-
result := NewErgo().Compute(tt.msg, tt.nonce, tt.height)
90-
if bytes.Compare(result, tt.result) != 0 {
99+
result, err := NewErgo().Compute(tt.msg, tt.height, tt.nonce)
100+
if err != nil {
101+
t.Errorf("failed on %d: %v", i, err)
102+
} else if bytes.Compare(result, tt.result) != 0 {
91103
t.Errorf("failed on %d: have %x, want %x", i, result, tt.result)
92104
}
93105
}

autolykos2/client.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package autolykos2
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
type Client struct {
8+
k uint32
9+
n uint32
10+
nBase uint32
11+
}
12+
13+
func New(k, n uint32) *Client {
14+
c := &Client{
15+
k: k,
16+
n: n,
17+
nBase: 1 << n,
18+
}
19+
20+
return c
21+
}
22+
23+
func NewErgo() *Client {
24+
return New(32, 26)
25+
}
26+
27+
func (c *Client) Compute(msg []byte, height, nonce uint64) ([]byte, error) {
28+
if len(msg) != 32 {
29+
return nil, fmt.Errorf("msg must be 32 bytes")
30+
}
31+
32+
return compute(c.k, c.nBase, msg, nonce, height), nil
33+
}

beamhashiii/client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package beamhashiii
22

3+
import (
4+
"fmt"
5+
)
6+
37
type Client struct {
48
n uint32
59
k uint32
@@ -21,5 +25,11 @@ func NewBeam() *Client {
2125
}
2226

2327
func (c *Client) Verify(header, soln []byte) (bool, error) {
28+
if len(header) != 40 {
29+
return false, fmt.Errorf("header must be 40 bytes")
30+
} else if len(soln) != 104 {
31+
return false, fmt.Errorf("soln must be 104 bytes")
32+
}
33+
2434
return verify(c.n, c.k, c.personal, header, soln)
2535
}

cuckoo/client.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,42 @@ package cuckoo
33
import (
44
"encoding/base64"
55
"encoding/binary"
6+
"fmt"
67

78
"github.com/sencha-dev/powkit/internal/crypto"
89
)
910

10-
type Config struct {
11+
type Client struct {
1112
proofSize int
1213
edgeBits int
1314
edgeMask uint64
1415
nodeBits int
1516
nodeMask uint64
1617
}
1718

18-
func New(edgeBits, nodeBits, proofSize int) *Config {
19-
edgeNum := uint64(1) << edgeBits
20-
nodeNum := uint64(1) << nodeBits
21-
22-
cfg := &Config{
19+
func New(edgeBits, nodeBits, proofSize int) *Client {
20+
c := &Client{
2321
proofSize: proofSize,
2422
edgeBits: edgeBits,
25-
edgeMask: edgeNum - 1,
23+
edgeMask: (uint64(1) << edgeBits) - 1,
2624
nodeBits: nodeBits,
27-
nodeMask: nodeNum - 1,
25+
nodeMask: (uint64(1) << nodeBits) - 1,
2826
}
2927

30-
return cfg
28+
return c
3129
}
3230

33-
func NewAeternity() *Config {
31+
func NewAeternity() *Client {
3432
return New(29, 29, 42)
3533
}
3634

37-
func (cfg *Config) Verify(hash []byte, nonce uint64, sols []uint64) bool {
35+
func (c *Client) Verify(hash []byte, nonce uint64, sols []uint64) (bool, error) {
36+
if len(hash) != 32 {
37+
return false, fmt.Errorf("hash must be 32 bytes")
38+
} else if len(sols) != 42 {
39+
return false, fmt.Errorf("sols must be 42 uint64s")
40+
}
41+
3842
// encode header
3943
nonceBytes := make([]uint8, 8)
4044
binary.LittleEndian.PutUint64(nonceBytes, nonce)
@@ -51,5 +55,5 @@ func (cfg *Config) Verify(hash []byte, nonce uint64, sols []uint64) bool {
5155
binary.LittleEndian.Uint64(h[24:32]),
5256
}
5357

54-
return cfg.verify(keys, sols)
58+
return verify(c.proofSize, c.edgeMask, keys, sols)
5559
}

0 commit comments

Comments
 (0)