Skip to content

Commit

Permalink
src: enable lwma PoW algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
ramizpolic committed Aug 26, 2020
1 parent 114a125 commit 0ae0e0d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 12 deletions.
9 changes: 9 additions & 0 deletions electrum/bitcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@
from .network import Network


def is_post_btg_fork(height):
return height >= constants.net.BTG_HEIGHT

def needs_retarget(height):
return is_post_btg_fork(height) or (height % difficulty_adjustment_interval() == 0)

def difficulty_adjustment_interval():
return constants.net.POW_TARGET_TIMESPAN_LEGACY // constants.net.POW_TARGET_SPACING

################################## transactions

COINBASE_MATURITY = 100
Expand Down
86 changes: 76 additions & 10 deletions electrum/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,16 +315,16 @@ def verify_header(cls, header: dict, prev_hash: str, target: int, expected_heade
def verify_chunk(self, index: int, data: bytes) -> None:
num = len(data) // HEADER_SIZE
start_height = index * 2016
prev_hash = self.get_hash(start_height - 1)
target = self.get_target(index-1)
for i in range(num):
height = start_height + i
target = self.get_target(height)
prev_hash = self.get_hash(start_height - 1)
try:
expected_header_hash = self.get_hash(height)
except MissingHeader:
expected_header_hash = None
raw_header = data[i*HEADER_SIZE : (i+1)*HEADER_SIZE]
header = deserialize_header(raw_header, index*2016 + i)
header = deserialize_header(raw_header, start_height + i)
self.verify_header(header, prev_hash, target, expected_header_hash)
prev_hash = hash_header(header)

Expand Down Expand Up @@ -519,7 +519,68 @@ def is_height_checkpoint():
raise MissingHeader(height)
return hash_header(header)

def get_target(self, index: int) -> int:
def get_header(self, height, headers=None):
if headers is None:
headers = {}
return headers[height] if height in headers else self.read_header(height)

def get_target(self, height: int) -> int:
if height < constants.net.BTG_HEIGHT:
new_target = self.get_legacy_target(height // 2016 - 1)
# Premine
elif height < constants.net.BTG_HEIGHT + constants.net.PREMINE_SIZE:
new_target = constants.net.POW_LIMIT
# Zawy LWMA
else:
new_target = self.get_lwma_target(height, None, constants.net.LWMA_ADJUST_WEIGHT,
constants.net.LWMA_MIN_DENOMINATOR)
return new_target

def get_lwma_target(self, height, headers, weight, denominator):
cur = self.get_header(height, headers)
last_height = (height - 1)
last = self.get_header(last_height, headers)

# Special testnet handling
if constants.net.TESTNET and cur.get('timestamp') > last.get('timestamp') + constants.net.POW_TARGET_SPACING * 2:
new_target = constants.net.POW_LIMIT
else:
total = 0
t = 0
j = 0

assert (height - constants.net.LWMA_AVERAGING_WINDOW) > 0

ts = 6 * constants.net.POW_TARGET_SPACING

# Loop through N most recent blocks. "< height", not "<=".
# height-1 = most recently solved block
for i in range(height - constants.net.LWMA_AVERAGING_WINDOW, height):
cur = self.get_header(i, headers)
prev_height = (i - 1)
prev = self.get_header(prev_height, headers)

solvetime = cur.get('timestamp') - prev.get('timestamp')

if constants.net.LWMA_SOLVETIME_LIMITATION and solvetime > ts:
solvetime = ts

j += 1
t += solvetime * j
total += self.bits_to_target(cur.get('bits')) // (weight * constants.net.LWMA_AVERAGING_WINDOW * constants.net.LWMA_AVERAGING_WINDOW)

# Keep t reasonable in case strange solvetimes occurred.
if t < constants.net.LWMA_AVERAGING_WINDOW * weight // denominator:
t = constants.net.LWMA_AVERAGING_WINDOW * weight // denominator

new_target = t * total

if new_target > constants.net.POW_LIMIT:
new_target = constants.net.POW_LIMIT

return new_target

def get_legacy_target(self, index: int) -> int:
# compute target from chunk x, used in chunk x+1
if constants.net.TESTNET:
return 0
Expand Down Expand Up @@ -567,8 +628,7 @@ def target_to_bits(cls, target: int) -> int:

def chainwork_of_header_at_height(self, height: int) -> int:
"""work done by single header at given height"""
chunk_idx = height // 2016 - 1
target = self.get_target(chunk_idx)
target = self.get_target(height)
work = ((2 ** 256 - target - 1) // (target + 1)) + 1
return work

Expand Down Expand Up @@ -614,7 +674,7 @@ def can_connect(self, header: dict, check_height: bool=True) -> bool:
if prev_hash != header.get('prev_block_hash'):
return False
try:
target = self.get_target(height // 2016 - 1)
target = self.get_target(height)
except MissingHeader:
return False
try:
Expand All @@ -637,10 +697,16 @@ def connect_chunk(self, idx: int, hexdata: str) -> bool:
def get_checkpoints(self):
# for each chunk, store the hash of the last block and the target after the chunk
cp = []
n = self.height() // 2016
diff_adj = difficulty_adjustment_interval()
n = self.height() // diff_adj
for index in range(n):
h = self.get_hash((index+1) * 2016 -1)
target = self.get_target(index)
height = (index + 1) * diff_adj

if is_post_btg_fork(height):
break

h = self.get_hash(height - 1)
target = self.get_target(height)
cp.append((h, target))
return cp

Expand Down
24 changes: 24 additions & 0 deletions electrum/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class BitcoinMainnet(AbstractNet):
ADDRTYPE_P2PKH = 38
ADDRTYPE_P2SH = 23
SEGWIT_HRP = "glob"

GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
DEFAULT_PORTS = {'t': '50001', 's': '50002'}
DEFAULT_SERVERS = read_json('servers.json', {})
Expand Down Expand Up @@ -93,6 +94,17 @@ class BitcoinMainnet(AbstractNet):
#'lseed.bitcoinstats.com.',
]

BTG_HEIGHT = 638592
PREMINE_SIZE = 100
BTG_REWARD = 1000
POW_LIMIT = 0x00000000ffff0000000000000000000000000000000000000000000000000000

LWMA_AVERAGING_WINDOW = 45
LWMA_ADJUST_WEIGHT = 13500
LWMA_MIN_DENOMINATOR = 10
POW_TARGET_SPACING = 10 * 60
POW_TARGET_TIMESPAN_LEGACY = 14 * 24 * 60 * 60
LWMA_SOLVETIME_LIMITATION = True

class BitcoinTestnet(AbstractNet):

Expand Down Expand Up @@ -129,6 +141,18 @@ class BitcoinTestnet(AbstractNet):
#'lseed.bitcoinstats.com.', # ignores REALM byte and returns mainnet peers...
]

BTG_HEIGHT = 1780318
PREMINE_SIZE = 50
BTG_REWARD = 1000
POW_LIMIT = 0x00000000ffff0000000000000000000000000000000000000000000000000000

LWMA_AVERAGING_WINDOW = 45
LWMA_ADJUST_WEIGHT = 13500
LWMA_MIN_DENOMINATOR = 10
POW_TARGET_SPACING = 10 * 60
POW_TARGET_TIMESPAN_LEGACY = 14 * 24 * 60 * 60
LWMA_SOLVETIME_LIMITATION = False


class BitcoinRegtest(BitcoinTestnet):

Expand Down
4 changes: 2 additions & 2 deletions electrum/version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ELECTRUM_VERSION = '4.0.2' # version of the client package
APK_VERSION = '4.0.2.0' # read by buildozer.spec
ELECTRUM_VERSION = '4.0.3' # version of the client package
APK_VERSION = '4.0.3.0' # read by buildozer.spec

PROTOCOL_VERSION = '1.4' # protocol version requested

Expand Down

0 comments on commit 0ae0e0d

Please sign in to comment.