Skip to content
Snippets Groups Projects
Commit 2bd0ca8e authored by MILLER Loic's avatar MILLER Loic
Browse files

Merge branch 'master' of gitlab.imt-atlantique.fr:b23loiso/mining-in-logarithmic-space

parents efaec43a 39d05af4
Branches
No related tags found
No related merge requests found
......@@ -28,7 +28,7 @@ def loadHeaders(break_at):
global headers
print('Loading headers...')
if not LOAD_FROM_HEADERS_FILE:
headersNumber = break_at if break_at else cli_json(['getblockchaininfo'])['headers']
headersNumber = break_at if break_at else int(cli(['getblockcount']))
headerHashes = pull('getblockhash', list(range(headersNumber)), 2)
headers = pull('getblockheader', headerHashes, 3)
else:
......
......@@ -3,6 +3,7 @@ import math
# bits_to_target(int(headers[0]['bits'], 16))
genesisTarget = 26959535291011309493156476344723991336010898738574164086137773096960
IS_EPSILON_TARGET_DEFINITION = True
MINING_IN_LOGARITHMIC_SPACE_CONSTANT_DIFFICULTY_LEVEL = False
class Block:
# `target` and `_hash` are `int`s.
......@@ -31,5 +32,5 @@ class Block:
def level(_hash, target):
ratio = _hash / target
ratio = _hash / (genesisTarget if MINING_IN_LOGARITHMIC_SPACE_CONSTANT_DIFFICULTY_LEVEL else target)
return math.floor(-math.log2(ratio))
\ No newline at end of file
......@@ -44,7 +44,7 @@ def Dissolve(m, k, C, previous_score, previous_ℓ):
def Compress(m, k, C, previous_score, previous_ℓ):
(D, , χ, new_score) = Dissolve(m, k, C, previous_score, previous_ℓ)
π = U(0, , D)
π = U(D)
return π + χ, new_score,
def uparrow(C, μ):
......@@ -93,9 +93,9 @@ def llbracket(C, μ, CIndex, actually = False):
def score(C, ):
return sum([(min(block.level, ) + 1 - block.level_min) * block.score for block in C])
def U(lowerBound, upperBound, chains):
def U(chains):
chains = [chains[μ] for μ in chains]
flattened_chain = [block for chain in chains[lowerBound:upperBound+1] for block in chain]
flattened_chain = [block for chain in chains for block in chain]
unique_blocks = {}
for block in flattened_chain:
if block.height not in unique_blocks or block.level_min < unique_blocks[block.height].level_min:
......
from bcolors import bcolors
import time
import bitcoin
import json
from argparser import get_parser
import config
import copy
# Bitcoin parameters
κ = 256
def Dissolve(m, k, C, previous_score, previous_ℓ):
CStar = C[:-k]
D = {}
if len(CStar) >= 2 * m:
= getℓ(CStar, previous_ℓ)
D[] = uparrow(CStar, )
previousCStarμ = D[]
for μ in range( - 1, -1, -1):
b = previousCStarμ[-m]
CStarμ = uparrow(CStar, μ)
CStarμSinceb = [block for block in CStarμ if block.height >= b.height]
chains = {chainsIndex: chain for chainsIndex, chain in enumerate([CStarμ[-2 * m:], CStarμSinceb])}
D[μ] = U(chains)
previousCStarμ = CStarμ
else:
= 0
D[0] = CStar
CStarScore = score(CStar, )
χ = C[-k:]
return (D, , χ, CStarScore)
def Compress(m, k, C, previous_score, previous_ℓ):
(D, , χ, new_score) = Dissolve(m, k, C, previous_score, previous_ℓ)
π = U(0, , D)
return π + χ, new_score,
def uparrow(C, μ):
chain = copy.deepcopy([block for block in C if block.level >= μ and block.level_min <= μ])
for blockIndex, block in enumerate(chain):
block.level_min = μ
return chain
# Proceeding by dichotomy may not be worth it.
# For instance for the first 60 000 blocks, we have in theory 10 levels for the last blocks while log2(256) = 8, so because of first blocks not having a higher than 8 level, it doesn't seem worth it at the beginning at least.
def getℓ(C, previous_ℓ):
previous_ℓ += 1
# Can't use `[0] * previous_ℓ`, as would have to check if the array is long enough below before updating its values.
blocksPerLevel = [0] * κ
for block in C:
for level in range(previous_ℓ, block.level + 1):
blocksPerLevel[level] += 1
for level in range(previous_ℓ, κ):
if blocksPerLevel[level] < 2 * m:
return level - 1
def score(C, ):
return sum([(min(block.level, ) + 1 - block.level_min) * block.score for block in C])
def U(lowerBound, upperBound, chains):
chains = [chains[μ] for μ in chains]
flattened_chain = [block for chain in chains[lowerBound:upperBound+1] for block in chain]
unique_blocks = {}
for block in flattened_chain:
if not block.height in unique_blocks or block.level_min < unique_blocks[block.height].level_min:
unique_blocks[block.height] = block
unique_blocks = unique_blocks.values()
sorted_blocks = sorted(unique_blocks, key=lambda block: block.height)
return sorted_blocks
lastTimeCheck = None
def printTimeSinceLastTimeCheck(s):
if verifyCorrectness:
return
global lastTimeCheck
currentTime = time.time()
if lastTimeCheck == None:
lastTimeCheck = currentTime
print(s, round((currentTime - lastTimeCheck) * 1_000, 3), 'ms')
lastTimeCheck = currentTime
def debug(*args):
if debugging:
print(*args)
parser = get_parser() # Create a parser
args = parser.parse_args() # Parse arguments
if args.headers != config.HEADERS_FILE_PATH:
args.load_from_headers = True
print(args)
# Mining in Logarithmic Space parameters
k = args.unstable_part_length # TODO: be able to redo Loïc computation
m = 3 * k
debugging = args.debugging
verifyCorrectness = args.verify_correctness
config.HEADERS_FILE_PATH = args.headers
bitcoin.LOAD_FROM_HEADERS_FILE = args.load_from_headers
lvls = {}
targets = []
compressSize = []
compressScore = []
previous_score = 0
previous_ℓ = 0
Π = []
headersNumber = bitcoin.loadHeaders(args.break_at)
for height in range(headersNumber):
lastTimeCheck = time.time()
b = bitcoin.getBlockByHeight(height)
# Not precise at the block scale.
targets += [b.target]
compressSize += [len(Π)]
compressScore += [previous_score]
#printTimeSinceLastTimeCheck('block retrieval')
isLastIteration = height == headersNumber - 1
debugging = debugging or isLastIteration
verifyCorrectness = verifyCorrectness or isLastIteration
debug(f'{height=}')
bLvl = b.level
for lvl in range(bLvl + 1):
if lvl in lvls:
lvls[lvl] += 1
else:
lvls[lvl] = 1
for lvl in lvls:
debug(f'{lvl}: {lvls[lvl]}')
debug(f'Before compress block level {bLvl} {b.score}')
Π, previous_score, previous_ℓ = Compress(m, k, Π + [b], previous_score, previous_ℓ)
printTimeSinceLastTimeCheck(f'compress computation ({height=})')
if verifyCorrectness:
(D, , χ, previous_score) = Dissolve(m, k, Π, previous_score, previous_ℓ)
debug(f'After compress (score: {score(Π[:-k], )}):')
#printTimeSinceLastTimeCheck('dissolve computation')
for μ in D:
debug(bcolors.WARNING + f'μ: {μ}' + bcolors.ENDC + f', len(Π): {len(Π)}, ' + bcolors.OKGREEN + f'len(D[μ]): {len(D[μ])}' + bcolors.ENDC + f' ({[f"{b.height} ({b.level_min} - {b.level}, {round(b.score, 4)})" for b in D[μ]]})')
#if ℓ == 2:
# break
if args.break_at:
if height == args.break_at:
break
if args.step:
input()
data = {
'targets': targets,
'compressSize': compressSize,
'compressScore': compressScore
}
with open('data.json', 'w') as f:
json.dump(data, f)
\ No newline at end of file
......@@ -2,34 +2,68 @@ import json
import math
import matplotlib.pyplot as plt
SHOW_TARGET_RECOMPUTATION = False
with open('data.json') as f:
data = json.load(f)
fig, ax1 = plt.subplots()
targets = [math.log2(target) for target in data['targets']]
with open('data_constant_difficulty.json') as f:
dataConstantDifficulty = json.load(f)
ax1.plot(targets, label = 'target', color = 'blue')
plt.legend(loc = 'upper left')
fig, targetsAxis = plt.subplots()
fig.subplots_adjust(right = 0.8)
fig.subplots_adjust(bottom = 0.4)
compressedScoreAxis = targetsAxis.twinx()
axes = [targetsAxis, targetsAxis.twinx(), compressedScoreAxis, targetsAxis.twinx(), compressedScoreAxis]
ax2 = ax1.twinx()
ax3 = ax1.twinx()
targets = data['targets']
ax2.plot(data['compressSize'], label = 'compress size', color = 'green')
ax2.legend(loc = 'upper right')
ax3.plot(data['compressScore'], label = 'compress score', color = 'brown')
ax3.legend(loc = 'lower right')
INDEX_OF_FIRST_TARGET_DECREASE = 32_256
plt.axvline(x = INDEX_OF_FIRST_TARGET_DECREASE, color = 'purple')
for x, (target, nextTarget) in enumerate(zip(targets, targets[1:])):
if nextTarget < target:
firstTargetDecreaseCurve = plt.axvline(x = x, color = 'purple', label = 'first target decrease')
break
for x in range(0, len(data['targets']), 2016):
plt.axvline(x = x, alpha = 0.1)
targetRecomputationCurve = []
if SHOW_TARGET_RECOMPUTATION:
for x in range(0, len(targets), 2016):
targetRecomputationCurve = [plt.axvline(x = x, alpha = 0.1, label = 'target recomputation')]
for x, (target, nextTarget) in enumerate(zip(targets, targets[1:])):
if nextTarget > target:
plt.axvline(x = x, color = 'red', label = 'target increase')
targetIncreaseCurve = plt.axvline(x = x, alpha = 0.5, color = 'red', label = 'target increase')
axesColors = ['blue', 'green', 'brown', 'orange', 'brown']
axesLabels = ['targets', 'compressed blockchain size (in blocks)', 'compressed blockchain score', 'constant difficulty compressed blockchain size (in blocks)', 'constant difficulty compressed blockchain score']
axesYValues = [targets, data['compressSize'], data['compressScore'], dataConstantDifficulty['compressSize'], dataConstantDifficulty['compressScore']]
curves = []
for curvesIndex, (axis, color, label, yValues) in enumerate(zip(axes, axesColors, axesLabels, axesYValues)):
dashes = (None, None) if curvesIndex != 4 else [1, 25]
alpha = 1 if curvesIndex != 3 else 0.5
curves += axis.plot(yValues, label = label, color = color, dashes = dashes, alpha = alpha)
targetsAxis.set_yscale('log')
axes[2].spines.right.set_position(('axes', 1.1))
axes[2].set_yscale('log')
axes[-2].spines.right.set_position(('axes', 1.2))
legendNewLine = targetsAxis.plot([], [], color='none', label=' ')
curves.insert(-2, legendNewLine[0])
curves += legendNewLine + targetRecomputationCurve + [firstTargetDecreaseCurve] + [targetIncreaseCurve]
labels = [curve.get_label() for curve in curves]
plt.legend(curves, labels, loc='upper center', framealpha=1, bbox_to_anchor=(0.5, -0.125))
targetsAxis.set_xlabel('Block height')
for ax, color in zip(axes, axesColors):
ax.tick_params(axis='y', colors=color)
plt.legend()
plt.title('Variable difficulty Bitcoin compressed blockchain evolution')
plt.show()
fig.set_size_inches((11.2, 7.5), forward=False)
#plt.savefig('data.svg')
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment