Module: BTC::ProofOfWork

Extended by:
ProofOfWork
Included in:
ProofOfWork
Defined in:
lib/btcruby/proof_of_work.rb

Overview

Proof of work is specified using several terms.

  1. target is big unsigned integer derived from 256-bit hash (interpreted as little-endian integer). Hash of a valid block should be below target.
  2. bits is a 'satoshi compact' representation of a target as uint32.
  3. difficulty is a floating point multiple of the minimum difficulty. Difficulty = 2 means the block is 2x more difficult than the minimal difficulty.

Constant Summary collapse

MAX_TARGET_MAINNET =
0x00000000ffff0000000000000000000000000000000000000000000000000000
MAX_TARGET_TESTNET =
0x00000007fff80000000000000000000000000000000000000000000000000000

Instance Method Summary collapse

Instance Method Details

#bits_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET) ⇒ Object

Computes bits from difficulty. Could be inaccurate since difficulty is a limited-precision floating-point number. Default max_target is for Bitcoin mainnet. float -> int32



64
65
66
# File 'lib/btcruby/proof_of_work.rb', line 64

def bits_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET)
  bits_from_target(target_from_difficulty(difficulty, max_target: max_target))
end

#bits_from_target(target) ⇒ Object

Converts 256-bit integer to 32-bit compact representation.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/btcruby/proof_of_work.rb', line 32

def bits_from_target(target)
  exponent = 3
  signed = (target < 0)
  target = -target if signed
  while target > 0x7fffff
    target >>= 8
    exponent += 1
  end
  # The 0x00800000 bit denotes the sign.
  # Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
  if (target & 0x00800000) > 0
    target >>= 8
    exponent += 1
  end
  result = (exponent << 24) + target
  result = result | 0x00800000 if signed
  result
end

#difficulty_from_bits(bits, max_target: MAX_TARGET_MAINNET) ⇒ Object

Computes difficulty from bits. Default max_target is for Bitcoin mainnet. int32 -> float



71
72
73
# File 'lib/btcruby/proof_of_work.rb', line 71

def difficulty_from_bits(bits, max_target: MAX_TARGET_MAINNET)
  difficulty_from_target(target_from_bits(bits), max_target: max_target)
end

#difficulty_from_target(target, max_target: MAX_TARGET_MAINNET) ⇒ Object

Compute relative difficulty from a given target. E.g. returns 2.5 if target is 2.5 times harder to reach than the max_target. Default max_target is for Bitcoin mainnet. bigint -> float



87
88
89
# File 'lib/btcruby/proof_of_work.rb', line 87

def difficulty_from_target(target, max_target: MAX_TARGET_MAINNET)
  (max_target / target.to_f)
end

#hash_from_target(target) ⇒ Object

Converts target integer to a binary 32-byte hash. bigint -> hash256



93
94
95
96
97
98
99
100
# File 'lib/btcruby/proof_of_work.rb', line 93

def hash_from_target(target)
  bytes = []
  while target > 0
    bytes << (target % 256)
    target /= 256
  end
  BTC::Data.data_from_bytes(bytes).ljust(32, "\x00".b)
end

#target_from_bits(bits) ⇒ Object

Converts 32-bit compact representation to a 256-bit integer. int32 -> bigint



53
54
55
56
57
58
# File 'lib/btcruby/proof_of_work.rb', line 53

def target_from_bits(bits)
  exponent = ((bits >> 24) & 0xff)
  mantissa = bits & 0x7fffff
  mantissa *= -1 if (bits & 0x800000) > 0
  (mantissa * (256**(exponent-3))).to_i
end

#target_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET) ⇒ Object

Computes target from difficulty. Could be inaccurate since difficulty is a limited-precision floating-point number. Default max_target is for Bitcoin mainnet. float -> bigint



79
80
81
# File 'lib/btcruby/proof_of_work.rb', line 79

def target_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET)
  (max_target / difficulty).round.to_i
end

#target_from_hash(hash) ⇒ Object

Converts 32-byte hash to target big integer (hash is treated as little-endian integer) hash256 -> bigint



104
105
106
107
108
109
110
111
112
# File 'lib/btcruby/proof_of_work.rb', line 104

def target_from_hash(hash)
  target = 0
  i = 0
  hash.each_byte do |byte|
    target += byte * (256**i)
    i += 1
  end
  target
end

#work_from_hash(hash) ⇒ Object

hash256 -> bigint



129
130
131
# File 'lib/btcruby/proof_of_work.rb', line 129

def work_from_hash(hash)
  work_from_target(target_from_hash(hash))
end

#work_from_target(target) ⇒ Object

Compute amount of work expressed as a target Based on arith_uint256 GetBlockProof(const CBlockIndex& block) from Bitcoin Core bigint -> bigint



119
120
121
122
123
124
125
126
# File 'lib/btcruby/proof_of_work.rb', line 119

def work_from_target(target)
  # We need to compute 2**256 / (target+1), but we can't represent 2**256
  # as it's too large for a arith_uint256. However, as 2**256 is at least as large
  # as target+1, it is equal to ((2**256 - target - 1) / (target+1)) + 1,
  # or ~target / (target+1) + 1.
  # In Ruby bigint is signed, so we can't use '~', but we can use 2**256
  return ((2**256 - target - 1) / (target + 1)) + 1
end