Skip to content
Snippets Groups Projects
Commit 4a8a9900 authored by inso's avatar inso
Browse files

Saving and loading data + latest ucoinpy API

parent 4a7d53ac
No related branches found
No related tags found
No related merge requests found
Showing
with 2056 additions and 3 deletions
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
#
PROTOCOL_VERSION="1"
MANAGED_API=["BASIC_MERKLED_API"]
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
#
__all__ = ['api']
__author__ = 'Caner Candan'
__version__ = '0.10.0'
__nonsense__ = 'uCoin'
import requests, logging, json
# import pylibscrypt
logger = logging.getLogger("ucoin")
class ConnectionHandler(object):
"""Helper class used by other API classes to ease passing server connection information."""
def __init__(self, server, port):
"""
Arguments:
- `server`: server hostname
- `port`: port number
"""
self.server = server
self.port = port
def __str__(self):
return 'connection info: %s:%d' % (self.server, self.port)
class API(object):
"""APIRequest is a class used as an interface. The intermediate derivated classes are the modules and the leaf classes are the API requests."""
def __init__(self, connection_handler, module):
"""
Asks a module in order to create the url used then by derivated classes.
Arguments:
- `module`: module name
- `connection_handler`: connection handler
"""
self.module = module
self.connection_handler = connection_handler
self.headers = {}
def reverse_url(self, path):
"""
Reverses the url using self.url and path given in parameter.
Arguments:
- `path`: the request path
"""
server, port = self.connection_handler.server, self.connection_handler.port
url = 'http://%s:%d/%s' % (server, port, self.module)
return url + path
def get(self, **kwargs):
"""wrapper of overloaded __get__ method."""
return self.__get__(**kwargs)
def post(self, **kwargs):
"""wrapper of overloaded __post__ method."""
logger.debug('do some work with')
data = self.__post__(**kwargs)
logger.debug('and send back')
return data
def __get__(self, **kwargs):
"""interface purpose for GET request"""
pass
def __post__(self, **kwargs):
"""interface purpose for POST request"""
pass
def requests_get(self, path, **kwargs):
"""
Requests GET wrapper in order to use API parameters.
Arguments:
- `path`: the request path
"""
response = requests.get(self.reverse_url(path), params=kwargs, headers=self.headers)
if response.status_code != 200:
raise ValueError('status code != 200 => %d (%s)' % (response.status_code, response.text))
return response
def requests_post(self, path, **kwargs):
"""
Requests POST wrapper in order to use API parameters.
Arguments:
- `path`: the request path
"""
response = requests.post(self.reverse_url(path), data=kwargs, headers=self.headers)
if response.status_code != 200:
raise ValueError('status code != 200 => %d (%s)' % (response.status_code, response.text))
return response
def merkle_easy_parser(self, path, begin=None, end=None):
root = self.requests_get(path, leaves='true').json()
for leaf in root['leaves'][begin:end]:
yield self.requests_get(path, leaf=leaf).json()['leaf']
from . import network, blockchain, tx, wot
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
#
from .. import API, logging
logger = logging.getLogger("ucoin/blockchain")
class Blockchain(API):
def __init__(self, connection_handler, module='blockchain'):
super(Blockchain, self).__init__(connection_handler, module)
class Parameters(Blockchain):
"""GET the blockchain parameters used by this node."""
def __get__(self, **kwargs):
return self.requests_get('/parameters', **kwargs).json()
class Membership(Blockchain):
"""POST a Membership document."""
def __post__(self, **kwargs):
assert 'membership' in kwargs
return self.requests_post('/membership', **kwargs).json()
class Block(Blockchain):
"""GET/POST a block from/to the blockchain."""
def __init__(self, connection_handler, number=None):
"""
Use the number parameter in order to select a block number.
Arguments:
- `number`: block number to select
"""
super(Block, self).__init__(connection_handler)
self.number = number
def __get__(self, **kwargs):
assert self.number is not None
return self.requests_get('/block/%d' % self.number, **kwargs).json()
def __post__(self, **kwargs):
assert 'block' in kwargs
assert 'signature' in kwargs
return self.requests_post('/block', **kwargs).json()
class Current(Blockchain):
"""GET, same as block/[number], but return last accepted block."""
def __get__(self, **kwargs):
return self.requests_get('/current', **kwargs).json()
class Hardship(Blockchain):
"""GET hardship level for given member's fingerprint for writing next block."""
def __init__(self, connection_handler, fingerprint):
"""
Use the number parameter in order to select a block number.
Arguments:
- `fingerprint`: member fingerprint
"""
super(Hardship, self).__init__(connection_handler)
self.fingerprint = fingerprint
def __get__(self, **kwargs):
assert self.fingerprint is not None
return self.requests_get('/hardship/%s' % self.fingerprint.upper(), **kwargs).json()
class Newcomers(Blockchain):
"""GET, return block numbers containing newcomers."""
def __get__(self, **kwargs):
return self.requests_get('/with/newcomers', **kwargs).json()
class Certifications(Blockchain):
"""GET, return block numbers containing certifications."""
def __get__(self, **kwargs):
return self.requests_get('/with/certs', **kwargs).json()
class Joiners(Blockchain):
"""GET, return block numbers containing joiners."""
def __get__(self, **kwargs):
return self.requests_get('/with/joiners', **kwargs).json()
class Actives(Blockchain):
"""GET, return block numbers containing actives."""
def __get__(self, **kwargs):
return self.requests_get('/with/actives', **kwargs).json()
class Leavers(Blockchain):
"""GET, return block numbers containing leavers."""
def __get__(self, **kwargs):
return self.requests_get('/with/leavers', **kwargs).json()
class Excluded(Blockchain):
"""GET, return block numbers containing excluded."""
def __get__(self, **kwargs):
return self.requests_get('/with/excluded', **kwargs).json()
class UD(Blockchain):
"""GET, return block numbers containing universal dividend."""
def __get__(self, **kwargs):
return self.requests_get('/with/ud', **kwargs).json()
class TX(Blockchain):
"""GET, return block numbers containing transactions."""
def __get__(self, **kwargs):
return self.requests_get('/with/tx', **kwargs).json()
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
#
from .. import API, logging
logger = logging.getLogger("ucoin/network")
class Network(API):
def __init__(self, connection_handler, module='network'):
super(Network, self).__init__(connection_handler, module)
class Peering(Network):
"""GET peering information about a peer."""
def __get__(self, **kwargs):
return self.requests_get('/peering', **kwargs).json()
from . import peering
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
#
from .. import Network, logging
logger = logging.getLogger("ucoin/network/peering")
class Base(Network):
def __init__(self, connection_handler):
super(Base, self).__init__(connection_handler, 'network/peering')
class Peers(Base):
"""GET peering entries of every node inside the currency network."""
def __get__(self, **kwargs):
"""creates a generator with one peering entry per iteration."""
return self.merkle_easy_parser('/peers')
def __post__(self, **kwargs):
assert 'entry' in kwargs
assert 'signature' in kwargs
return self.requests_post('/peers', **kwargs).json()
class Status(Base):
"""POST a network status document to this node in order notify of its status."""
def __post__(self, **kwargs):
assert 'status' in kwargs
assert 'signature' in kwargs
return self.requests_post('/status', **kwargs).json()
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
#
from .. import API, logging
logger = logging.getLogger("ucoin/tx")
class Tx(API):
def __init__(self, connection_handler, module='tx'):
super(Tx, self).__init__(connection_handler, module)
class Process(Tx):
"""POST a transaction."""
def __post__(self, **kwargs):
assert 'transaction' in kwargs
assert 'signature' in kwargs
return self.requests_post('/process', **kwargs).json()
class Sources(Tx):
"""Get transaction sources."""
def __init__(self, connection_handler, pubkey, module='tx'):
super(Tx, self).__init__(connection_handler, module)
self.pubkey = pubkey
def __get__(self, **kwargs):
assert self.pubkey is not None
return self.requests_get('/sources/%s' % self.pubkey, **kwargs).json()
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
#
from .. import API, logging
logger = logging.getLogger("ucoin/wot")
class WOT(API):
def __init__(self, connection_handler, module='wot'):
super(WOT, self).__init__(connection_handler, module)
class Add(WOT):
"""POST Public key data."""
def __post__(self, **kwargs):
assert 'pubkey' in kwargs
assert 'self' in kwargs
assert 'other' in kwargs
return self.requests_post('/add', **kwargs).json()
class Lookup(WOT):
"""GET Public key data."""
def __init__(self, connection_handler, search, module='wot'):
super(WOT, self).__init__(connection_handler, module)
self.search = search
def __get__(self, **kwargs):
assert self.search is not None
return self.requests_get('/lookup/%s' % self.search, **kwargs).json()
class CertifiersOf(WOT):
"""GET Certification data over a member."""
def __init__(self, connection_handler, search, module='wot'):
super(WOT, self).__init__(connection_handler, module)
self.search = search
def __get__(self, **kwargs):
assert self.search is not None
return self.requests_get('/certifiers-of/%s' % self.search, **kwargs).json()
class CertifiedBy(WOT):
"""GET Certification data from a member."""
def __init__(self, connection_handler, search, module='wot'):
super(WOT, self).__init__(connection_handler, module)
self.search = search
def __get__(self, **kwargs):
assert self.search is not None
return self.requests_get('/certified-by/%s' % self.search, **kwargs).json()
class Members(WOT):
"""GET List all current members of the Web of Trust."""
def __init__(self, connection_handler, module='wot'):
super(WOT, self).__init__(connection_handler, module)
def __get__(self, **kwargs):
return self.requests_get('/members', **kwargs).json()
'''
Created on 3 déc. 2014
@author: inso
'''
import base58
import re
from ..key import Base58Encoder
from nacl.encoding import Base64Encoder
class Document:
re_version = re.compile("Version: ([0-9]+)\n")
re_currency = re.compile("Currency: ([^\n]+)\n")
re_signature = re.compile("([A-Za-z0-9+/]+(?:=|==)?)\n")
def __init__(self, version, currency, signatures):
self.version = version
self.currency = currency
self.signatures = signatures
def sign(self, keys):
'''
Sign the current document.
Warning : current signatures will be replaced with the new ones.
'''
self.signatures = []
for k in keys:
self.signatures.append(k.sign(self.raw(), Base64Encoder))
def signed_raw(self):
'''
If keys are None, returns the raw + current signatures
If keys are present, returns the raw signed by these keys
'''
raw = self.raw()
signed_raw = raw
for s in self.signatures:
if s is not None:
signed_raw += s + "\n"
return signed_raw
'''
Created on 2 déc. 2014
@author: inso
'''
from .. import PROTOCOL_VERSION
from . import Document
from .certification import SelfCertification, Certification
from .membership import Membership
from .transaction import Transaction
import re
class Block(Document):
'''
Version: VERSION
Type: Block
Currency: CURRENCY
Nonce: NONCE
Number: BLOCK_NUMBER
PoWMin: NUMBER_OF_ZEROS
Time: GENERATED_ON
MedianTime: MEDIAN_DATE
UniversalDividend: DIVIDEND_AMOUNT
Issuer: ISSUER_KEY
PreviousHash: PREVIOUS_HASH
PreviousIssuer: PREVIOUS_ISSUER_KEY
Parameters: PARAMETERS
MembersCount: WOT_MEM_COUNT
Identities:
PUBLIC_KEY:SIGNATURE:TIMESTAMP:USER_ID
...
Joiners:
PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
...
Actives:
PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
...
Leavers:
PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
...
Excluded:
PUBLIC_KEY
...
Certifications:
PUBKEY_FROM:PUBKEY_TO:BLOCK_NUMBER:SIGNATURE
...
Transactions:
COMPACT_TRANSACTION
...
BOTTOM_SIGNATURE
'''
re_type = re.compile("Type: (Block)\n")
re_noonce = re.compile("Nonce: ([0-9]+)\n")
re_number = re.compile("Number: ([0-9]+)\n")
re_powmin = re.compile("PoWMin: ([0-9]+)\n")
re_time = re.compile("Time: ([0-9]+)\n")
re_mediantime = re.compile("MedianTime: ([0-9]+)\n")
re_universaldividend = re.compile("UniversalDividend: ([0-9]+)\n")
re_issuer = re.compile("Issuer: ([1-9A-Za-z][^OIl]{42,45})\n")
re_previoushash = re.compile("PreviousHash: ([0-9a-fA-F]{5,40})\n")
re_previousissuer = re.compile("PreviousIssuer: ([1-9A-Za-z][^OIl]{42,45})\n")
re_parameters = re.compile("Parameters: ([0-9]+\.[0-9]+):([0-9]+):([0-9]+):([0-9]+):\
([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):\
([0-9]+\.[0-9]+)\n")
re_memberscount = re.compile("MembersCount: ([0-9]+)\n")
re_identities = re.compile("Identities:\n")
re_joiners = re.compile("Joiners:\n")
re_actives = re.compile("Actives:\n")
re_leavers = re.compile("Leavers:\n")
re_excluded = re.compile("Excluded:\n")
re_certifications = re.compile("Certifications:\n")
re_transactions = re.compile("Transactions:\n")
def __init__(self, version, currency, noonce, number, powmin, time,
mediantime, ud, issuer, prev_hash, prev_issuer,
parameters, members_count, identities, joiners,
actives, leavers, excluded, certifications,
transactions, signature):
'''
Constructor
'''
if signature:
super().__init__(version, currency, [signature])
else:
super().__init__(version, currency, [])
self.noonce = noonce
self.number = number
self.powmin = powmin
self.time = time
self.mediantime = mediantime
self.ud = ud
self.issuer = issuer
self.prev_hash = prev_hash
self.prev_issuer = prev_issuer
self.parameters = parameters
self.members_count = members_count
self.identities = identities
self.joiners = joiners
self.actives = actives
self.leavers = leavers
self.excluded = excluded
self.certifications = certifications
self.transactions = transactions
@classmethod
def from_signed_raw(cls, raw, signature=None):
lines = raw.splitlines(True)
n = 0
version = int(Block.re_version.match(lines[n]).group(1))
n = n + 1
Block.re_type.match(lines[n]).group(1)
n = n + 1
currency = Block.re_currency.match(lines[n]).group(1)
n = n + 1
noonce = int(Block.re_noonce.match(lines[n]).group(1))
n = n + 1
number = int(Block.re_number.match(lines[n]).group(1))
n = n + 1
powmin = int(Block.re_powmin.match(lines[n]).group(1))
n = n + 1
time = int(Block.re_time.match(lines[n]).group(1))
n = n + 1
mediantime = int(Block.re_mediantime.match(lines[n]).group(1))
n = n + 1
ud = Block.re_universaldividend.match(lines[n])
if ud is not None:
ud = int(ud.group(1))
n = n + 1
issuer = Block.re_issuer.match(lines[n]).group(1)
n = n + 1
prev_hash = None
prev_issuer = None
if number > 0:
prev_hash = Block.re_previoushash.match(lines[n]).group(1)
n = n + 1
prev_issuer = Block.re_previousissuer.match(lines[n]).group(1)
n = n + 1
parameters = None
if number == 0:
parameters = Block.re_parameters.match(lines[n]).groups()
n = n + 1
members_count = int(Block.re_memberscount.match(lines[n]).group(1))
n = n + 1
identities = []
joiners = []
actives = []
leavers = []
excluded = []
certifications = []
transactions = []
if Block.re_identities.match(lines[n]) is not None:
n = n + 1
while Block.re_joiners.match(lines[n]) is None:
selfcert = SelfCertification.from_inline(version, currency, lines[n])
identities.append(selfcert)
n = n + 1
if Block.re_joiners.match(lines[n]):
n = n + 1
while Block.re_actives.match(lines[n]) is None:
membership = Membership.from_inline(version, currency, "IN", lines[n])
joiners.append(membership)
n = n + 1
if Block.re_actives.match(lines[n]):
n = n + 1
while Block.re_leavers.match(lines[n]) is None:
membership = Membership.from_inline(version, currency, "IN", lines[n])
actives.append(membership)
n = n + 1
if Block.re_leavers.match(lines[n]):
n = n + 1
while Block.re_excluded.match(lines[n]) is None:
membership = Membership.from_inline(version, currency, "OUT", lines[n])
leavers.append(membership)
n = n + 1
if Block.re_excluded.match(lines[n]):
n = n + 1
while Block.re_certifications.match(lines[n]) is None:
membership = Membership.from_inline(version, currency, "OUT", lines[n])
excluded.append(membership)
n = n + 1
if Block.re_certifications.match(lines[n]):
n = n + 1
while Block.re_transactions.match(lines[n]) is None:
certification = Certification.from_inline(version, currency,
prev_hash, lines[n])
certifications.append(certification)
n = n + 1
if Block.re_transactions.match(lines[n]):
n = n + 1
while not Block.re_signature.match(lines[n]):
transaction = Transaction.from_compact(version, lines[n])
transactions.append(transaction)
n = n + 1
signature = Block.re_signature.match(lines[n]).group(1)
return cls(version, currency, noonce, number, powmin, time,
mediantime, ud, issuer, prev_hash, prev_issuer,
parameters, members_count, identities, joiners,
actives, leavers, excluded, certifications,
transactions, signature)
def raw(self):
doc = """Version: {0}
Type: Block
Currency: {1}
Nonce: {2}
Number: {3}
PoWMin: {4}
Time: {5}
MedianTime: {6}
""".format(self.version,
self.currency,
self.noonce,
self.number,
self.powmin,
self.time,
self.mediantime)
if self.ud:
doc += "UniversalDividend: {0}\n".format(self.ud)
doc += "Issuer: {0}\n".format(self.issuer)
if self.number == 0:
str_params = ":".join(self.parameters)
doc += "Parameters: {0}\n".format(str_params)
else:
doc += "PreviousHash: {0}\n\
PreviousIssuer: {1}\n".format(self.prev_hash, self.prev_issuer)
doc += "MembersCount: {0}\n".format(self.members_count)
doc += "Identities:\n"
for identity in self.identities:
doc += "{0}\n".format(identity.inline())
doc += "Joiners:\n"
for joiner in self.joiners:
doc += "{0}\n".format(joiner.inline())
doc += "Actives:\n"
for active in self.actives:
doc += "{0}\n".format(active.inline())
doc += "Leavers:\n"
for leaver in self.leavers:
doc += "{0]\n".format(leaver.inline())
doc += "Excluded:\n"
for exclude in self.excluded:
doc += "{0}\n".format(exclude.inline())
doc += "Certifications:\n"
for cert in self.certifications:
doc += "{0}\n".format(cert.inline())
doc += "Transactions:\n"
for transaction in self.transactions:
doc += "{0}\n".format(transaction.inline())
return doc
'''
Created on 2 déc. 2014
@author: inso
'''
import re
from . import Document
class SelfCertification(Document):
'''
A document discribing a self certification.
'''
re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([A-Za-z0-9+/]+(?:=|==)?):([0-9]+):([^\n]+)\n")
re_uid = re.compile("UID:([^\n]+)\n")
re_timestamp = re.compile("META:TS:([0-9]+)\n")
def __init__(self, version, currency, pubkey, ts, uid, signature):
if signature:
super().__init__(version, currency, [signature])
else:
super().__init__(version, currency, [])
self.pubkey = pubkey
self.timestamp = ts
self.uid = uid
@classmethod
def from_inline(cls, version, currency, inline):
selfcert_data = SelfCertification.re_inline.match(inline)
pubkey = selfcert_data.group(1)
signature = selfcert_data.group(2)
ts = int(selfcert_data.group(3))
uid = selfcert_data.group(4)
return cls(version, currency, pubkey, ts, uid, signature)
def raw(self):
return """UID:{0}
META:TS:{1}""".format(self.uid(), self.ts())
def inline(self):
return "{0}:{1}:{2}:{3}".format(self.pubkey, self.signatures[0],
self.timestamp, self.uid)
class Certification(Document):
'''
A document describing a certification.
'''
re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):\
([1-9A-Za-z][^OIl]{42,45}):([0-9]+):([A-Za-z0-9+/]+(?:=|==)?)\n")
re_timestamp = re.compile("META:TS:([0-9]+)-([0-9a-fA-F]{5,40})\n")
def __init__(self, version, currency, pubkey_from, pubkey_to,
blockhash, blocknumber, signature):
'''
Constructor
'''
super().__init__(version, currency, [signature])
self.pubkey_from = pubkey_from
self.pubkey_to = pubkey_to
self.blockhash = blockhash
self.blocknumber = blocknumber
@classmethod
def from_inline(cls, version, currency, blockhash, inline):
cert_data = Certification.re_inline.match(inline)
pubkey_from = cert_data.group(1)
pubkey_to = cert_data.group(2)
blocknumber = int(cert_data.group(3))
if blocknumber == 0:
blockhash = "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"
signature = cert_data.group(4)
return cls(version, currency, pubkey_from, pubkey_to,
blockhash, blocknumber, signature)
def raw(self, selfcert):
return """{0}
META:TS:{1}-{2}""".format(selfcert.signed_raw(), self.blockhash, self.blocknumber)
def inline(self):
return "{0}:{1}:{2}:{3}".format(self.pubkey_from, self.pubkey_to,
self.blocknumber, self.signatures[0])
'''
Created on 2 déc. 2014
@author: inso
'''
from .. import PROTOCOL_VERSION
from . import Document
import re
class Membership(Document):
'''
This is a utility class to generate membership documents :
Version: VERSION
Type: Membership
Currency: CURRENCY_NAME
Issuer: ISSUER
Block: NUMBER-HASH
Membership: MEMBERSHIP_TYPE
UserID: USER_ID
CertTS: CERTIFICATION_TS
'''
# PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID
re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([A-Za-z0-9+/]+(?:=|==)?):\
([0-9]+):([0-9a-fA-F]{5,40}):([0-9]+):([^\n]+)\n")
re_type = re.compile("Type: (Membership)")
re_issuer = re.compile("Issuer: ([1-9A-Za-z][^OIl]{42,45})\n")
re_block = re.compile("Block: ([0-9]+)-([0-9a-fA-F]{5,40})\n")
re_membership_type = re.compile("Membership: (IN|OUT)")
re_userid = re.compile("UserID: ([^\n]+)\n")
re_certts = re.compile("CertTS: ([0-9]+)\n")
def __init__(self, version, currency, issuer, block_number, block_hash,
membership_type, uid, cert_ts, signature):
'''
Constructor
'''
if signature:
super().__init__(version, currency, [signature])
else:
super().__init__(version, currency, [])
self.issuer = issuer
self.block_number = block_number
self.block_hash = block_hash
self.membership_type = membership_type
self.uid = uid
self.cert_ts = cert_ts
@classmethod
def from_inline(cls, version, currency, membership_type, inline):
data = Membership.re_inline.match(inline)
issuer = data.group(1)
signature = data.group(2)
block_number = int(data.group(3))
block_hash = data.group(4)
cert_ts = int(data.group(5))
uid = data.group(6)
return cls(version, currency, issuer, block_number,
block_hash, membership_type, uid, cert_ts, signature)
@classmethod
def from_signed_raw(cls, raw, signature=None):
lines = raw.splitlines(True)
n = 0
version = int(Membership.re_version.match(lines[n]).group(1))
n = n + 1
Membership.re_type.match(lines[n]).group(1)
n = n + 1
currency = Membership.re_currency.match(lines[n]).group(1)
n = n + 1
issuer = Membership.re_issuer.match(lines[n]).group(1)
n = n + 1
blockid = Membership.re_block.match(lines[n])
blocknumber = int(blockid.group(1))
blockhash = blockid.group(2)
n = n + 1
membership_type = Membership.re_membership_type.match(lines[n]).group(1)
n = n + 1
uid = Membership.re_userid.match(lines[n]).group(1)
n = n + 1
cert_ts = int(Membership.re_certts.match(lines[n]).group(1))
n = n + 1
signature = Membership.re_signature.match(lines[n]).group(1)
n = n + 1
return cls(version, currency, issuer, blocknumber, blockhash,
membership_type, uid, cert_ts, signature)
def raw(self):
return """Version: {0}
Type: Membership
Currency: {1}
Issuer: {2}
Block: {3}-{4}
Membership: {5}
UserID: {6}
CertTS: {7}
""".format(self.version,
self.currency,
self.issuer,
self.block_number, self.block_hash,
self.membership_type,
self.uid,
self.cert_ts)
def inline(self):
return "{0}:{1}:{2}:{3}:{4}:{5}".format(self.issuer,
self.signatures[0],
self.block_number,
self.block_hash,
self.cert_ts,
self.uid)
'''
Created on 2 déc. 2014
@author: inso
'''
import re
from . import Document
from .. import PROTOCOL_VERSION, MANAGED_API
class Peer(Document):
"""
Version: VERSION
Type: Peer
Currency: CURRENCY_NAME
PublicKey: NODE_PUBLICKEY
Block: BLOCK
Endpoints:
END_POINT_1
END_POINT_2
END_POINT_3
[...]
"""
re_type = re.compile("Type: (Peer)")
re_pubkey = re.compile("PublicKey: ([1-9A-Za-z][^OIl]{42,45})\n")
re_block = re.compile("Block: ([0-9]+-[0-9a-fA-F]{5,40})\n")
re_endpoints = re.compile("Endpoints:\n")
def __init__(self, version, currency, pubkey, blockid,
endpoints, signature):
if signature:
super().__init__(version, currency, [signature])
else:
super().__init__(version, currency, [])
self.pubkey = pubkey
self.blockid = blockid
self.endpoints = endpoints
@classmethod
def from_signed_raw(cls, raw):
lines = raw.splitlines(True)
n = 0
version = int(Peer.re_version.match(lines[n]).group(1))
n = n + 1
Peer.re_type.match(lines[n]).group(1)
n = n + 1
currency = Peer.re_currency.match(lines[n]).group(1)
n = n + 1
pubkey = Peer.re_pubkey.match(lines[n]).group(1)
n = n + 1
blockid = Peer.re_block.match(lines[n]).group(1)
n = n + 1
Peer.re_endpoints.match(lines[n])
n = n + 1
endpoints = []
while not Peer.re_signature.match(lines[n]):
endpoint = Endpoint.from_inline(lines[n])
endpoints.append(endpoint)
n = n + 1
signature = Peer.re_signature.match(lines[n]).group(1)
return cls(version, currency, pubkey, blockid, endpoints, signature)
def raw(self):
doc = """Version: {0}
Type: Peer
Currency: {1}
PublicKey: {2}
Block: {3}
Endpoints:
""".format(self.version, self.currency, self.pubkey, self.blockid)
for endpoint in self.endpoints:
doc += "{0}\n".format(endpoint.inline())
doc += "{0}\n".format(self.signatures[0])
return doc
class Endpoint():
"""
Describing endpoints
"""
@staticmethod
def from_inline(inline):
for api in MANAGED_API:
if (inline.startswith(api)):
if (api == "BASIC_MERKLED_API"):
return BMAEndpoint.from_inline(inline)
return UnknownEndpoint.from_inline(inline)
class UnknownEndpoint(Endpoint):
def __init__(self, api, properties):
self.api = api
self.properties = properties
@classmethod
def from_inline(cls, inline):
api = inline.split()[0]
properties = inline.split()[1:]
return cls(api, properties)
def inline(self):
doc = self.api
for p in self.properties:
doc += " {0}".format(p)
return doc
class BMAEndpoint(Endpoint):
re_inline = re.compile('^BASIC_MERKLED_API(?: ([a-z_][a-z0-9-_.]+))?(?: ([0-9.]+))?(?: ([0-9a-f:]+))?(?: ([0-9]+))$')
@classmethod
def from_inline(cls, inline):
m = BMAEndpoint.re_inline.match(inline)
server = m.group(1)
ipv4 = m.group(2)
ipv6 = m.group(3)
port = int(m.group(4))
return cls(server, ipv4, ipv6, port)
def __init__(self, server, ipv4, ipv6, port):
self.server = server
self.ipv4 = ipv4
self.ipv6 = ipv6
self.port = port
def inline(self):
return "BASIC_MERKLED_API {DNS} {IPv4} {IPv6} {PORT}" \
.format(DNS=self.server,
IPv4=self.ipv4,
IPv6=self.ipv6,
PORT=self.port)
'''
Created on 2 déc. 2014
@author: inso
'''
import re
from . import Document
class Status(Document):
'''
Version: VERSION
Type: Status
Currency: CURRENCY_NAME
Status: STATUS
Block: BLOCK
From: SENDER
To: RECIPIENT
'''
re_type = re.compile("Type: (Status)")
re_status = re.compile("Status: (NEW|NEW_BACK|UP|UP_BACK|DOWN)")
re_block = re.compile("Block: ([0-9]+-[0-9a-fA-F]{5,40})\n")
re_from = re.compile("From: ([1-9A-Za-z][^OIl]{42,45})\n")
re_to = re.compile("To: ([1-9A-Za-z][^OIl]{42,45})\n")
def __init__(self, version, currency, status, blockid, sender,
recipient, signature):
'''
Constructor
'''
if signature:
super().__init__(version, currency, [signature])
else:
super().__init__(version, currency, [])
self.status = status
self.blockid = blockid
self.sender = sender
self.recipient = recipient
@classmethod
def from_signed_raw(cls, raw):
lines = raw.splitlines(True)
n = 0
version = int(Status.re_version.match(lines[n]).group(1))
n = n + 1
Status.re_type.match(lines[n]).group(1)
n = n + 1
currency = Status.re_currency.match(lines[n]).group(1)
n = n + 1
status = Status.re_status.match(lines[n]).group(1)
n = n + 1
blockid = Status.re_block.match(lines[n]).group(1)
n = n + 1
sender = Status.re_from.match(lines[n]).group(1)
n = n + 1
recipient = Status.re_to.match(lines[n]).group(1)
n = n + 1
signature = Status.re_signature.match(lines[n]).group(1)
n = n + 1
return cls(version, currency, status, blockid,
sender, recipient, signature)
def raw(self):
return '''Version: {0}
Type: Status
Currency: {1}
Status: {2}
Block: {3}
From: {4}
To: {5}
'''.format(self.version, self.currency, self.status,
self.blockid, self.sender, self.recipient)
'''
Created on 2 déc. 2014
@author: inso
'''
from . import Document
import re
class Transaction(Document):
'''
Document format :
Version: VERSION
Type: Transaction
Currency: CURRENCY_NAME
Issuers:
PUBLIC_KEY
...
Inputs:
INDEX:SOURCE:NUMBER:FINGERPRINT:AMOUNT
...
Outputs:
PUBLIC_KEY:AMOUNT
...
Comment: COMMENT
...
Compact format :
TX:VERSION:NB_ISSUERS:NB_INPUTS:NB_OUTPUTS:HAS_COMMENT
PUBLIC_KEY:INDEX
...
INDEX:SOURCE:FINGERPRINT:AMOUNT
...
PUBLIC_KEY:AMOUNT
...
COMMENT
SIGNATURE
...
'''
re_type = re.compile("Type: (Transaction)\n")
re_header = re.compile("TX:([0-9])+:([0-9])+:([0-9])+:([0-9])+:(0|1)\n")
re_issuers = re.compile("Issuers:\n")
re_inputs = re.compile("Inputs:\n")
re_outputs = re.compile("Outputs:\n")
re_compact_comment = re.compile("-----@@@-----([^\n]+)\n")
re_comment = re.compile("Comment:(?:)?([^\n]*)\n")
re_pubkey = re.compile("([1-9A-Za-z][^OIl]{42,45})\n")
def __init__(self, version, currency, issuers, inputs, outputs,
comment, signatures):
'''
Constructor
'''
if signatures:
super().__init__(version, currency, signatures)
else:
super().__init__(version, currency, [])
self.issuers = issuers
self.inputs = inputs
self.outputs = outputs
self.comment = comment
@classmethod
def from_compact(cls, currency, compact):
lines = compact.splitlines(True)
n = 0
header_data = Transaction.re_header.match(lines[n])
version = int(header_data.group(1))
issuers_num = int(header_data.group(2))
inputs_num = int(header_data.group(3))
outputs_num = int(header_data.group(4))
n = n + 1
issuers = []
inputs = []
outputs = []
signatures = []
for i in range(0, issuers_num):
issuer = Transaction.re_pubkey.match(lines[n]).group(1)
issuers.append(issuer)
n = n + 1
for i in range(0, inputs_num):
input_source = InputSource.from_inline(lines[n])
inputs.append(input_source)
n = n + 1
for i in range(0, outputs_num):
output_source = OutputSource.from_inline(lines[n])
outputs.append(output_source)
n = n + 1
comment = None
if Transaction.re_comment.match(lines[n]):
comment = Transaction.re_compact_comment.match(lines[n]).group(1)
n = n + 1
while n < len(lines):
signatures.append(Transaction.re_signature.match(lines[n]).group(1))
n = n + 1
return cls(version, currency, issuers, inputs, outputs, comment, signatures)
@classmethod
def from_signed_raw(cls, raw):
lines = raw.splitlines(True)
n = 0
version = int(Transaction.re_version.match(lines[n]).group(1))
n = n + 1
Transaction.re_type.match(lines[n]).group(1)
n = n + 1
currency = Transaction.re_currency.match(lines[n]).group(1)
n = n + 1
issuers = []
inputs = []
outputs = []
signatures = []
if Transaction.re_issuers.match(lines[n]):
n = n + 1
while Transaction.re_inputs.match(lines[n]) is None:
issuer = Transaction.re_pubkey.match(lines[n]).group(1)
issuers.append(issuer)
n = n + 1
if Transaction.re_inputs.match(lines[n]):
n = n + 1
while Transaction.re_outputs.match(lines[n]) is None:
input_source = InputSource.from_inline(lines[n])
inputs.append(input_source)
n = n + 1
if Transaction.re_outputs.match(lines[n]) is not None:
n = n + 1
while not Transaction.re_comment.match(lines[n]):
output = OutputSource.from_inline(lines[n])
outputs.append(output)
n = n + 1
comment = Transaction.re_comment.match(lines[n]).group(1)
n = n + 1
if Transaction.re_signature.match(lines[n]) is not None:
while n < len(lines):
sign = Transaction.re_signature.match(lines[n]).group(1)
signatures.append(sign)
n = n + 1
return cls(version, currency, issuers, inputs, outputs,
comment, signatures)
def raw(self):
doc = """Version: {0}
Type: Transaction
Currency: {1}
Issuers:
""".format(self.version,
self.currency)
for p in self.issuers:
doc += "{0}\n".format(p)
doc += "Inputs:\n"
for i in self.inputs:
doc += "{0}\n".format(i.inline())
doc += "Outputs:\n"
for o in self.outputs:
doc += "{0}\n".format(o.inline())
doc += "Comment: "
if self.comment:
doc += "{0}".format(self.comment)
doc += "\n"
for signature in self.signatures:
doc += "{0}\n".format(signature)
return doc
def compact(self):
'''
Return a transaction in its compact format.
'''
"""TX:VERSION:NB_ISSUERS:NB_INPUTS:NB_OUTPUTS:HAS_COMMENT
PUBLIC_KEY:INDEX
...
INDEX:SOURCE:FINGERPRINT:AMOUNT
...
PUBLIC_KEY:AMOUNT
...
COMMENT
"""
doc = "TX:{0}:{1}:{2}:{3}:{4}".format(self.version,
self.issuers.len,
self.inputs.len,
self.outputs.len,
'1' if self.Comment else '0')
for pubkey in self.issuers:
doc += "{0}\n".format(pubkey)
for i in self.inputs:
doc += "{0}\n".format(i.compact())
for o in self.outputs:
doc += "{0}\n".format(o.inline())
if self.comment:
doc += "-----@@@----- {0}\n".format(self.comment)
for s in self.signatures:
doc += "{0}\n".format(s)
return doc
class SimpleTransaction(Transaction):
'''
As transaction class, but for only one issuer.
...
'''
def __init__(self, version, currency, issuer,
single_input, outputs, comment, signature):
'''
Constructor
'''
super().__init__(version, currency, [issuer], [single_input],
outputs, comment, [signature])
class InputSource():
'''
A Transaction INPUT
Compact :
INDEX:SOURCE:FINGERPRINT:AMOUNT
'''
re_inline = re.compile("([0-9]+):(D|T):([0-9]+):\
([0-9a-fA-F]{5,40}):([0-9]+)\n")
re_compact = re.compile("([0-9]+):(D|T):([0-9a-fA-F]{5,40}):([0-9]+)\n")
def __init__(self, index, source, number, txhash, amount):
self.index = index
self.source = source
self.number = number
self.txhash = txhash
self.amount = amount
@classmethod
def from_inline(cls, inline):
data = InputSource.re_inline.match(inline)
index = int(data.group(1))
source = data.group(2)
number = int(data.group(3))
txhash = data.group(4)
amount = int(data.group(5))
return cls(index, source, number, txhash, amount)
@classmethod
def from_bma(cls, bma_data):
index = None
source = bma_data['type']
number = bma_data['number']
txhash = bma_data['fingerprint']
amount = bma_data['amount']
return cls(index, source, number, txhash, amount)
def inline(self):
return "{0}:{1}:{2}:{3}:{4}".format(self.index,
self.source,
self.number,
self.txhash,
self.amount)
def compact(self):
return "{0}:{1}:{2}:{3}".format(self.index,
self.source,
self.txhash,
self.amount)
class OutputSource():
'''
A Transaction OUTPUT
'''
re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([0-9]+)")
def __init__(self, pubkey, amount):
self.pubkey = pubkey
self.amount = amount
@classmethod
def from_inline(cls, inline):
data = OutputSource.re_inline.match(inline)
pubkey = data.group(1)
amount = int(data.group(2))
return cls(pubkey, amount)
def inline(self):
return "{0}:{1}".format(self.pubkey, self.amount)
'''
Ucoin public and private keys
@author: inso
'''
import base58
import base64
import scrypt
from nacl.signing import SigningKey as NaclSigningKey
from nacl.encoding import Base64Encoder
SEED_LENGTH = 32 # Length of the key
crypto_sign_BYTES = 64
SCRYPT_PARAMS = {'N': 4096,
'r': 16,
'p': 1
}
class SigningKey(NaclSigningKey):
def __init__(self, salt, password):
seed = scrypt.hash(password, salt,
SCRYPT_PARAMS['N'], SCRYPT_PARAMS['r'], SCRYPT_PARAMS['p'],
SEED_LENGTH)
super().__init__(seed)
self.pubkey = self.verify_key.encode(encoder=Base58Encoder)
class Base58Encoder(object):
@staticmethod
def encode(data):
return base58.b58encode(data)
@staticmethod
def decode(data):
return base58.b58decode(data)
'''
HD Wallet inspired from Bip32 wallets.
@author: inso
'''
'''
import os
import hmac
import hashlib
import ed25519
import struct
import base58
import base64
from hashlib import sha256
from ecdsa.curves import SECP256k1
from ecdsa.ecdsa import int_to_string, string_to_int
from ecdsa.numbertheory import square_root_mod_prime as sqrt_mod
MIN_ENTROPY_LEN = 128 # bits
HDWALLET_HARDENED = 0x80000000 # choose from hardened set of child keys
CURVE_GEN = ecdsa.ecdsa.generator_secp256k1
CURVE_ORDER = CURVE_GEN.order()
FIELD_ORDER = SECP256k1.curve.p()
INFINITY = ecdsa.ellipticcurve.INFINITY
class HDWalletKey(object):
# Static initializers to create from entropy or external formats
#
@staticmethod
def fromEntropy(entropy, public=False):
"Create a HDWallet using supplied entropy >= MIN_ENTROPY_LEN"
if entropy == None:
entropy = os.urandom(MIN_ENTROPY_LEN/8) # Python doesn't have os.random()
if not len(entropy) >= MIN_ENTROPY_LEN/8:
raise ValueError("Initial entropy %i must be at least %i bits" %
(len(entropy), MIN_ENTROPY_LEN))
I = hmac.new("UCoin seed", entropy, hashlib.sha512).digest()
Il, Ir = I[:32], I[32:]
# FIXME test Il for 0 or less than SECP256k1 prime field order
key = HDWalletKey(secret=Il, chain=Ir, depth=0, index=0, fpr='\0\0\0\0', public=False)
if public:
key.SetPublic()
return key
@staticmethod
def fromExtendedKey(xkey, public=False):
"""
Create a HDWallet by importing from extended private or public key string
If public is True, return a public-only key regardless of input type.
"""
# Sanity checks
raw = base58.b58decode_check(xkey)
# To fix
#if len(raw) != 78:
# raise ValueError("extended key format wrong length")
# Verify address version/type
#version = raw[:4]
#if version == EX_MAIN_PRIVATE:
# raise ValueError("unknown extended key version")
# Extract remaining fields
depth = ord(raw[4])
fpr = raw[5:9]
child = struct.unpack(">L", raw[9:13])[0]
chain = raw[13:45]
secret = raw[45:78]
# Extract private key or public key point
if keytype == 'xprv':
secret = secret[1:]
else:
# Recover public curve point from compressed key
lsb = ord(secret[0]) & 1
x = string_to_int(secret[1:])
ys = (x**3+7) % FIELD_ORDER # y^2 = x^3 + 7 mod p
y = sqrt_mod(ys, FIELD_ORDER)
if y & 1 != lsb:
y = FIELD_ORDER-y
point = ecdsa.ellipticcurve.Point(SECP256k1.curve, x, y)
secret = ecdsa.VerifyingKey.from_public_point(point, curve=SECP256k1)
is_pubkey = (keytype == 'xpub')
key = HDWalletKey(secret=secret, chain=chain, depth=depth, index=child,
fpr=fpr, public=is_pubkey)
if not is_pubkey and public:
key = key.SetPublic()
return key
# Normal class initializer
def __init__(self, secret, chain, depth, index, fpr, public=False):
"""
Create a public or private BIP32Key using key material and chain code.
secret This is the source material to generate the keypair, either a
32-byte string representation of a private key, or the ECDSA
library object representing a public key.
chain This is a 32-byte string representation of the chain code
depth Child depth; parent increments its own by one when assigning this
index Child index
fpr Parent fingerprint
public If true, this keypair will only contain a public key and can only create
a public key chain.
"""
self.public = public
if public is False:
self.k = ed25519.SigningKey(base58.b58decode(secret))
self.K = self.k.get_verifying_key()
else:
self.k = None
self.K = secret
self.C = chain
self.depth = depth
self.index = index
self.parent_fpr = fpr
# Internal methods not intended to be called externally
def _hmac(self, data):
"""
Calculate the HMAC-SHA512 of input data using the chain code as key.
Returns a tuple of the left and right halves of the HMAC
"""
I = hmac.new(self.C, data, hashlib.sha512).digest()
return (I[:32], I[32:])
def _CKDpriv(self, i):
"""
Create a child key of index 'i'.
If the most significant bit of 'i' is set, then select from the
hardened key set, otherwise, select a regular child key.
Returns a BIP32Key constructed with the child key parameters,
or None if i index would result in an invalid key.
"""
# Index as bytes, BE
i_str = struct.pack(">L", i)
# Data to HMAC
if i & HDWALLET_HARDENED:
data = b'\0' + self.k.to_string() + i_str
else:
data = self.PublicKey() + i_str
# Get HMAC of data
(Il, Ir) = self._hmac(data)
# Construct new key material from Il and current private key
Il_int = string_to_int(Il)
if Il_int > CURVE_ORDER:
return None
pvt_int = string_to_int(self.k.to_string())
k_int = (Il_int + pvt_int) % CURVE_ORDER
if (k_int == 0):
return None
secret = (b'\0'*32 + int_to_string(k_int))[-32:]
# Construct and return a new BIP32Key
return HDWalletKey(secret=secret, chain=Ir, depth=self.depth+1,
index=i, fpr=self.Fingerprint(), public=False)
def _CKDpub(self, i):
"""
Create a publicly derived child key of index 'i'.
If the most significant bit of 'i' is set, this is
an error.
Returns a HDWalletKey constructed with the child key parameters,
or None if index would result in invalid key.
"""
if i & HDWALLET_HARDENED:
raise Exception("Cannot create a hardened child key using public child derivation")
# Data to HMAC. Same as CKDpriv() for public child key.
data = self.PublicKey() + struct.pack(">L", i)
# Get HMAC of data
(Il, Ir) = self.hmac(data)
# Construct curve point Il*G+K
Il_int = string_to_int(Il)
if Il_int >= CURVE_ORDER:
return None
point = Il_int*CURVE_GEN + self.K.pubkey.point
if point == INFINITY:
return None
# Retrieve public key based on curve point
K_i = ed25519.VerifyingKey.from_public_point(point, curve=SECP256k1)
# Construct and return a new BIP32Key
return HDWalletKey(secret=K_i, chain=Ir, depth=self.depth, index=i, fpr=self.Fingerprint(), public=True)
# Public methods
#
def ChildKey(self, i):
"""
Create and return a child key of this one at index 'i'.
The index 'i' should be summed with BIP32_HARDEN to indicate
to use the private derivation algorithm.
"""
if self.public is False:
return self.CKDpriv(i)
else:
return self.CKDpub(i)
def SetPublic(self):
"Convert a private BIP32Key into a public one"
self.k = None
self.public = True
def PrivateKey(self):
"Return private key as string"
if self.public:
raise Exception("Publicly derived deterministic keys have no private half")
else:
return self.k.to_string()
def PublicKey(self):
"Return compressed public key encoding"
if self.K.pubkey.point.y() & 1:
ck = b'\3'+int_to_string(self.K.pubkey.point.x())
else:
ck = b'\2'+int_to_string(self.K.pubkey.point.x())
return ck
def ChainCode(self):
"Return chain code as string"
return self.C
def Identifier(self):
"Return key identifier as string"
cK = self.PublicKey()
return hashlib.new('ripemd160', sha256(cK).digest()).digest()
def Fingerprint(self):
"Return key fingerprint as string"
return self.Identifier()[:4]
def Address(self):
"Return compressed public key address"
vh160 = '\x00'+self.Identifier()
return Base58.check_encode(vh160)
def WalletImportFormat(self):
"Returns private key encoded for wallet import"
if self.public:
raise Exception("Publicly derived deterministic keys have no private half")
raw = '\x80' + self.k.to_string() + '\x01' # Always compressed
return Base58.check_encode(raw)
def ExtendedKey(self, private=True, encoded=True):
"Return extended private or public key as string, optionally Base58 encoded"
if self.public is True and private is True:
raise Exception("Cannot export an extended private key from a public-only deterministic key")
version = EX_MAIN_PRIVATE if private else EX_MAIN_PUBLIC
depth = chr(self.depth)
fpr = self.parent_fpr
child = struct.pack('>L', self.index)
chain = self.C
if self.public is True or private is False:
data = self.PublicKey()
else:
data = '\x00' + self.PrivateKey()
raw = version+depth+fpr+child+chain+data
if not encoded:
return raw
else:
return Base58.check_encode(raw)
# Debugging methods
#
def dump(self):
"Dump key fields mimicking the BIP0032 test vector format"
print " * Identifier"
print " * (hex): ", self.Identifier().encode('hex')
print " * (fpr): ", self.Fingerprint().encode('hex')
print " * (main addr):", self.Address()
if self.public is False:
print " * Secret key"
print " * (hex): ", self.PrivateKey().encode('hex')
print " * (wif): ", self.WalletImportFormat()
print " * Public key"
print " * (hex): ", self.PublicKey().encode('hex')
print " * Chain code"
print " * (hex): ", self.C.encode('hex')
print " * Serialized"
print " * (pub hex): ", self.ExtendedKey(private=False, encoded=False).encode('hex')
print " * (prv hex): ", self.ExtendedKey(private=True, encoded=False).encode('hex')
print " * (pub b58): ", self.ExtendedKey(private=False, encoded=True)
print " * (prv b58): ", self.ExtendedKey(private=True, encoded=True)
if __name__ == "__main__":
import sys
# BIP0032 Test vector 1
entropy='000102030405060708090A0B0C0D0E0F'.decode('hex')
m = BIP32Key.fromEntropy(entropy)
print "Test vector 1:"
print "Master (hex):", entropy.encode('hex')
print "* [Chain m]"
m.dump()
print "* [Chain m/0h]"
m = m.ChildKey(0+BIP32_HARDEN)
m.dump()
print "* [Chain m/0h/1]"
m = m.ChildKey(1)
m.dump()
print "* [Chain m/0h/1/2h]"
m = m.ChildKey(2+BIP32_HARDEN)
m.dump()
print "* [Chain m/0h/1/2h/2]"
m = m.ChildKey(2)
m.dump()
print "* [Chain m/0h/1/2h/2/1000000000]"
m = m.ChildKey(1000000000)
m.dump()
# BIP0032 Test vector 2
entropy = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542'.decode('hex')
m = BIP32Key.fromEntropy(entropy)
print "Test vector 2:"
print "Master (hex):", entropy.encode('hex')
print "* [Chain m]"
m.dump()
print "* [Chain m/0]"
m = m.ChildKey(0)
m.dump()
print "* [Chain m/0/2147483647h]"
m = m.ChildKey(2147483647+BIP32_HARDEN)
m.dump()
print "* [Chain m/0/2147483647h/1]"
m = m.ChildKey(1)
m.dump()
print "* [Chain m/0/2147483647h/1/2147483646h]"
m = m.ChildKey(2147483646+BIP32_HARDEN)
m.dump()
print "* [Chain m/0/2147483647h/1/2147483646h/2]"
m = m.ChildKey(2)
m.dump()
'''
\ No newline at end of file
...@@ -69,9 +69,17 @@ class Core(object): ...@@ -69,9 +69,17 @@ class Core(object):
if (os.path.exists(config.parameters['data']) if (os.path.exists(config.parameters['data'])
and os.path.isfile(config.parameters['data'])): and os.path.isfile(config.parameters['data'])):
logging.debug("Loading data...")
json_data = open(config.parameters['data'], 'r') json_data = open(config.parameters['data'], 'r')
data = json.load(json_data) data = json.load(json_data)
json_data.close() json_data.close()
for account_name in data['local_accounts']:
account_path = os.path.join(config.parameters['home'],
account_name, 'properties')
json_data = open(account_path, 'r')
data = json.load(json_data)
self.accounts.append(Account.load(data))
def save(self, account): def save(self, account):
with open(config.parameters['data'], 'w') as outfile: with open(config.parameters['data'], 'w') as outfile:
......
...@@ -19,14 +19,17 @@ class WalletsListModel(QAbstractListModel): ...@@ -19,14 +19,17 @@ class WalletsListModel(QAbstractListModel):
''' '''
super(WalletsListModel, self).__init__(parent) super(WalletsListModel, self).__init__(parent)
self.wallets = account.wallets self.wallets = account.wallets
self.communities = account.communities
def rowCount(self, parent): def rowCount(self, parent):
return len(self.wallets) return len(self.wallets) * len(self.communities)
def data(self, index, role): def data(self, index, role):
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
row = index.row() row = index.row()
value = self.wallets[row].get_text() index_community = row % len(self.communities)
index_wallet = int(row / len(self.communities))
value = self.wallets[index_wallet].get_text(self.communities[index_community])
return value return value
def flags(self, index): def flags(self, index):
......
...@@ -5,6 +5,7 @@ Created on 1 févr. 2014 ...@@ -5,6 +5,7 @@ Created on 1 févr. 2014
''' '''
from ucoinpy.api import bma from ucoinpy.api import bma
from ucoinpy.documents.block import Block
import hashlib import hashlib
import json import json
import logging import logging
...@@ -56,6 +57,13 @@ class Community(object): ...@@ -56,6 +57,13 @@ class Community(object):
def send_membership(self, account, membership): def send_membership(self, account, membership):
pass pass
def get_block(self, number=None):
if number is None:
data = self.request(bma.blockchain.Current)
else:
data = self.request(bma.blockchain.Block, req_args={'number': number})
return Block.from_signed_raw("{0}{1}\n".format(data['raw'], data['signature']))
def members_pubkeys(self): def members_pubkeys(self):
''' '''
Listing members pubkeys of a community Listing members pubkeys of a community
...@@ -109,6 +117,13 @@ class Community(object): ...@@ -109,6 +117,13 @@ class Community(object):
continue continue
return error return error
def jsonify_nodes_list(self):
data = []
for node in self.nodes:
data.append(node.jsonify())
return data
def jsonify(self): def jsonify(self):
data = {'currency': self.currency} data = {'currency': self.currency,
'nodes': self.jsonify_nodes_list()}
return data return data
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment