Skip to content

Commit

Permalink
Flask Floppy bird API and various bugfixses on sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Aug 2, 2018
1 parent cb5028c commit c3accc5
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 19 deletions.
19 changes: 19 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
These are instructions to run the floppy-bird Flask API
Remember this is a example app and is not intended for production use, only to test the mobius python sdk and showcase It's Classes/Functions,
So there's some steps to take before you can run the app.

1.Create virtualenv with: $ virtualenv -p python3 env
2.Activate env with : $ source env/bin/activate
3.Install requirements from python sdk with : $ pip install -r requirements.txt
4.Export needed vars for the api to work : $ export FLASK_ENV=development
$ export APP_KEY={you're developer keypair}
$ export FLASK_APP={path to api.py}
5.Run the API with (Do this from directory outside the mobius sdk folder): $ python -m flask run
6.Get the challenge and token from API recommended to use some API development tool like Postman or anyother rest client
-Get challenge xdr from : GET http://{API_DOMAIN}/api/
-Post challenge xdr to get the token: : POST http://{API_DOMAIN}/api/ with {"xdr":"(you're challenge xdr from last step)"}
(I'll leave get_token.py script that does above requests and writes token to token.txt)
-Post token to : POST http://{API_DOMAIN}/api/test?token={you're token} to get public key
7.Go to https://mobius.network/friendbot and add some MOBI coins to you're account
8.Change DAPP_API in flappy-dapp/frontend/public/js/main.js into url of the api (localhost:5000/api in my case)
9.go to http://{APP_DOMAIN}/?token={you're token} and enjoy the game.
Empty file added backend/__init__.py
Empty file.
241 changes: 241 additions & 0 deletions backend/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import os
import json
import binascii
import datetime
from functools import wraps

from flask import Flask
from flask_cors import CORS
from flask import jsonify, abort, request

from ..mobius_client_python.app.app_builder import AppBuilder
from ..mobius_client_python.auth.challenge import Challenge
from ..mobius_client_python.auth.token import Token
from ..mobius_client_python.auth.jwt import Jwt
from ..mobius_client_python.auth.sign import Sign
from ..mobius_client_python.blockchain.friend_bot import FriendBot # working !
from ..mobius_client_python.blockchain.add_cosigner import AddCosigner # working
from ..mobius_client_python.blockchain.create_trustline import CreateTrustline # working
from ..mobius_client_python.client import Client # working

from stellar_base.keypair import Keypair

from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp

# Flask app
app = Flask(__name__)

# Enable cors
CORS(app)

app.config['DEBUG'] = True
app.config['APP_KEY'] = os.environ['APP_KEY']
app_key = app.config['APP_KEY']

# Developer
f = FriendBot()

dev_keypair = Keypair.from_seed(app_key)
dev_created = f.call(dev_keypair)
trust_line_dev = CreateTrustline().call(keypair=dev_keypair)

class User(object):
def __init__(self, id, username, password):
self.id = id
self.username = username
self.password = password
self.keypair = Keypair.random()
self.address = self.keypair.address().decode()

# Mobi balance and dev user cosign config
user_created = f.call(self.keypair)
cosig = AddCosigner().call(keypair=self.keypair, cosigner_keypair=dev_keypair)
trust_line_user = CreateTrustline().call(keypair=self.keypair)

def __str__(self):
return "User(id='%s')" % self.id


users = [
User(1, 'test', 'test'),
User(2, 'mobius', 'mobius'),
User(3, 'crowdbotics', 'crowdbotics'),
]

username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}

# quick auth handle
def authenticate(username, password):
user = username_table.get(username, None)
if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
return user

def identity(payload):
user_id = payload['identity']
return userid_table.get(user_id, None)

def app_instance(user):
app = AppBuilder().build(dev_keypair.seed(),user.address)

return app

jwt = JWT(app, authenticate, identity)

def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if request.args.get('token') is None:
return abort(403, description='Token missing')
else:
token = request.args.get('token')
jwt = Jwt(secret=app_key)
jwt_token = jwt.decode(value=token)
current_user = None

for user in users:
if jwt_token['public_key'] == user.address:
current_user = user

if current_user == None:
return abort(403, description='Token not matched')

return f(user=current_user, *args, **kwargs)
return decorated_function



@app.route('/api/', methods=['GET','POST'])
def index():
try:
user = users[0] # test user

if request.method == 'GET':

time = datetime.datetime.now() + datetime.timedelta(days=60)

challenge_te_xdr = Challenge(developer_secret=dev_keypair.seed(),
expires_in=time)\
.call()


te_xdr = Sign(user_secret=user.keypair.seed(),te_xdr=challenge_te_xdr.decode(),address=dev_keypair.address().decode()).call()


return jsonify(te_xdr)

elif request.method == 'POST':

data = json.loads(request.data.decode('utf-8'))
dapp = app_instance(user)


token = Token(
developer_secret=app_key,
te_xdr=data['xdr'],
address=user.keypair.address().decode(),
)

token.validate()
jwt = Jwt(secret=app_key)

jwt_token = jwt.encode(token=token)

return jsonify(jwt_token.decode())

except Exception as error:
return error

@app.route('/api/test', methods=['POST'])
@login_required
def test(user=None):
try:
print(user)
return jsonify(user.address)
except Exception as error:
return error

@app.route('/api/balance',methods=['GET'])
@login_required
def balance(user=None):
try:
dapp = app_instance(user)
return jsonify({'balance': dapp.user_balance()})
except Exception as error:
return error

@app.route('/api/charge',methods=['POST'])
@login_required
def charge(user=None):
try:
dapp = app_instance(user)

try:
data = json.loads(request.data.decode('utf-8'))
amount = data['amount']
except:
amount = 1

if not amount or not float(amount):
return abort(400, description='Invalid amount')

response = dapp.charge(amount)

hash_meta = binascii.hexlify(response.hash_meta()).decode()

return jsonify({
'status': 'ok',
'tx_hash': hash_meta,
'balance': dapp.user_balance(),
})

except Exception as error:
return error

@app.route('/api/payout')
@login_required
def payout(user=None):
try:
dapp = app_instance(current_identity)
data = json.loads(request.data.decode('utf-8'))
amount = data['amount']
target = data['target_address']

if not amount or not float(amount):
return abort(400, description='Invalid amount')

response = dapp.payout(amount,target)

return jsonify({
'status': 'ok',
'tx_hash': response.hash_meta(),
'balance': dapp.user_balance(),
})

except Exception as error:
return error

@app.route('/api/transfer')
@login_required
def transfer(user=None):
try:
dapp = app_instance(user)
data = json.loads(request.data.decode('utf-8'))

amount = data['amount']
target = data['target_address']

if not amount or not float(amount):
return abort(400, description='Invalid amount')

response = dapp.transfer(amount,target)

return jsonify({
'status': 'ok',
'tx_hash': response.hash_meta(),
'balance': response.user_balance(),
})

except Exception as error:
return error
16 changes: 16 additions & 0 deletions get_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import requests

base_url = "http://localhost:5000/api/"

friend_bot_url = "https://mobius.network/friendbot"

xdr = requests.get(base_url)

token = requests.post(base_url,json={"xdr":xdr.text})
token = str(token.text)

f = open('token.txt', 'w')

f.write(token)

f.close()
52 changes: 37 additions & 15 deletions mobius_client_python/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self, app_account, user_account):
def authorized(self):
return self.user_account.authorized(self.app_keypair())

def app_account(self):
def app_accqount(self):
return self.app_account

def app_balance(self):
Expand All @@ -37,16 +37,23 @@ def user_keypair(self):
if not keypair:
keypair = self.user_account.keypair
return keypair

def charge(self, amount, destination=None):
if self.user_balance() < float(amount):
raise Exception('Insufficient Funds')

tx = self.submit_tx()
te = self.build_te()

if destination:
return tx.add_operation(self.app_payment_op(amount,destination))
te.tx.add_operation(self.app_payment_op(amount,destination))
else:
return tx.add_operation(self.user_payment_op(amount,self.app_keypair().address().decode()))
te.tx.add_operation(self.user_payment_op(amount,self.app_keypair().address().decode()))

te.sign(self.app_keypair())

self.submit_tx(te)

return te

def payout(self, amount, asset, destination=None):

Expand All @@ -56,44 +63,59 @@ def payout(self, amount, asset, destination=None):
if self.app_balance(asset) < float(amount):
raise Exception('Insufficient Funds')

tx = self.submit_tx()
te = self.build_te()

te.tx.add_operation(self.app_payment_op(amount,destination))

te.sign(self.app_keypair())

self.submit_tx(te)

return tx.add_operation(self.app_payment_op(amount,destination))
return te

def transfer(self, amount, destination):
if self.user_balance() < float(amount):
raise Exception('Insufficient Funds')

tx = self.submit_tx()
return tx.add_operation(self.user_payment_op(amount,destination))
te = self.build_te()

def submit_tx(self):
builder = Builder(address=self.user_account.get_info()['account_id'])
te.tx.add_operation(self.user_payment_op(amount,destination))

te.sign(self.app_keypair())

self.submit_tx(te)

return te

def build_te(self):
builder = Builder(address=self.user_account.get_info()['account_id'])
te = builder.gen_te()

te.sign(self.app_keypair())
return te


def submit_tx(self, te):

response = self.client_instance.submit(te)
response = self.client_instance.submit(te.xdr().decode())

self.reload()

return te.tx
return te

def reload(self):
return self.app_account.reload() and self.user_account.reload()


def user_payment_op(self, amount, destination):
return Payment(opts={'destination': destination,
'amount': amount,
'amount': str(amount),
'asset': Client().get_stellar_asset(),
'source': self.user_keypair().address().decode()
})

def app_payment_op(self, amount, destination):
return Payment(opts={'destination': destination,
'amount': amount,
'amount': str(amount),
'asset': Client().get_stellar_asset(),
'source': self.app_keypair().address().decode()
})
Expand Down
2 changes: 1 addition & 1 deletion mobius_client_python/auth/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ def encode(self, token):
return jwt.encode(payload,self.secret,self.ALG)

def decode(self, value):
return jwt.decode(value.decode(),self.secret,algorithms=[self.ALG])
return jwt.decode(value,self.secret,algorithms=[self.ALG])
Loading

0 comments on commit c3accc5

Please sign in to comment.