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 Transaction feature #21

Merged
merged 3 commits into from
Mar 27, 2017
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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Silkaj is based on Python dependencies:

- [Tabulate](https://bitbucket.org/astanin/python-tabulate/overview): to display charts.
- [Commandlines](https://github.com/chrissimpkins/commandlines): to parse command and sub-commands.
- [PyNaCl](https://github.com/pyca/pynacl/): Cryptography (NaCl) library.
- [scrypt](https://bitbucket.org/mhallin/py-scrypt): scrypt key derivation function.

#### From pip
```bash
Expand All @@ -28,7 +30,7 @@ sudo (apt-get or dnf) install python-tabulate python-ipaddress
```

### Integrate it on a dropdown menu to the panel
Under GNOME Shell, with [Argos](https://github.com/p-e-w/argos) extension:
Under GNOME Shell, with [Argos](https://github.com/p-e-w/argos) extension:

- [Install Argos](https://github.com/p-e-w/argos#installation)
- Put inside `~/.config/argos/silkaj.30s.sh`:
Expand Down Expand Up @@ -60,6 +62,8 @@ Argos should the script and reload it every 30 seconds.

## Features
- Currency information
- Show amount of account
- Send transaction
- Proof-of-Work difficulties: Σ diffi, Π diffi, match
- network information tab:
- endpoints, ip6, ip4, domain name and port. Handle nodes sharing same pubkey.
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
commandlines
ipaddress
tabulate
pynacl
scrypt
64 changes: 64 additions & 0 deletions src/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from tools import *
import getpass
import os

def auth_method(c):
if c.contains_switches('auth-scrypt'):
return auth_by_scrypt()
if c.contains_switches('auth-seed'):
return auth_by_seed()
if c.contains_switches('auth-file'):
return auth_by_auth_file(c)
print("Error: no authentication method")
exit()

def generate_auth_file(c):
if c.contains_definitions('file'):
file = c.get_definition('file')
else:
file = "authfile"
seed = auth_method(c)
with open(file, "w") as f:
f.write(seed)
print("Authfile generated for the public key: ", get_publickey_from_seed(seed))

def auth_by_auth_file(c):
if c.contains_definitions('file'):
file = c.get_definition('file')
else:
file = "authfile"
if not os.path.isfile(file):
print("Error: the file \"" + file + "\" does not exist")
exit()
with open(file) as f:
seed = f.read()
regex = re.compile('^[0-9a-fA-F]{64}$')
if not re.search(regex, seed):
print("Error: the format of the file is invalid")
exit()
return seed

def auth_by_seed():
seed = input("Please enter your seed on hex format: ")
regex = re.compile('^[0-9a-fA-F]{64}$')
if not re.search(regex, seed):
print("Error: the format of the seed is invalid")
exit()
return seed


def auth_by_scrypt():
salt = input("Please enter your Scrypt Salt (Secret identifier): ")
password = getpass.getpass("Please enter your Scrypt password (masked): ")
scrypt_param = input("Please enter your Scrypt parameters (N,r,p): [4096,16,1]")
if not scrypt_param:
scrypt_param = "4096,16,1"
scrypt_param_splited= scrypt_param.split(",")
n = int(scrypt_param_splited[0])
r = int(scrypt_param_splited[1])
p = int(scrypt_param_splited[2])
if (n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32):
print("Error: the values of Scrypt parameters are not good")

seed = get_seed_from_scrypt(salt, password, n, r, p)
return seed
78 changes: 78 additions & 0 deletions src/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from operator import itemgetter

from network_tools import *
from tx import *
from auth import *
from tools import *

def currency_info(ep):
Expand Down Expand Up @@ -165,6 +167,82 @@ def list_issuers(ep, nbr, last):
print("from {0} issuers\n{1}".format(len(list_issued),
tabulate(sorted_list, headers="keys", tablefmt="orgtbl", floatfmt=".1f", stralign="center")))


def cmd_amount(ep, c):
if c.contains_definitions('pubkey'):
pubkey = c.get_definition('pubkey')
pubkey = check_public_key(pubkey)
else:
seed = auth_method(c)
pubkey = get_publickey_from_seed(seed)

show_amount_from_pubkey(ep, pubkey)


def cmd_transaction(ep, c):
seed = auth_method(c)

if not (c.contains_definitions('amount') or c.contains_definitions('amountDU')):
print("--amount or --amountDU is not set")
exit()
if not c.contains_definitions('output'):
print("--output is not set")
exit()

if c.contains_definitions('amount'):
amount = int(float(c.get_definition('amount')) * 100)
if c.contains_definitions('amountDU'):
du = get_last_du_value(ep)
amount = int(float(c.get_definition('amountDU')) * du)

output = c.get_definition('output')

if c.contains_definitions('comment'):
comment = c.get_definition('comment')
else:
comment = ""

if c.contains_switches('allSources'):
allSources = True
else:
allSources = False

if c.contains_definitions('outputBackChange'):
outputBackChange = c.get_definition('outputBackChange')
else:
outputBackChange = None

generate_and_send_transaction(ep, seed, amount, output, comment, allSources, outputBackChange)


def show_amount_from_pubkey(ep, pubkey):

value = get_amount_from_pubkey(ep, pubkey)
totalAmountInput = value[0]
amount = value[1]
#output
DUvalue = get_last_du_value(ep)
current_blk = get_current_block(ep)
currency_name = str(current_blk["currency"])


if totalAmountInput-amount != 0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spaces around -.

print("Blockchain:")
print("-----------")
print("Relative =", round(amount / DUvalue, 2), "DU", currency_name)
print("Quantitative =", round(amount / 100, 2), currency_name+"\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spaces around +.


print("Pending Transaction:")
print("--------------------")
print("Relative =", round((totalAmountInput - amount) / DUvalue, 2), "DU", currency_name)
print("Quantitative =", round((totalAmountInput - amount) / 100, 2), currency_name + "\n")


print("Total amount of: " + pubkey)
print("----------------------------------------------------------------")
print("Total Relative =", round(totalAmountInput / DUvalue, 2), "DU", currency_name)
print("Total Quantitative =", round(totalAmountInput / 100, 2), currency_name + "\n")

def argos_info(ep):
info_type = ["newcomers", "certs", "actives", "leavers", "excluded", "ud", "tx"]
pretty_names = {'g1': 'Ğ1', 'gtest': 'Ğtest'}
Expand Down
15 changes: 15 additions & 0 deletions src/network_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ def request(ep, path):
encoding = response.info().get_content_charset('utf8')
return (json.loads(response.read().decode(encoding)))

def post_request(ep, path, postdata):
address = best_node(ep, 0)
if address is None: return (address)
url = "http://" + ep[address] + ":" + ep["port"] + "/" + path
if ep["port"] == "443":
url = "https://" + ep[address] + "/" + path
request = urllib.request.Request(url,bytes(postdata, 'utf-8'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after ,.

try:
response = urllib.request.urlopen(request)
except urllib.error.URLError as e:
print(e)
exit()
encoding = response.info().get_content_charset('utf8')
return (json.loads(response.read().decode(encoding)))

def best_node(ep, main):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
addresses, port = {"ip6", "ip4", "domain"}, int(ep["port"])
Expand Down
74 changes: 56 additions & 18 deletions src/silkaj.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,89 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys

from commandlines import Command

from commands import *


def usage():
print("Silkaj: command line Duniter client \
\n\nhelp: -h, --help, --usage \
\nversion: -v, --version \
\n \
\nCustom endpoint with option `-p` and <domain>:<port>\
\n \
\nCommands: \
\n - info: Display information about currency \
\n \
\n - amount: Get amount of one account \
\n --pubkey=<pubkey[:checksum]> | --auth-scrypt | --auth-seed | --auth-file\
\n \
\n - transaction: Send transaction\
\n --auth-scrypt | --auth-seed | --auth-file [--file=<path file>]\
\n --amountDU=<relative value> | --amount=<quantitative value>\
\n --output=<public key>[:checksum] \
\n [--comment=<comment>] \
\n [--allSources] \
\n [--outputBackChange=<public key[:checksum]>] \
\n \
\n - network: Display current network with many information \
\n --discover Discover all network (could take a while) \
\n \
\n - diffi: list proof-of-work difficulty to generate next block \
\n \
\n - issuers n: display last n issuers (`0` for all blockchain) \
\n last issuers are displayed under n <= 30. To force display last ones, use `--last` option\
\n \
\n - argos: display information about currency formated for Argos or BitBar \
\n \
\n - generate_auth_file : Generate file to store the seed of the account\
\n --auth-scrypt | --auth-seed \
\n [--file=<path file>]")
exit()


def cli():
# ep: endpoint, node's network interface
ep, c = dict(), Command()
subcmd = ["info", "diffi", "network", "issuers", "argos"]
subcmd = ["info", "diffi", "network", "issuers", "argos", "amount", "transaction", "generate_auth_file"]
if c.is_help_request() or c.is_usage_request() or c.subcmd not in subcmd: usage(); exit()
if c.is_version_request(): print("silkaj 0.1.0"); exit()
ep["domain"], ep["port"] = "duniter.org", "10901"
try: ep["domain"], ep["port"] = c.get_definition('p').rsplit(':', 1)
except:
print("Fallback to default node {}:{}\nCause: no specifed node, node not reachable or parsing issue."
print("Fallback to default node: <{}:{}>"
.format(ep["domain"], ep["port"]), file=sys.stderr)
if ep["domain"].startswith('[') and ep["domain"].endswith(']'): ep["domain"] = ep["domain"][1:-1]
return (ep, c)


def manage_cmd(ep, c):
if c.subcmd == "info":
currency_info(ep)

elif c.subcmd == "diffi":
difficulties(ep)

elif c.subcmd == "network":
network_info(ep, c.contains_switches("discover"))

elif c.subcmd == "issuers" and c.subsubcmd and int(c.subsubcmd) >= 0:
list_issuers(ep, int(c.subsubcmd), c.contains_switches('last'))

elif c.subcmd == "argos":
argos_info(ep)
argos_info(ep)

elif c.subcmd == "amount" and c.subsubcmd:
cmd_amount(ep, c)

elif c.subcmd == "transaction":
cmd_transaction(ep, c)

elif c.subcmd == "generate_auth_file":
generate_auth_file(c)

def usage():
print("Silkaj: command line Duniter client \
\n\nhelp: -h, --help, --usage \
\nversion: -v, --version \
\ncommands: \
\n - info: display information about currency \
\n - diffi: list proof-of-work difficulty to generate next block \
\n - network: display current network with many information \
\n - `--discover` option to discover all network (could take a while) \
\n - issuers n: display last n issuers (`0` for all blockchain) \
\n - last issuers are displayed under n ≤ 30. To force display last ones, use `--last` option \
\n - argos: display information about currency formated for Argos or BitBar \
\ncustom endpoint with option `-p` and <domain>:<port>")
exit()

if __name__ == '__main__':
ep, c = cli()
Expand Down
Loading