Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

add bitcoin receiving address to profile #1437

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions branch.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-------------------------------------------------------------------------------
-- https://github.com/gittip/www.gittip.com/issues/1164

BEGIN;
CREATE TABLE bitcoin_addresses
( id serial PRIMARY KEY
, ctime timestamp with time zone NOT NULL
, mtime timestamp with time zone NOT NULL
DEFAULT CURRENT_TIMESTAMP
, participant text NOT NULL REFERENCES participants
ON UPDATE CASCADE ON DELETE RESTRICT
, bitcoin_address text NOT NULL
);

ALTER TABLE participants ADD COLUMN bitcoin_address text DEFAULT NULL;

CREATE RULE bitcoin_addresses
AS ON UPDATE TO participants
WHERE NEW.bitcoin_address <> OLD.bitcoin_address
DO
INSERT INTO bitcoin_addresses
(ctime, participant, bitcoin_address)
VALUES ( COALESCE (( SELECT ctime
FROM bitcoin_addresses
WHERE participant=OLD.username
LIMIT 1
), CURRENT_TIMESTAMP)
, OLD.username
, NEW.bitcoin_address
);

END;
40 changes: 40 additions & 0 deletions templates/connected-accounts.html
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,46 @@ <h2>Connected Accounts</h2>
>on Balanced Payments</a>{% else %}on Balanced Payments{% end %}</div>
</td>
</tr>
<tr>
<td class="account-type">
<img src="/assets/bitcoin.png" />
</td>
<td class="account-details">

{% if not user.ANON and user.participant == participant %}
<div class="bitcoin">
{% else %}
<div>
{% end %}
{% if participant.bitcoin_address %}
<a rel="me" href="http://blockchain.info/address/{{ participant.bitcoin_address }}">
{{ participant.bitcoin_address }}
</a>
<div class="account-type">Bitcoin address
{% if not user.ANON and user.participant == participant %}
(<a href="javascript:;" class="toggle-bitcoin">edit</a>)
{% end %}
</div>
{% else %}
{% if not user.ANON and user.participant == participant %}
Add a <a href="javascript:;" class="toggle-bitcoin">Bitcoin address</a>.
{% else %}
No Bitcoin receiving address.
{% end %}
{% end %}
</div>
<form class="bitcoin-submit">
<input type="text" class="bitcoin hidden"
{% if participant.bitcoin_address %}
value="{{ participant.bitcoin_address }}"
{% end %}
>
<button type="submit" class="bitcoin hidden">Save</button>
<button type="cancel" class="bitcoin cancel hidden">Cancel</button>
</form>

</td>
</tr>
</table>


37 changes: 37 additions & 0 deletions tests/test_bitcoin_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import print_function, unicode_literals

import json

from gittip.testing import Harness
from gittip.testing.client import TestClient


class Tests(Harness):

def change_bitcoin_address(self, address, user='alice'):
self.make_participant('alice')

client = TestClient()
response = client.get('/')
csrf_token = response.request.context['csrf_token']

response = client.post( "/alice/bitcoin.json"
, { 'bitcoin_address': address
, 'csrf_token': csrf_token
}
, user=user
)
return response

def test_participant_can_change_their_address(self):
response = self.change_bitcoin_address('17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j')
actual = json.loads(response.body)['bitcoin_address']
assert actual == '17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j', actual

def test_anonymous_gets_404(self):
response = self.change_bitcoin_address('17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j', user=None)
assert response.code == 404, response.code

def test_invalid_is_400(self):
response = self.change_bitcoin_address('12345')
assert response.code == 400, response.code
37 changes: 37 additions & 0 deletions www/%username/bitcoin.json.spt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Save a bitcoin address.

"""
from aspen import Response
from gittip import db
from hashlib import sha256

digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def decode_base58(bc, length):
n = 0
for char in bc:
n = n * 58 + digits58.index(char)
return bytearray( (n >> i*8) & 0xff for i in reversed(range(length)))

def check_bc(bc):
bcbytes = decode_base58(bc, 25)
return bcbytes[-4:] == sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]

[---------------------------------------------------------------]

if user.ANON:
raise Response(404)
request.allow("POST")

bitcoin_address = request.body['bitcoin_address'].strip()

if check_bc(bitcoin_address) or bitcoin_address == '':
db.run( "UPDATE participants SET bitcoin_address=%s "
"WHERE username=%s"
, (bitcoin_address, user.participant.username)
)
else:
raise Response(400)

response.body = { "bitcoin_address": bitcoin_address
, "username": user.participant.username }
6 changes: 6 additions & 0 deletions www/%username/public.json.spt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ if not user.ANON:
# https://github.com/gittip/www.gittip.com/issues/680

github, twitter, bitbucket, bountysource = participant.get_accounts_elsewhere()
bitcoin = None

if bitbucket is not None:
bitbucket = "https://bitbucket.org/api/1.0/users/%s" % \
bitbucket.user_info['username']
Expand All @@ -91,10 +93,14 @@ if bountysource is not None:
bountysource = "https://api.bountysource.com/users/%s" % \
bountysource.user_info['display_name']

if participant.bitcoin_address is not None:
bitcoin = "http://blockchain.info/address/%s" % participant.bitcoin_address

output['elsewhere'] = { 'bitbucket': bitbucket
, 'github': github
, 'twitter': twitter
, 'bountysource': bountysource
, 'bitcoin': bitcoin
}

response.body = output
Expand Down
54 changes: 54 additions & 0 deletions www/assets/%version/gittip/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,60 @@ $(document).ready(function()
);
});

// Wire up bitcoin input.
// ==============================

$('.bitcoin').on("click", "a.toggle-bitcoin", function()
{
// "Add bitcoin address" text or existing
// bitcoin address was clicked, show the text box
$('.bitcoin').toggle();
$('input.bitcoin').focus();
});

$('.bitcoin-submit')
.on('click', '[type=submit]', function () {
var $this = $(this);

$this.text('Saving...');

function success(d)
{
$('a.bitcoin').text(d.bitcoin_address);
$('.bitcoin').toggle();
if (d.bitcoin_address === '') {
html = "Add a <a href=\"javascript:;\" class=\"toggle-bitcoin\">Bitcoin address</a>.";
} else {
html = "<a rel=\"me\" href=\"http://blockchain.info/address/";
html += d.bitcoin_address + "\">" + d.bitcoin_address + "</a>";
html += "<div class=\"account-type\">Bitcoin address ";
html += "(<a href=\"javascript:;\" class=\"toggle-bitcoin\">edit</a>)</div>";
}
$('div.bitcoin').html(html);
$this.text('Save');
}
jQuery.ajax(
{ url: "bitcoin.json"
, type: "POST"
, dataType: 'json'
, success: success
, error: function() {
$this.text('Save');
alert( "Invalid Bitcoin address. "
+ "Please try again."
);
}
, data: { bitcoin_address: $('input.bitcoin').val() }
}
)

return false;
})
.on('click', '[type=cancel]', function () {
$('.bitcoin').toggle();

return false;
});

// Wire up API Key
// ===============
Expand Down
Binary file added www/assets/bitcoin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.