1// SPDX-License-Identifier: ISC
2// Copyright (c) 2014-2020 Bitmark Inc.
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package difficulty_test
7
8import (
9	"encoding/json"
10	"fmt"
11	"math"
12	"testing"
13
14	"github.com/stretchr/testify/assert"
15
16	"github.com/bitmark-inc/bitmarkd/difficulty"
17)
18
19// test difficulty initiialisation
20func TestInitialBits(t *testing.T) {
21
22	expected := difficulty.OneUint64
23
24	actual := difficulty.Current.Bits()
25
26	if actual != expected {
27		t.Errorf("actual: %d  expected: %d", actual, expected)
28	}
29}
30
31type testItem struct {
32	bits       uint64
33	reciprocal float64
34	big        string
35	bigf       string
36}
37
38var tests = []testItem{
39	{
40		reciprocal: 1,
41		bits:       0x00ffffffffffffff,
42		big:        "00ffffffffffffff800000000000000000000000000000000000000000000000",
43		bigf:       "00ffffffffffffff800000000000000000000000000000000000000000000000",
44	},
45	{
46		reciprocal: 2,
47		bits:       0x01ffffffffffffff,
48		big:        "007fffffffffffffc00000000000000000000000000000000000000000000000",
49		bigf:       "007fffffffffffffc00000000000000000000000000000000000000000000000",
50	},
51	{
52		reciprocal: 2.2874055134732947,
53		bits:       0x01bfab3425899de4,
54		big:        "006feacd09626779000000000000000000000000000000000000000000000000",
55		bigf:       "006feacd0962677916d10918344a505d5a363a023349f9c39623104c540b1ae2",
56	},
57	{
58		reciprocal: 16,
59		bits:       0x04ffffffffffffff,
60		big:        "000ffffffffffffff80000000000000000000000000000000000000000000000",
61		bigf:       "000ffffffffffffff80000000000000000000000000000000000000000000000",
62	},
63	{
64		reciprocal: 64,
65		bits:       0x06ffffffffffffff,
66		big:        "0003fffffffffffffe0000000000000000000000000000000000000000000000",
67		bigf:       "0003fffffffffffffe0000000000000000000000000000000000000000000000",
68	},
69	{
70		reciprocal: 256,
71		bits:       0x08ffffffffffffff,
72		big:        "0000ffffffffffffff8000000000000000000000000000000000000000000000",
73		bigf:       "0000ffffffffffffff8000000000000000000000000000000000000000000000",
74	},
75	{
76		reciprocal: 511.99999999999997,
77		bits:       0x0800000000000007,
78		big:        "0000800000000000038000000000000000000000000000000000000000000000",
79		bigf:       "000080000000000003c000000000001e000000000000f0000000000007800000",
80	},
81	{
82		reciprocal: 1000,
83		bits:       0x090624dd2f1a9fbd,
84		big:        "00004189374bc6a7ef4000000000000000000000000000000000000000000000",
85		bigf:       "00004189374bc6a7ef7ced916872b020c49ba5e353f7ced916872b020c49ba5e",
86	},
87	{
88		reciprocal: 10000,
89		bits:       0x0da36e2eb1c432c9,
90		big:        "0000068db8bac710cb2400000000000000000000000000000000000000000000",
91		bigf:       "0000068db8bac710cb2617c1bda5119ce075f6fd21ff2e48e8a71de69ad42c3c",
92	},
93	{
94		reciprocal: 47643398017.803443,
95		bits:       0x23713f413f413f40,
96		big:        "00000000001713f413f413f40000000000000000000000000000000000000000",
97		bigf:       "00000000001713f413f413f40821936d0ab882041e769aaa6e7999e3a827ef1b",
98	},
99	{
100		reciprocal: 1e15,
101		bits:       0x31203af9ee756159,
102		big:        "00000000000000480ebe7b9d5856400000000000000000000000000000000000",
103		bigf:       "00000000000000480ebe7b9d585648806f5db1f9cfcec44485b1756799f713b1",
104	},
105	{
106		reciprocal: 9223372036854775808,
107		bits:       0x3fffffffffffffff,
108		big:        "000000000000000001ffffffffffffff00000000000000000000000000000000",
109		bigf:       "000000000000000001ffffffffffffff00000000000000000000000000000000",
110	},
111	{
112		reciprocal: 18446744073709551616,
113		bits:       0x40ffffffffffffff,
114		big:        "000000000000000000ffffffffffffff80000000000000000000000000000000",
115		bigf:       "000000000000000000ffffffffffffff80000000000000000000000000000000",
116	},
117	{
118		reciprocal: 36893488147419099136,
119		bits:       0x4000000000000007,
120		big:        "0000000000000000008000000000000380000000000000000000000000000000",
121		bigf:       "00000000000000000080000000000003c000000000001e000000000000f00000",
122	},
123	{
124		reciprocal: 3138550867693340381917894711603833208051177722232017256448,
125		bits:       0xbfffffffffffffff,
126		big:        "00000000000000000000000000000000000000000000000001ffffffffffffff",
127		bigf:       "00000000000000000000000000000000000000000000000001ffffffffffffff",
128	},
129	{ // the smallest value allowed (panics if smaller) = hash with 24 leading zero bytes!
130		reciprocal: 6277101735386680066937501969125693243111159424202737451008,
131		bits:       0xbf00000000000007,
132		big:        "0000000000000000000000000000000000000000000000000100000000000007",
133		bigf:       "0000000000000000000000000000000000000000000000000100000000000007",
134	},
135	// { // 13 - the theoretical smallest possible non-zero value - not useful
136	// 	reciprocal: ?,
137	// 	bits:       0xf7ffffffffffffff,
138	// 	big:        "0000000000000000000000000000000000000000000000000000000000000001",
139	// 	bigf:       "0000000000000000000000000000000000000000000000000000000000000001",
140	// },
141}
142
143// test 64 bit word
144func TestUint64(t *testing.T) {
145
146	d := difficulty.New()
147
148	for i, item := range tests {
149
150		d.SetBits(item.bits)
151		actual := d.Value()
152
153		if actual != item.reciprocal {
154			t.Errorf("%d: actual: %20.10f  reciprocal: %20.10f  diff: %g", i, actual, item.reciprocal, actual-item.reciprocal)
155		}
156
157		hexActual := d.String()
158		hexExpected := fmt.Sprintf("%016x", item.bits)
159
160		if hexActual != hexExpected {
161			t.Errorf("%d: hex: actual: %q  expected: %q", i, hexActual, hexExpected)
162		}
163
164		bigActual := fmt.Sprintf("%064x", d.BigInt())
165
166		if bigActual != item.big {
167			t.Errorf("%d: big: actual: %q  expected: %q", i, bigActual, item.big)
168		}
169	}
170
171}
172
173// test bytes
174func TestBytes(t *testing.T) {
175
176	d := difficulty.New()
177
178	// 0x0da36e2eb1c432c9
179	bits := []byte{0xc9, 0x32, 0xc4, 0xb1, 0x2e, 0x6e, 0xa3, 0x0d} // little endian bytes
180	d.SetBytes(bits)
181
182	expected := float64(10000)
183	actual := d.Value()
184
185	bits2 := d.Bits()
186
187	if math.Abs(actual-expected) > 0.000001 {
188		t.Errorf("0x%016x:  actual: %f  expected: %f  diff: %g", bits2, actual, expected, actual-expected)
189	}
190}
191
192// test JSON
193func TestJSON(t *testing.T) {
194
195	d := difficulty.New()
196
197	for i, item := range tests {
198
199		d.SetBits(item.bits)
200
201		buffer, err := json.Marshal(d)
202		if nil != err {
203			t.Fatalf("%d: JSON encode error: %s", i, err)
204		}
205
206		dNew := difficulty.New()
207		err = json.Unmarshal(buffer, dNew)
208		if nil != err {
209			t.Fatalf("%d: JSON decode error: %s", i, err)
210		}
211
212		actual := dNew.Bits()
213		expected := item.bits
214
215		if actual != expected {
216			t.Errorf("%d: JSON actual: %016x  expected: %016x", i, actual, expected)
217		}
218	}
219}
220
221// test floating point (reciprocal)
222func TestReciprocal(t *testing.T) {
223
224	d := difficulty.New()
225
226	for i, item := range tests {
227
228		d.Set(item.reciprocal)
229		actual := d.Bits()
230
231		if actual != item.bits {
232			t.Errorf("%d: actual: 0x%016x  bits: 0x%016x", i, actual, item.bits)
233		}
234
235		hexActual := d.String()
236		hexExpected := fmt.Sprintf("%016x", item.bits)
237
238		if hexActual != hexExpected {
239			t.Errorf("%d: hex: actual: %q  expected: %q", i, hexActual, hexExpected)
240		}
241
242		bigActual := fmt.Sprintf("%064x", d.BigInt())
243
244		if bigActual != item.bigf {
245			t.Errorf("%d: big: actual: %q  expected: %q", i, bigActual, item.bigf)
246		}
247		bitsString := fmt.Sprintf("%v", d)
248		if bitsString != hexExpected {
249			t.Errorf("%d: String(): actual: %q  expected: %q", i, bitsString, hexExpected)
250		}
251
252		bigString := fmt.Sprintf("%#v", d)
253		if bigString != item.bigf {
254			t.Errorf("%d: GoString(): actual: %v  expected: %q", i, bigString, item.bigf)
255		}
256	}
257}
258
259func TestPrevTimespanBlockBeginAndEndWhenAtMiddle(t *testing.T) {
260	height := uint64(difficulty.AdjustTimespanInBlocks*3 + 10)
261	begin, end := difficulty.PrevTimespanBlockBeginAndEnd(height)
262
263	assert.Equal(t, uint64(difficulty.AdjustTimespanInBlocks*3-1-difficulty.AdjustTimespanInBlocks), begin, "fail to get begin block")
264	assert.Equal(t, uint64(difficulty.AdjustTimespanInBlocks*3-1), end, "get end block at middle")
265}
266
267func TestPrevTimespanBlockBeginAndEndWhenAtStart(t *testing.T) {
268	height := uint64(difficulty.AdjustTimespanInBlocks * 3)
269	begin, end := difficulty.PrevTimespanBlockBeginAndEnd(height)
270
271	assert.Equal(t, uint64(difficulty.AdjustTimespanInBlocks*3-1-difficulty.AdjustTimespanInBlocks), begin, "fail to get begin block")
272	assert.Equal(t, uint64(difficulty.AdjustTimespanInBlocks*3-1), end, "get end block at start")
273}
274
275func TestPrevTimespanBlockBeginAndEndWhenInFirstTimespan(t *testing.T) {
276	height := uint64(difficulty.AdjustTimespanInBlocks + 10)
277	begin, end := difficulty.PrevTimespanBlockBeginAndEnd(height)
278
279	assert.Equal(t, uint64(2), begin, "fail to get begin block")
280	assert.Equal(t, uint64(difficulty.AdjustTimespanInBlocks-1), end, "get end block in first timespan")
281}
282
283func TestHashrate(t *testing.T) {
284	difficulty.Current.Set(4.8)
285	hashrate := difficulty.Hashrate()
286
287	// difficulty 4.8, log2(4.8) = 2.263034405833794
288	// total bits of empty zero will 8+2.263034405833794 = 10.263034405833794 bits
289	// possible hashes for a correct one is pow(2, 10.263034405833794) = 1228.8000000000004 hashes
290	// expected time for a block is 120 seconds
291	// hash rate = hashes / time = 1228.8000000000004 / 120
292	expected := math.Floor(float64(1228.8000000000004)/120*1000) / 1000
293	assert.Equal(t, expected, hashrate, "network hashrate")
294}
295
296func TestNextDifficultyByPreviousTimespanWhenTooLong(t *testing.T) {
297	diff := float64(8)
298	targetTimespan := 2 * 60 * 200
299	testTime := targetTimespan * 8
300	actual := difficulty.NextDifficultyByPreviousTimespan(uint64(testTime), diff)
301
302	assert.Equal(t, diff/4, actual, "wrong difficulty adjust")
303}
304
305func TestNextDifficultyByPreviousTimespanWhenTooShort(t *testing.T) {
306	diff := float64(8)
307	targetTimespan := 2 * 60 * 200
308	testTime := targetTimespan / 8
309	actual := difficulty.NextDifficultyByPreviousTimespan(uint64(testTime), diff)
310
311	assert.Equal(t, diff*4, actual, "wrong difficulty adjust")
312}
313
314func TestNextDifficultyByPreviousTimespanWhenLarger(t *testing.T) {
315	diff := float64(8)
316	targetTimespan := 2 * 60 * 200
317	testTime := targetTimespan * 3
318	actual := difficulty.NextDifficultyByPreviousTimespan(uint64(testTime), diff)
319
320	assert.Equal(t, diff/3, actual, "wrong difficulty adjust")
321}
322
323func TestNextDifficultyByPreviousTimespanWhenSmaller(t *testing.T) {
324	diff := float64(8)
325	targetTimespan := 2 * 60 * 200
326	testTime := targetTimespan / 3
327	actual := difficulty.NextDifficultyByPreviousTimespan(uint64(testTime), diff)
328
329	assert.Equal(t, diff*3, actual, "wrong difficulty adjust")
330}
331