Skip to content

Commit

Permalink
Merge pull request #212 from StellarCN/dev
Browse files Browse the repository at this point in the history
feat: Support for SEP 10 challenge transaction.
  • Loading branch information
overcat committed Aug 4, 2019
2 parents 8ce51ab + a9a2ca1 commit a34b79d
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 11 deletions.
35 changes: 30 additions & 5 deletions stellar_base/builder.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# coding: utf-8
import binascii
import os
import time
import warnings

from . import memo
from . import operation
from .asset import Asset
from .exceptions import NoStellarSecretOrAddressError, StellarAddressInvalidError, SequenceError
from .federation import federation, FederationError
from .horizon import HORIZON_LIVE, HORIZON_TEST
from .horizon import Horizon
from .keypair import Keypair
from . import memo
from .network import NETWORKS, Network
from . import operation
from .transaction import Transaction
from .transaction_envelope import TransactionEnvelope as Te
from .exceptions import NoStellarSecretOrAddressError, StellarAddressInvalidError, SequenceError
from .federation import federation, FederationError


class Builder(object):
Expand Down Expand Up @@ -446,7 +448,7 @@ def append_manage_buy_offer_op(self,
selling = Asset(selling_code, selling_issuer)
buying = Asset(buying_code, buying_issuer)
op = operation.ManageBuyOffer(selling, buying, amount, price, offer_id,
source)
source)
return self.append_op(op)

def append_manage_sell_offer_op(self,
Expand Down Expand Up @@ -939,3 +941,26 @@ def get_sequence(self):

address = self.horizon.account(self.address)
return int(address.get('sequence'))

@classmethod
def challenge_tx(cls, server_secret, client_account_id, archor_name, network='TESTNET', timeout=300):
"""Returns a valid `SEP0010 <https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md>`_
challenge transaction which you can use for Stellar Web Authentication.
:param server_secret: secret key for server's signing account.
:param client_account_id: The stellar account that the wallet wishes to authenticate with the server.
:param archor_name: Anchor's name to be used in the manage_data key.
:param str network: The network to connect to for verifying and retrieving
additional attributes from. 'PUBLIC' is an alias for 'Public Global Stellar Network ; September 2015',
'TESTNET' is an alias for 'Test SDF Network ; September 2015'. Defaults to TESTNET.
:param int timeout: Challenge duration in seconds (default to 5 minutes).
:return: a valid SEP0010 challenge transaction which you can use for Stellar Web Authentication.
:rtype: :class:`Builder`
"""
now = int(time.time())
transaction = cls(secret=server_secret, network=network, sequence=-1)
transaction.add_time_bounds({'minTime': now, 'maxTime': now + timeout})
nonce = os.urandom(64)
transaction.append_manage_data_op(data_name='{} auth'.format(archor_name), data_value=nonce,
source=client_account_id)
return transaction
8 changes: 7 additions & 1 deletion stellar_base/keypair.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import warnings

from .base58 import b58decode_check, b58encode_check
from .exceptions import MissingSigningKeyError, BadSignatureError, NotValidParamError
from .stellarxdr import Xdr
from .utils import encode_check, StellarMnemonic, \
is_valid_address, is_valid_secret_key
from .exceptions import MissingSigningKeyError, BadSignatureError, NotValidParamError

# noinspection PyBroadException
try:
Expand Down Expand Up @@ -279,3 +279,9 @@ def to_old_address(self):
def to_old_seed(self):
seed = chr(33).encode() + self.raw_seed()
return b58encode_check(seed)

def __eq__(self, other):
if not isinstance(other, Keypair):
return False

return self.signing_key == other.signing_key and self.verifying_key == other.verifying_key
37 changes: 32 additions & 5 deletions tests/test_builder.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# encoding: utf-8
import time

import pytest
import mock
import pytest

from stellar_base import memo
from stellar_base.builder import Builder
from stellar_base.exceptions import NoStellarSecretOrAddressError, FederationError
from stellar_base.horizon import horizon_testnet, horizon_livenet, Horizon
from stellar_base.keypair import Keypair
from stellar_base.exceptions import NoStellarSecretOrAddressError, FederationError
from stellar_base.operation import ManageData


@pytest.fixture(scope='module')
Expand Down Expand Up @@ -71,11 +73,11 @@ def test_builder_append_ops(self):
pre_auth_tx=b"\x95\xe5\xbb\x95\x15\xd9\x9f\x82\x9d\xf9\x93\xc3'\x8e\xeb\xf1\nj!\xda\xa4\xa1\xe4\xf2<6cG}\x17\x97\xfe",
signer_weight=1). \
append_manage_sell_offer_op(selling_code='XLM', selling_issuer=None, buying_code='BEER',
buying_issuer=bob_account, amount='1', price='10', offer_id=0). \
append_manage_buy_offer_op(selling_code='XLM', selling_issuer=None, buying_code='BEER',
buying_issuer=bob_account, amount='1', price='10', offer_id=0). \
append_manage_buy_offer_op(selling_code='XLM', selling_issuer=None, buying_code='BEER',
buying_issuer=bob_account, amount='1', price='10', offer_id=0). \
append_create_passive_sell_offer_op(selling_code='XLM', selling_issuer=None, buying_code='BEER',
buying_issuer=bob_account, amount='1', price={'n': 10, 'd': 1}). \
buying_issuer=bob_account, amount='1', price={'n': 10, 'd': 1}). \
append_account_merge_op(destination=bob_account). \
append_inflation_op(). \
append_manage_data_op(data_name='hello', data_value='world'). \
Expand Down Expand Up @@ -199,3 +201,28 @@ def test_network_and_horizon(self, setup, test_data):
horizon_uri=setup.horizon_endpoint_uri)
assert builder.network == 'PUBLIC'
assert builder.horizon.horizon_uri == Horizon(horizon_uri=setup.horizon_endpoint_uri).horizon_uri

def test_challenge_tx(self):
server_kp = Keypair.random()
client_account_id = "GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF"
timeout = 600
network = 'TESTNET'
archor_name = "SDF"

tx = Builder.challenge_tx(server_secret=server_kp.seed().decode(),
client_account_id=client_account_id,
archor_name=archor_name,
network=network,
timeout=timeout)
assert len(tx.ops) == 1
op = tx.ops[0]
assert isinstance(op, ManageData)
assert op.data_name == "SDF auth"
assert len(op.data_value) == 64
assert op.source == client_account_id

now = int(time.time())
assert now - 3 < tx.time_bounds['minTime'] < now + 3
assert tx.time_bounds['maxTime'] - tx.time_bounds['minTime'] == timeout
assert tx.keypair == server_kp
assert tx.sequence == -1

0 comments on commit a34b79d

Please sign in to comment.