Skip to content
Snippets Groups Projects
Commit 5d6cde4b authored by Moul's avatar Moul
Browse files

[feat] #262: Add 'verify' command to check blocks’ signatures

- Based on DuniterPy 0.56.0 which implements block signature
  verification
- #183: Click progress bar
- Handle BMA anti-spam protection whith an exception
- Possibility to pass starting and ending block numbers to verify them
- Default to 0,0(head) for the full blockchain
parent 6d5fe97f
No related branches found
No related tags found
No related merge requests found
"""
Copyright 2016-2020 Maël Azimi <m.a@moul.re>
Silkaj is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Silkaj is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Silkaj. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
from asyncio import sleep
from click import command, argument, INT, progressbar
from aiohttp.client_exceptions import ServerDisconnectedError
from duniterpy.api import bma
from duniterpy.api.client import Client
from duniterpy.api.errors import DuniterError
from duniterpy.documents import Block
from duniterpy.key.verifying_key import VerifyingKey
from silkaj.tools import message_exit, coroutine
from silkaj.network_tools import EndPoint, HeadBlock
from silkaj.constants import BMA_MAX_BLOCKS_CHUNK_SIZE
@command("verify", help="Verify blocks’ signatures. Range defaults to 0, 0 (head)")
@argument("from_", default=0, type=INT)
@argument("to", default=0, type=INT)
@coroutine
async def verify_blocks_signatures(from_, to):
client = Client(EndPoint().BMA_ENDPOINT)
if to == 0:
to = (await HeadBlock().head_block)["number"]
if from_ > to:
message_exit("FROM should be lower than TO")
invalid_blocks_signature = list()
chunks_from = range(from_, to + 1, BMA_MAX_BLOCKS_CHUNK_SIZE)
with progressbar(chunks_from, label="Processing blocks verification") as bar:
for chunk_from in bar:
chunk_size = get_chunk_size(from_, to, chunks_from, chunk_from)
logging.info(
"Processing chunk from block {} to {}".format(
chunk_from, chunk_from + chunk_size
)
)
chunk = await get_chunk(client, chunk_size, chunk_from)
for block in chunk:
block = Block.from_signed_raw(block["raw"] + block["signature"] + "\n")
verify_block_signature(invalid_blocks_signature, block)
await client.close()
display_result(from_, to, invalid_blocks_signature)
def get_chunk_size(from_, to, chunks_from, chunk_from):
"""If not last chunk, take the maximum size
Otherwise, calculate the size for the last chunk"""
if chunk_from != chunks_from[-1]:
return BMA_MAX_BLOCKS_CHUNK_SIZE
else:
return (to + 1 - from_) % BMA_MAX_BLOCKS_CHUNK_SIZE
async def get_chunk(client, chunk_size, chunk_from):
try:
return await client(bma.blockchain.blocks, chunk_size, chunk_from)
except ServerDisconnectedError:
logging.info("Reach BMA anti-spam protection. Waiting two seconds")
await sleep(2)
return await client(bma.blockchain.blocks, chunk_size, chunk_from)
except DuniterError as error:
logging.error(error)
def verify_block_signature(invalid_blocks_signature, block):
key = VerifyingKey(block.issuer)
if not key.verify_document(block):
invalid_blocks_signature.append(block.number)
def display_result(from_, to, invalid_blocks_signature):
print("Within {0}-{1} range, ".format(from_, to), end="")
if invalid_blocks_signature:
print("blocks with a wrong signature:", *invalid_blocks_signature)
else:
print("no blocks with a wrong signature.")
...@@ -33,6 +33,7 @@ from silkaj.commands import ( ...@@ -33,6 +33,7 @@ from silkaj.commands import (
from silkaj.wot import received_sent_certifications, id_pubkey_correspondence from silkaj.wot import received_sent_certifications, id_pubkey_correspondence
from silkaj.auth import generate_auth_file from silkaj.auth import generate_auth_file
from silkaj.license import license_command from silkaj.license import license_command
from silkaj.blocks import verify_blocks_signatures
from silkaj.constants import SILKAJ_VERSION from silkaj.constants import SILKAJ_VERSION
...@@ -95,6 +96,7 @@ cli.add_command(currency_info) ...@@ -95,6 +96,7 @@ cli.add_command(currency_info)
cli.add_command(license_command) cli.add_command(license_command)
cli.add_command(network_info) cli.add_command(network_info)
cli.add_command(send_transaction) cli.add_command(send_transaction)
cli.add_command(verify_blocks_signatures)
cli.add_command(received_sent_certifications) cli.add_command(received_sent_certifications)
......
...@@ -25,3 +25,4 @@ ASYNC_SLEEP = 0.1 ...@@ -25,3 +25,4 @@ ASYNC_SLEEP = 0.1
SOURCES_PER_TX = 40 SOURCES_PER_TX = 40
SUCCESS_EXIT_STATUS = 0 SUCCESS_EXIT_STATUS = 0
FAILURE_EXIT_STATUS = 1 FAILURE_EXIT_STATUS = 1
BMA_MAX_BLOCKS_CHUNK_SIZE = 5000
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment