예제 #1
0
def ListProposalsEndpoint(session, wallet):
    if request.args.get('signing') == 'true':
        proposals = Proposal.select().where(Proposal.wallet == wallet,
                                            Proposal.status == 'signing')
    else:
        proposals = Proposal.select().where(Proposal.wallet == wallet)
    proposals = list(proposals)
    return jsonify([proposal.serialize() for proposal in proposals])
예제 #2
0
def TxRelayStatusEndpoint(session, wallet, data, proposal_id):
    proposal = Proposal.select().where(Proposal.proposal_id == proposal_id,
                                       Proposal.wallet == wallet).first()
    if not proposal:
        raise APIError("Proposal not found", status_code=404)

    if proposal.status == 'relayed':
        return ('', 208)
    elif proposal.status != 'approved':
        raise APIError("Proposal is not approved", status_code=403)

    proposal.tx_id = ','.join(data['tx_id'])
    proposal.status = 'relayed'
    proposal.save()

    app.redis.publish(
        "stream:proposal_status",
        json.dumps({
            "wallet_id": wallet.id,
            "public_key": pk,
            "multisig": wallet.multisig_source,
            "status": proposal.status,
            "proposal_id": proposal.proposal_id
        }))

    return jsonify(proposal.serialize())
예제 #3
0
async def NewTxProposalStream(session, wallet, request):
    stream = web.StreamResponse()
    stream.headers['Content-Type'] = 'text/event-stream'
    stream.headers['Cache-Control'] = 'no-cache'
    stream.headers['Connection'] = 'keep-alive'
    stream.enable_chunked_encoding()
    await stream.prepare(request)
    sub = await aioredis.create_redis('redis://localhost')
    subscriber = await sub.subscribe('stub:new_tx_proposal')
    subscriber = subscriber[0]
    pk = session.public_key

    while (await subscriber.wait_message()):
        msg = await subscriber.get()
        if wallet is None:
            break
        if msg is None:
            continue

        try:
            proposal = await objects.get(Proposal.select().where(
                Proposal.proposal_id == msg, Proposal.wallet == wallet.id))
        except Proposal.DoesNotExist:
            continue
        proposal_json = '%s\r\n' % json.dumps(proposal.serialize())
        await stream.write(
            bytes(proposal_json, 'utf-8')
            # b"data: %s\r\n\r\n" % bytes(msg)
        )

    await stream.write_eof()
    await sub.unsubscribe('stub:new_tx_proposal')

    return stream
예제 #4
0
def TxProposalDeleteEndpoint(session, wallet, proposal_id):
    proposal = Proposal.select().where(Proposal.proposal_id == proposal_id,
                                       Proposal.wallet == wallet).first()
    if not proposal:
        raise APIError("Proposal not found", status_code=404)

    proposal.status = 'deleted'
    proposal.save()

    app.redis.publish(
        "stream:proposal_status",
        json.dumps({
            "wallet_id": wallet.id,
            "public_key": pk,
            "multisig": wallet.multisig_source,
            "status": proposal.status,
            "proposal_id": proposal.proposal_id
        }))

    return jsonify(proposal.serialize())
예제 #5
0
def CreateProposalEndpoint(session, wallet, data):
    pk = session.public_key
    amount = int(data['amount'])
    fee = int(data['fee'])

    if Proposal.select().where(Proposal.wallet == wallet,
                               Proposal.status == 'signing').exists():
        raise APIError("Only one open proposal can exist at the time",
                       status_code=409)

    proposal = Proposal.create(
        wallet=wallet,
        destination_address=data['destination_address'],
        description=data['description'],
        proposal_id=Proposal.generate_unique_id(),
        amount=amount,
        fee=fee,
    )

    # create first transaction approve
    ProposalDecision.create(
        proposal=proposal,
        signed_transaction=data['signed_transaction'],
        source=pk,
        approved=True,
    )

    # send message to all wallet participants
    app.redis.publish("stub:new_tx_proposal", proposal.proposal_id)
    app.redis.publish(
        "stream:new_proposal",
        json.dumps({
            "wallet_id": wallet.id,
            "public_key": pk,
            "multisig": wallet.multisig_source,
            "proposal_id": proposal.proposal_id
        }))

    return jsonify({'proposal_id': proposal.proposal_id})
예제 #6
0
def ProposalDecisionEndpoint(session, wallet, data, proposal_id):
    pk = session.public_key

    lock = app.redis.get("lock:%s" % wallet.source)
    if not lock or lock.decode('ascii') != session.id:
        raise APIError("Proposal signing locked for 25 seconds",
                       status_code=409)

    proposal = Proposal.select().where(
        Proposal.proposal_id == proposal_id,
        Proposal.wallet == wallet,
    ).first()
    if not proposal:
        raise APIError("Proposal not found", status_code=404)

    existing_decision = ProposalDecision.select().where(
        ProposalDecision.proposal == proposal,
        ProposalDecision.source == pk,
    ).first()
    if existing_decision:
        existing_decision.delete_instance()
        # raise APIError("Decision already made", status_code=400)

    if bool(data['approved']):
        if 'signed_transaction' not in data:
            raise APIError("Field 'signed_transaction' must be supplied")

        if proposal.approvals != int(data['approval_nonce']):
            raise APIError("Approval nonce must be actual number of approvals",
                           status_code=400)

        signed_transaction = data['signed_transaction']
    else:
        signed_transaction = ""

    ProposalDecision.create(
        proposal=proposal,
        approved=bool(data['approved']),
        signed_transaction=signed_transaction,
        source=pk,
    )

    if proposal.approvals >= wallet.signers:
        proposal.status = 'approved'
        proposal.save()
    if proposal.rejects > (wallet.participants - wallet.signers):
        proposal.status = 'rejected'
        proposal.save()

    # TODO: check number of received decisions
    # if reject_decisions.count() > (wallet.participans - wallet.signers):
    #     reject_proposal
    # if approve_decisions.count() == wallet.signers:
    #     send transaction to monero network
    #     send tx_relay_status notification

    # send message to all wallet participants
    app.redis.publish("stub:tx_proposal_status", proposal.proposal_id)
    app.redis.publish(
        "stream:proposal_status",
        json.dumps({
            "wallet_id": wallet.id,
            "public_key": pk,
            "multisig": wallet.multisig_source,
            "status": proposal.status,
            "proposal_id": proposal.proposal_id
        }))

    app.redis.delete("lock:%s" % wallet.source)

    return ('', 204)
예제 #7
0
def ProposalEndpoint(session, wallet, proposal_id):
    proposal = Proposal.select().where(Proposal.proposal_id == proposal_id,
                                       Proposal.wallet == wallet).first()
    if not proposal:
        raise APIError("Proposal not found", status_code=404)
    return jsonify(proposal.serialize())
예제 #8
0
def NewProposalEndpoint(session, wallet):
    proposals = Proposal.select().join(ProposalDecision).where(
        Proposal.wallet == wallet.id, ProposalDecision.source != wallet.source)

    return jsonify([proposal.serialize() for proposal in proposals])