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])
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())
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
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())
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})
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)
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())
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])