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