Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checking faucet balance before sending transactions #21

Merged
merged 1 commit into from
Feb 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,9 @@ Required environment variables:
3. `MASTER_ACCOUNT_PRIVATE_KEY` - account's private key to send testing token from.
4. `STABLE_REMME_TOKENS_REQUEST_AMOUNT` - amount of the Remme tokens to send from master account.
5. `NODE_HOST` - node, Telegram bot should make requests, host (`i.e. node-genesis-testnet.remme.io`).
6. `NODE_PUBLIC_KEY` - node, Telegram bot should make requests, public key.
7. `STORAGE_PUBLIC_KEY` - storage, Telegram bot should make requests, public key.
8. `PRODUCTION_HOST` - if you run Telegram bot on production, set host (i.e. `https://intense-harbor-47746.herokuapp.com`)
9. `DATABASE_URL` - production database DSN URL to store information about users.
10. `REQUEST_TOKENS_PERIOD_IN_HOURS_LIMIT` - request tokens period in hours limit.
6. `PRODUCTION_HOST` - if you run Telegram bot on production, set host (i.e. `https://intense-harbor-47746.herokuapp.com`)
7. `DATABASE_URL` - production database DSN URL to store information about users.
8. `REQUEST_TOKENS_PERIOD_IN_HOURS_LIMIT` - request tokens period in hours limit.

To get node and storage public keys, visit [RPC API](https://remmeio.atlassian.net/wiki/spaces/WikiREMME/pages/292814862/RPC+API+specification) of node.

Expand Down
17 changes: 13 additions & 4 deletions gimmeremmetokensbot/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from constants import (
ALREADY_GOTTEN_ACCOUNT_CREDENTIALS_PHRASE,
CHECK_MY_BALANCE_KEYBOARD_BUTTON,
FAUCET_IS_EMPTY_PHRASE,
REQUEST_TOKENS_KEYBOARD_BUTTON,
SOMETHING_WENT_WRONG_PHRASE,
START_COMMAND_BOT_GREETING_PHRASE,
Expand All @@ -27,6 +28,7 @@
update_request_tokens_datetime,
)
from remme.account import RemmeAccount
from remme.constants.amount import STABLE_REMME_TOKENS_REQUEST_AMOUNT
from remme.token import RemmeToken
from utils import send_keystore_file

Expand All @@ -51,7 +53,7 @@ def render_keyboard(message):
bot.send_message(message.from_user.id, 'Choose one of the the following keyboard buttons:', reply_markup=keyboard)


def is_request_tokens_possible(public_key, request_tokens_datetime):
def is_request_tokens_possible(message, public_key):
"""
Check if last user's tokens request is fit to set period.

Expand All @@ -64,6 +66,8 @@ def is_request_tokens_possible(public_key, request_tokens_datetime):
4. If user's tokens request time in seconds is not more that periodic limit time in seconds, return False.
5. Else, return True.
"""
request_tokens_datetime = get_request_tokens_datetime(message.chat.id)

if request_tokens_datetime is not None:
request_tokens_period_in_seconds_limit = REQUEST_TOKENS_PERIOD_IN_HOURS_LIMIT * 60 * 60

Expand All @@ -89,16 +93,21 @@ def handle_gimme_tokens_button(message):
try:
public_key = get_public_key(chat_id=message.chat.id)

request_tokens_datetime = get_request_tokens_datetime(message.chat.id)
master_account = RemmeAccount(private_key_hex=MASTER_ACCOUNT_PRIVATE_KEY)
master_account_token = RemmeToken(private_key_hex=MASTER_ACCOUNT_PRIVATE_KEY)

if master_account_token.get_balance(address=master_account.address) < STABLE_REMME_TOKENS_REQUEST_AMOUNT:
bot.send_message(message.chat.id, FAUCET_IS_EMPTY_PHRASE)
return

if not is_request_tokens_possible(public_key=public_key, request_tokens_datetime=request_tokens_datetime):
if not is_request_tokens_possible(message=message, public_key=public_key):
bot.send_message(
message.chat.id,
f'You are able to request tokens only once per {REQUEST_TOKENS_PERIOD_IN_HOURS_LIMIT} hours.',
)
return

batch_id = RemmeToken(private_key_hex=MASTER_ACCOUNT_PRIVATE_KEY).send_transaction(public_key_to=public_key)
batch_id = master_account_token.send_transaction(public_key_to=public_key)
update_request_tokens_datetime(chat_id=message.chat.id)

bot.send_message(
Expand Down
7 changes: 5 additions & 2 deletions gimmeremmetokensbot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

START_COMMAND_BOT_TESTNET_INTERACTIONS_PHRASE = """
\n[Blockexplorer](https://blockexplorer.remme.io) can be useful to check sent transactions, including yours!
Broaden horizon - [use libraries written in several programming languages](https://docs.remme.io/) to work with blockchain in many different ways!
Broaden horizon [use libraries written in several programming languages](https://docs.remme.io/) to work with blockchain in many different ways!

If you are more a user than a developer, take a look at our [certificate-based authentication](https://webauth-testnet.remme.io/how-to-use) called *WebAuth*.
It allows you to log in without the password. Use keystore file generated especially for you.
Expand All @@ -25,4 +25,7 @@
'You already got the credentials. Find it at the start of this dialog.'

SOMETHING_WENT_WRONG_PHRASE = \
'Something went wrong! Please, contact administrator — @SergYelagin.'
'Something went wrong! Please, contact administrator — @dmytrostriletskyi.'

FAUCET_IS_EMPTY_PHRASE = \
'Faucet is empty. Please, contact administrator to top up its tokens — @dmytrostriletskyi.'
53 changes: 31 additions & 22 deletions gimmeremmetokensbot/remme/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,31 @@
from sawtooth_sdk.protobuf.transaction_pb2 import TransactionHeader, Transaction


class RequestToNode:

@staticmethod
def send(method, params=None):
if params is None:
params = {}

parameters = {
'jsonrpc': '2.0',
'id': '11',
'method': method,
'params': params,
}

return requests.post(
'https://' + os.environ.get('NODE_HOST'),
data=json.dumps(parameters),
verify=False
)


class TransactionService:

def __init__(self, private_key_hex):
self.request_to_node = RequestToNode()
self._remme_account = RemmeAccount(private_key_hex)

def create(self, family_name, family_version, inputs, outputs, payload_bytes):
Expand All @@ -43,28 +65,29 @@ def create(self, family_name, family_version, inputs, outputs, payload_bytes):
:param payload_bytes: {bytes}
:return: {Couroutine}
"""
config = {
"node_public_key": os.environ.get('NODE_PUBLIC_KEY'),
"storage_public_key": os.environ.get('STORAGE_PUBLIC_KEY')
}
node_config = self.request_to_node.send(method='get_node_config')
node_public_key = node_config.json().get('result').get('node_public_key')

txn_header_bytes = TransactionHeader(
family_name=family_name,
family_version=family_version,
inputs=inputs + [self._remme_account.address],
outputs=outputs + [self._remme_account.address],
signer_public_key=self._remme_account.public_key_hex,
batcher_public_key=config.get('node_public_key'),
batcher_public_key=node_public_key,
nonce=create_nonce(),
dependencies=[],
payload_sha512=sha512_hexdigest(payload_bytes)
).SerializeToString()

signature = self._remme_account.sign(txn_header_bytes)

txn = Transaction(
header=txn_header_bytes,
header_signature=signature,
payload=payload_bytes
).SerializeToString()

return b64encode(txn).decode('utf-8')


Expand All @@ -73,23 +96,9 @@ class RemmeToken:
_family_version = "0.1"

def __init__(self, private_key_hex=None):
self.request_to_node = RequestToNode()
self.transaction_service = TransactionService(private_key_hex=private_key_hex)

@staticmethod
def _send_request(method, params):
parameters = {
'jsonrpc': '2.0',
'id': '11',
'method': method,
'params': params,
}

return requests.post(
'https://' + os.environ.get('NODE_HOST'),
data=json.dumps(parameters),
verify=False
)

@staticmethod
def _validate_public_key(key):
if len(key) != 66:
Expand All @@ -98,7 +107,7 @@ def _validate_public_key(key):

def get_balance(self, address):

balance_info = self._send_request('get_balance', {
balance_info = self.request_to_node.send(method='get_balance', params={
'public_key_address': address,
})

Expand All @@ -108,7 +117,7 @@ def send_transaction(self, public_key_to, amount=STABLE_REMME_TOKENS_REQUEST_AMO
"""
Send raw transaction to Remme node.
"""
sent_transaction_info = self._send_request('send_raw_transaction', {
sent_transaction_info = self.request_to_node.send(method='send_raw_transaction', params={
'data': self._create_transaction(public_key_to, amount),
})

Expand Down