"""Lightning network API for a lightning node.

Interface:
API -- the Blueprint returned by serverutil.api_factory

init(conf) - Set up the database
send(url, amount)
- Send amount satoshis to the node identified by url. The url is not
  necessarily a direct peer.

Error conditions have not been defined.

Database:
PEERS contains information nodes we have channels open with
address: their url
fees: how much we require as fees for relaying across this channel

ROUTES is the routing table. It has one row for every other lightning node
address: their url
cost: total fees to route payment to that node
nexthop: where should payment go next on the path to that node
"""

from flask import g
import jsonrpcproxy
from serverutil import api_factory, database
import channel
from sqlalchemy import Column, Integer, String

API, REMOTE, Model = api_factory('lightning')

class Peer(Model):
    """Database model of a peer node."""

    __tablename__ = "peers"

    address = Column(String, primary_key=True)
    fees = Column(Integer)

class Route(Model):
    """Database model of a route."""

    __tablename__ = "routes"

    address = Column(String, primary_key=True)
    cost = Column(Integer)
    next_hop = Column(String)

@channel.CHANNEL_OPENED.connect_via('channel')
def on_open(dummy_sender, address, **dummy_args):
    """Routing update on open."""
    fees = 10000
    # Add the new peer
    peer = Peer(address=address, fees=fees)
    database.session.add(peer)
    # Broadcast a routing update
    update(address, address, 0)
    # The new peer doesn't know all our routes.
    # As a hack, rebuild/rebroadcast the whole routing table.
    routes = Route.query.all()
    Route.query.delete()
    database.session.commit()
    for route in routes:
        update(route.next_hop, route.address, route.cost)

@REMOTE
def update(next_hop, address, cost):
    """Routing update."""
    # Check previous route, and only update if this is an improvement
    if address == g.addr:
        return
    route = Route.query.get(address)
    if route is None:
        route = Route(address=address, cost=cost, next_hop=next_hop)
        database.session.add(route)
    elif route.cost <= cost:
        return
    else:
        route.cost = cost
        route.next_hop = next_hop
    # Tell all our peers
    database.session.commit()
    for peer in Peer.query.all():
        bob = jsonrpcproxy.Proxy(peer.address + 'lightning/')
        bob.update(g.addr, address, cost + peer.fees)
    return True

@REMOTE
def send(url, amount):
    """Send coin, perhaps through more than one hop.

    After this call, the node at url should have recieved amount satoshis.
    Any fees should be collected from this node's balance.
    """
    # Paying ourself is easy
    if url == g.addr:
        return
    route = Route.query.get(url)
    if route is None:
        # If we don't know how to get there, let channel try.
        # As set up currently, this shouldn't ever work, but
        # we can let channel throw the error
        channel.send(url, amount)
    else:
        # Send the next hop money over our payment channel
        channel.send(route.next_hop, amount + route.cost)
        # Ask the next hop to send money to the destination
        bob = jsonrpcproxy.Proxy(route.next_hop+'lightning/')
        bob.send(url, amount)

"""Lightning network API for a lightning node.

Interface:
API -- the Blueprint returned by serverutil.api_factory

init(conf) - Set up the database
send(url, amount)
- Send amount satoshis to the node identified by url. The url is not
  necessarily a direct peer.

Error conditions have not been defined.

Database:
PEERS contains information nodes we have channels open with
address: their url
fees: how much we require as fees for relaying across this channel

ROUTES is the routing table. It has one row for every other lightning node
address: their url
cost: total fees to route payment to that node
nexthop: where should payment go next on the path to that node
"""

from flask import g
import jsonrpcproxy
from serverutil import api_factory, database
import channel
from sqlalchemy import Column, Integer, String

API, REMOTE, Model = api_factory('lightning')

class Peer(Model):
    """Database model of a peer node."""

    __tablename__ = "peers"

    address = Column(String, primary_key=True)
    fees = Column(Integer)

class Route(Model):
    """Database model of a route."""

    __tablename__ = "routes"

    address = Column(String, primary_key=True)
    cost = Column(Integer)
    next_hop = Column(String)

@channel.CHANNEL_OPENED.connect_via('channel')
def on_open(dummy_sender, address, **dummy_args):
    """Routing update on open."""
    fees = 10000
    # Add the new peer
    peer = Peer(address=address, fees=fees)
    database.session.add(peer)
    # Broadcast a routing update
    update(address, address, 0)
    # The new peer doesn't know all our routes.
    # As a hack, rebuild/rebroadcast the whole routing table.
    routes = Route.query.all()
    Route.query.delete()
    database.session.commit()
    for route in routes:
        update(route.next_hop, route.address, route.cost)

@REMOTE
def update(next_hop, address, cost):
    """Routing update."""
    # Check previous route, and only update if this is an improvement
    if address == g.addr:
        return
    route = Route.query.get(address)
    if route is None:
        route = Route(address=address, cost=cost, next_hop=next_hop)
        database.session.add(route)
    elif route.cost <= cost:
        return
    else:
        route.cost = cost
        route.next_hop = next_hop
    # Tell all our peers
    database.session.commit()
    for peer in Peer.query.all():
        bob = jsonrpcproxy.Proxy(peer.address + 'lightning/')
        bob.update(g.addr, address, cost + peer.fees)
    return True

@REMOTE
def send(url, amount):
    """Send coin, perhaps through more than one hop.

    After this call, the node at url should have recieved amount satoshis.
    Any fees should be collected from this node's balance.
    """
    # Paying ourself is easy
    if url == g.addr:
        return
    route = Route.query.get(url)
    if route is None:
        # If we don't know how to get there, let channel try.
        # As set up currently, this shouldn't ever work, but
        # we can let channel throw the error
        channel.send(url, amount)
    else:
        # Send the next hop money over our payment channel
        channel.send(route.next_hop, amount + route.cost)
        # Ask the next hop to send money to the destination
        bob = jsonrpcproxy.Proxy(route.next_hop+'lightning/')
        bob.send(url, amount)

Lightning network API for a lightning node.  Interface: API -- the Blueprint returned by serverutil.api_factory  init(conf) - Set up the database send(url, amount) - Send amount satoshis to the node identified by url. The url is not   necessarily a direct peer.  Error conditions have not been defined.  Database: PEERS contains information nodes we have channels open with address: their url fees: how much we require as fees for relaying across this channel  ROUTES is the routing table. It has one row for every other lightning node address: their url cost: total fees to route payment to that node nexthop: where should payment go next on the path to that node Database model of a peer node. Database model of a route. Routing update on open. # Add the new peer # Broadcast a routing update # The new peer doesn't know all our routes. # As a hack, rebuild/rebroadcast the whole routing table. Routing update. # Check previous route, and only update if this is an improvement # Tell all our peers Send coin, perhaps through more than one hop.      After this call, the node at url should have recieved amount satoshis.     Any fees should be collected from this node's balance. # Paying ourself is easy # If we don't know how to get there, let channel try. # As set up currently, this shouldn't ever work, but # we can let channel throw the error # Send the next hop money over our payment channel # Ask the next hop to send money to the destination