forked from CounterpartyXCP/counterparty-core
-
Notifications
You must be signed in to change notification settings - Fork 2
/
burn.py
103 lines (79 loc) · 3.62 KB
/
burn.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#! /usr/bin/python3
import struct
import decimal
D = decimal.Decimal
from fractions import Fraction
from . import (util, config, exceptions, bitcoin, util)
"""Burn {} to earn {} during a special period of time.""".format(config.BTC, config.XCP)
ID = 60
def validate (db, source, destination, quantity, block_index, overburn=False):
problems = []
# Check destination address.
if destination != config.UNSPENDABLE:
problems.append('wrong destination address')
if not isinstance(quantity, int):
problems.append('quantity must be in satoshis')
return problems
if quantity < 0: problems.append('negative quantity')
# Try to make sure that the burned funds won't go to waste.
if block_index < config.BURN_START - 1:
problems.append('too early')
elif block_index > config.BURN_END:
problems.append('too late')
return problems
def compose (db, source, quantity, overburn=False):
cursor = db.cursor()
destination = config.UNSPENDABLE
problems = validate(db, source, destination, quantity, util.last_block(db)['block_index'], overburn=overburn)
if problems: raise exceptions.BurnError(problems)
# Check that a maximum of 1 BTC total is burned per address.
burns = list(cursor.execute('''SELECT * FROM burns WHERE (status = ? AND source = ?)''', ('valid', source)))
already_burned = sum([burn['burned'] for burn in burns])
if quantity > (config.MAX_BURN_BY_ADDRESS * config.UNIT - already_burned) and not overburn:
raise exceptions.BurnError('{} {} may be burned per address'.format(config.MAX_BURN_BY_ADDRESS, config.BTC))
cursor.close()
return (source, [(destination, quantity)], None)
def parse (db, tx, message=None):
burn_parse_cursor = db.cursor()
status = 'valid'
if status == 'valid':
problems = validate(db, tx['source'], tx['destination'], tx['btc_amount'], tx['block_index'], overburn=False)
if problems: status = 'invalid: ' + '; '.join(problems)
if tx['btc_amount'] != None:
sent = tx['btc_amount']
else:
sent = 0
if status == 'valid':
# Calculate quantity of XCP earned. (Maximum 1 BTC in total, ever.)
cursor = db.cursor()
cursor.execute('''SELECT * FROM burns WHERE (status = ? AND source = ?)''', ('valid', tx['source']))
burns = cursor.fetchall()
already_burned = sum([burn['burned'] for burn in burns])
ONE = config.MAX_BURN_BY_ADDRESS * config.UNIT
max_burn = ONE - already_burned
if sent > max_burn: burned = max_burn # Exceeded maximum burn; earn what you can.
else: burned = sent
total_time = config.BURN_END - config.BURN_START
partial_time = config.BURN_END - tx['block_index']
multiplier = config.BURN_MULTIPLIER * (1 + (.5 * Fraction(partial_time, total_time)))
earned = round(burned * multiplier)
# Credit source address with earned XCP.
util.credit(db, tx['block_index'], tx['source'], config.XCP, earned, event=tx['tx_hash'])
else:
burned = 0
earned = 0
# Add parsed transaction to message-type–specific table.
# TODO: store sent in table
bindings = {
'tx_index': tx['tx_index'],
'tx_hash': tx['tx_hash'],
'block_index': tx['block_index'],
'source': tx['source'],
'burned': burned,
'earned': earned,
'status': status,
}
sql='insert into burns values(:tx_index, :tx_hash, :block_index, :source, :burned, :earned, :status)'
burn_parse_cursor.execute(sql, bindings)
burn_parse_cursor.close()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4