Compare commits

...

27 Commits

Author SHA1 Message Date
Vivek Teega
3f628b70a5 Merging upstrem changes 2018-12-16 17:44:11 +05:30
Vivek Teega
64733e93d9 FLO URI on the QT wallet 2018-11-29 12:50:15 +05:30
Vivek Teega
dd6e5ae9ac Changing icons from BTC -> FLO 2018-11-22 17:15:58 +05:30
Vivek Teega
34e84858c8 Changed naming from BTC -> FLO 2018-11-22 11:03:15 +05:30
Vivek Teega
ecea61d9ca Fixed display of saved transactions 2018-11-21 21:15:28 +05:30
Vivek Teega
b3a21f53bc Chunk verification without saving headers locally 2018-11-08 21:07:26 +05:30
Vivek Teega
085cd9919e Target calculation & chunk verification
This commit adds the code for FLO chunk verification. FLO chunk verification is different from Bitcoin chunk verification as the blockchain has 3 different target recalculation times. ie. 90 blocks at height 0->208440, 15 blocks at height 208440->426000 & 1 block further down

Changes have also been maded such that chunk doesnt need to be saved in parts
2018-10-21 07:42:07 +05:30
Vivek Teega
d418976f5e GUI changes on KIVY 2018-10-18 00:19:37 +05:30
Vivek Teega
91273f7114 Adding FLO data support
1. All the GUI changes for the QT version of the wallet
2. Saving flodata locally in the wallet
2018-09-29 23:09:21 +05:30
Vivek Teega
4b1b6e3adf Renaming everything from FloData to txcomment 2018-09-29 18:20:06 +05:30
Vivek Teega
6a2d2fbc3d Creating Morph branch
This is an attempt to commit the changes made to Electrum-BTC to morph it into FLO, on top of the latest Electrum-BTC codebase at the time of writing. The code has become messy pulling upstream changes and its become difficult to debug issues
2018-09-29 17:52:30 +05:30
Vivek Teega
826a56311c
Merge pull request #7 from spesmilo/master
Pulling upstream changes
2018-09-28 18:02:50 +05:30
Vivek Teega
2ce11fa83b
Merge pull request #6 from ranchimall/updateFork
Pulling upstream data
2018-09-23 22:04:46 +05:30
Vivek Teega
4253dd27eb Fixing upstream merge conflicts 2018-09-23 22:02:05 +05:30
Vivek Teega
f2e93e323a Fixing target calculation after changes from the upstream 2018-09-12 06:39:04 +05:30
Vivek Teega
9e0777a7b4 Solved upstream merge conflicts 2018-09-12 03:38:43 +05:30
Vivek Teega
278a6bcb4d
Merge pull request #3 from ranchimall/txcomment
Updating further changes to master branch
2018-09-11 00:14:45 +05:30
Vivek Teega
66777d4566 FLO data box moved up 2018-09-10 11:27:27 +05:30
Vivek Teega
d8d2f3329b Save txcomments/floData locally 2018-09-10 11:12:05 +05:30
Vivek Teega
1e6edd0abc Fixing exchanges and block explorer for FLO 2018-08-18 11:39:26 +05:30
Vivek Teega
c0daef2657 More changes to for save_chunk()
save_chunk() required more changes to save the block header chunks properly locally.
2018-08-14 15:10:19 +05:30
Vivek Teegalapally
a64228ba2c Small change to fix save_chunk_part() 2018-08-05 23:21:46 +05:30
Vivek Teegalapally
85bae50e62 Changes for target calculation
All the changes required for target calculation, verification and saving parts of chunks for FLO
@akhil2015

Co-authored-by: Akhil Bharti <akhil2015@users.noreply.github.com>
2018-08-05 23:18:30 +05:30
Vivek Teegalapally
ca30705c69 Commiting comment/floData changes
The changes made in this commit make sure the transaction comments or floData are being sent out of the wallet.
It still has some errors left though, related to verifying SegWit address.
2018-08-05 21:12:18 +05:30
Vivek Teegalapally
95b7aef579 Changing data directories to reflect FLO 2018-08-05 16:18:53 +05:30
Bitspill
e0d33279de Commiting changes made by @bitspill
The following are the changes made by @bitspill to the original repo.
1. Changes to transaction.py to accomodate the txcomment/floData feature
2. Changes in server list
3. Renaming of BTC to FLO
The GUI and code changes required for exchange_rate.py file haven't been implemented. Its an independant module of the wallet, so will be changed later on
2018-08-05 16:10:36 +05:30
Vivek Teega
f008f2e4dc
Merge pull request #2 from spesmilo/master
Merging upstream commits
2018-08-05 14:42:45 +05:30
27 changed files with 378 additions and 4239 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -78,7 +78,8 @@ class AddressSynchronizer(PrintError):
conf=None,
timestamp=timestamp,
txpos=txpos,
header_hash=header_hash)
header_hash=header_hash,
flodata=flodata)
# Transactions pending verification. txid -> tx_height. Access with self.lock.
self.unverified_tx = defaultdict(int)
# true when synchronized
@ -626,6 +627,23 @@ class AddressSynchronizer(PrintError):
# local transaction
return TxMinedInfo(height=TX_HEIGHT_LOCAL, conf=0)
def get_flodata(self, tx_hash: str):
""" Given a transaction, returns flodata """
with self.lock:
if tx_hash in self.verified_tx:
info = self.verified_tx[tx_hash]
flodata = info[4]
return flodata
elif tx_hash in self.unverified_tx:
tx = self.transactions.get(tx_hash)
flodata = tx.flodata[5:]
return flodata
else:
# local transaction
tx = self.transactions.get(tx_hash)
flodata = tx.flodata[5:]
return flodata
def set_up_to_date(self, up_to_date):
with self.lock:
self.up_to_date = up_to_date
@ -775,7 +793,7 @@ class AddressSynchronizer(PrintError):
@with_local_height_cached
def get_addr_balance(self, address):
"""Return the balance of a bitcoin address:
"""Return the balance of a FLO address:
confirmed and matured, unconfirmed, unmatured
"""
received, sent = self.get_addr_io(address)

View File

@ -31,9 +31,16 @@ from . import constants
from .util import bfh, bh2u
from .simple_config import SimpleConfig
try:
import scrypt
getPoWHash = lambda x: scrypt.hash(x, x, N=1024, r=1, p=1, buflen=32)
except ImportError:
util.print_msg("Warning: package scrypt not available; synchronization could be very slow")
from .scrypt import scrypt_1024_1_1_80 as getPoWHash
HEADER_SIZE = 80 # bytes
MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
MAX_TARGET = 0x00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
class MissingHeader(Exception):
@ -254,34 +261,70 @@ class Blockchain(util.PrintError):
@classmethod
def verify_header(cls, header: dict, prev_hash: str, target: int, expected_header_hash: str=None) -> None:
_hash = hash_header(header)
_powhash = pow_hash_header(header)
if expected_header_hash and expected_header_hash != _hash:
raise Exception("hash mismatches with expected: {} vs {}".format(expected_header_hash, _hash))
if prev_hash != header.get('prev_block_hash'):
raise Exception("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
if constants.net.TESTNET:
return
bits = cls.target_to_bits(target)
#bits = cls.target_to_bits(target)
bits = target
if bits != header.get('bits'):
raise Exception("bits mismatch: %s vs %s" % (bits, header.get('bits')))
block_hash_as_num = int.from_bytes(bfh(_hash), byteorder='big')
if block_hash_as_num > target:
raise Exception(f"insufficient proof of work: {block_hash_as_num} vs target {target}")
block_hash = int('0x' + _hash, 16)
target_val = self.bits_to_target(bits)
if int('0x' + _powhash, 16) > target_val:
raise Exception("insufficient proof of work: %s vs target %s" % (int('0x' + _hash, 16), target_val))
def verify_chunk(self, index: int, data: bytes) -> None:
num = len(data) // HEADER_SIZE
start_height = index * 2016
prev_hash = self.get_hash(start_height - 1)
target = self.get_target(index-1)
current_header = index * 2016
prev_hash = self.get_hash(current_header - 1)
headerLast = None
headerFirst = None
capture = None
lst = []
for i in range(num):
height = start_height + i
try:
expected_header_hash = self.get_hash(height)
except MissingHeader:
expected_header_hash = None
raw_header = data[i*HEADER_SIZE : (i+1)*HEADER_SIZE]
header = deserialize_header(raw_header, index*2016 + i)
self.verify_header(header, prev_hash, target, expected_header_hash)
prev_hash = hash_header(header)
averaging_interval = self.AveragingInterval(current_header)
difficulty_interval = self.DifficultyAdjustmentInterval(current_header)
if current_header < 426000:
target = self.get_target(current_header - 1, headerLast, headerFirst)
try:
expected_header_hash = self.get_hash(current_header)
except MissingHeader:
expected_header_hash = None
raw_header = data[i*HEADER_SIZE : (i+1)*HEADER_SIZE]
header = deserialize_header(raw_header, current_header)
self.verify_header(header, prev_hash, target, expected_header_hash)
prev_hash = hash_header(header)
headerLast = header
if current_header == 0:
headerFirst = header
elif (current_header + averaging_interval + 1) % difficulty_interval == 0:
capture = header
if current_header != 0 and current_header % difficulty_interval == 0:
headerFirst = capture
if current_header >= 425993:
lst.append(headerLast)
current_header = current_header + 1
else:
if len(lst)>6:
headerFirst = lst[0]
target = self.get_target(current_header - 1, headerLast, headerFirst)
try:
expected_header_hash = self.get_hash(current_header)
except MissingHeader:
expected_header_hash = None
raw_header = data[i * HEADER_SIZE: (i + 1) * HEADER_SIZE]
header = deserialize_header(raw_header, current_header)
self.verify_header(header, prev_hash, target, expected_header_hash)
prev_hash = hash_header(header)
headerLast = header
lst.append(header)
if len(lst)>7:
lst.pop(0)
current_header = current_header + 1
@with_lock
def path(self):
@ -446,30 +489,60 @@ class Blockchain(util.PrintError):
raise MissingHeader(height)
return hash_header(header)
def get_target(self, index: int) -> int:
def get_target(self, index: int, headerLast: dict=None, headerFirst: dict=None) -> int:
# compute target from chunk x, used in chunk x+1
if constants.net.TESTNET:
return 0
if index == -1:
return MAX_TARGET
# The range is first 90 blocks because FLO's block time was 90 blocks when it started
if -1 <= index <= 88:
return 0x1e0ffff0
if index < len(self.checkpoints):
h, t = self.checkpoints[index]
return t
# new target
first = self.read_header(index * 2016)
last = self.read_header(index * 2016 + 2015)
if not first or not last:
raise MissingHeader()
bits = last.get('bits')
target = self.bits_to_target(bits)
nActualTimespan = last.get('timestamp') - first.get('timestamp')
nTargetTimespan = 14 * 24 * 60 * 60
nActualTimespan = max(nActualTimespan, nTargetTimespan // 4)
nActualTimespan = min(nActualTimespan, nTargetTimespan * 4)
new_target = min(MAX_TARGET, (target * nActualTimespan) // nTargetTimespan)
# not any target can be represented in 32 bits:
new_target = self.bits_to_target(self.target_to_bits(new_target))
return new_target
if headerLast is None:
headerLast = self.read_header(index)
height = headerLast["block_height"]
# check if the height passes is in range for retargeting
if (height + 1) % self.DifficultyAdjustmentInterval(height + 1) != 0:
return int(headerLast["bits"])
if headerFirst is None:
averagingInterval = self.AveragingInterval(height + 1)
blockstogoback = averagingInterval - 1
# print("Blocks to go back = " + str(blockstogoback))
if (height + 1) != averagingInterval:
blockstogoback = averagingInterval
firstHeight = height - blockstogoback
headerFirst = self.read_header(int(firstHeight))
firstBlockTime = headerFirst["timestamp"]
nMinActualTimespan = int(self.MinActualTimespan(int(headerLast["block_height"]) + 1))
nMaxActualTimespan = int(self.MaxActualTimespan(int(headerLast["block_height"]) + 1))
# Limit adjustment step
nActualTimespan = headerLast["timestamp"] - firstBlockTime
if nActualTimespan < nMinActualTimespan:
nActualTimespan = nMinActualTimespan
if nActualTimespan > nMaxActualTimespan:
nActualTimespan = nMaxActualTimespan
# Retarget
bnNewBits = int(headerLast["bits"])
bnNew = self.bits_to_target(bnNewBits)
bnOld = bnNew
# FLO: intermediate uint256 can overflow by 1 bit
# const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
fShift = bnNew > MAX_TARGET - 1
if (fShift):
bnNew = bnNew >> 1
bnNew = bnNew * nActualTimespan
bnNew = bnNew / self.TargetTimespan(headerLast["block_height"] + 1)
if fShift:
bnNew = bnNew << 1
if bnNew > MAX_TARGET:
bnNew = MAX_TARGET
bnNew = self.target_to_bits(int(bnNew))
return bnNew
@classmethod
def bits_to_target(cls, bits: int) -> int:
@ -542,7 +615,7 @@ class Blockchain(util.PrintError):
if prev_hash != header.get('prev_block_hash'):
return False
try:
target = self.get_target(height // 2016 - 1)
target = self.get_target(height - 1)
except MissingHeader:
return False
try:
@ -573,6 +646,61 @@ class Blockchain(util.PrintError):
cp.append((h, target))
return cp
def AveragingInterval(self, height):
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return constants.net.nAveragingInterval_Version1
# V2
elif height < constants.net.nHeight_Difficulty_Version3:
return constants.net.nAveragingInterval_Version2
# V3
else:
return constants.net.nAveragingInterval_Version3
def MinActualTimespan(self, height):
averagingTargetTimespan = self.AveragingInterval(height) * constants.net.nPowTargetSpacing
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return int(averagingTargetTimespan * (100 - constants.net.nMaxAdjustUp_Version1) / 100)
# V2
elif height < constants.net.nHeight_Difficulty_Version3:
return int(averagingTargetTimespan * (100 - constants.net.nMaxAdjustUp_Version2) / 100)
# V3
else:
return int(averagingTargetTimespan * (100 - constants.net.nMaxAdjustUp_Version3) / 100)
def MaxActualTimespan(self, height):
averagingTargetTimespan = self.AveragingInterval(height) * constants.net.nPowTargetSpacing
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return int(averagingTargetTimespan * (100 + constants.net.nMaxAdjustDown_Version1) / 100)
# V2
elif height < constants.net.nHeight_Difficulty_Version3:
return int(averagingTargetTimespan * (100 + constants.net.nMaxAdjustDown_Version2) / 100)
# V3
else:
return int(averagingTargetTimespan * (100 + constants.net.nMaxAdjustDown_Version3) / 100)
def TargetTimespan(self, height):
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return constants.net.nTargetTimespan_Version1
# V2
if height < constants.net.nHeight_Difficulty_Version3:
return constants.net.nAveragingInterval_Version2 * constants.net.nPowTargetSpacing
# V3
return constants.net.nAveragingInterval_Version3 * constants.net.nPowTargetSpacing
def DifficultyAdjustmentInterval(self, height):
# V1
if height < constants.net.nHeight_Difficulty_Version2:
return constants.net.nInterval_Version1
# V2
if height < constants.net.nHeight_Difficulty_Version3:
return constants.net.nInterval_Version2
# V3
return constants.net.nInterval_Version3
def check_header(header: dict) -> Optional[Blockchain]:
if type(header) is not dict:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -47,59 +47,104 @@ class AbstractNet:
class BitcoinMainnet(AbstractNet):
TESTNET = False
WIF_PREFIX = 0x80
ADDRTYPE_P2PKH = 0
ADDRTYPE_P2SH = 5
SEGWIT_HRP = "bc"
GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
WIF_PREFIX = 0xa3
ADDRTYPE_P2PKH = 35
ADDRTYPE_P2SH = 8
SEGWIT_HRP = "ltc"
GENESIS = "09c7781c9df90708e278c35d38ea5c9041d7ecfcdd1c56ba67274b7cff3e1cea"
DEFAULT_PORTS = {'t': '50001', 's': '50002'}
DEFAULT_SERVERS = read_json('servers.json', {})
CHECKPOINTS = read_json('checkpoints.json', [])
XPRV_HEADERS = {
'standard': 0x0488ade4, # xprv
'standard': 0x01343c31, # xprv
'p2wpkh-p2sh': 0x049d7878, # yprv
'p2wsh-p2sh': 0x0295b005, # Yprv
'p2wpkh': 0x04b2430c, # zprv
'p2wsh': 0x02aa7a99, # Zprv
}
XPUB_HEADERS = {
'standard': 0x0488b21e, # xpub
'standard': 0x0134406b, # xpub
'p2wpkh-p2sh': 0x049d7cb2, # ypub
'p2wsh-p2sh': 0x0295b43f, # Ypub
'p2wpkh': 0x04b24746, # zpub
'p2wsh': 0x02aa7ed3, # Zpub
}
BIP44_COIN_TYPE = 0
BIP44_COIN_TYPE = 2
# FLO Network constants
fPowAllowMinDifficultyBlocks = False
fPowNoRetargeting = False
nRuleChangeActivationThreshold = 6048 # 75% of 8064
nMinerConfirmationWindow = 8064
# Difficulty adjustments
nPowTargetSpacing = 40 # 40s block time
# V1
nTargetTimespan_Version1 = 60 * 60
nInterval_Version1 = nTargetTimespan_Version1 / nPowTargetSpacing
nMaxAdjustUp_Version1 = 75
nMaxAdjustDown_Version1 = 300
nAveragingInterval_Version1 = nInterval_Version1
# V2
nHeight_Difficulty_Version2 = 208440
nInterval_Version2 = 15
nMaxAdjustDown_Version2 = 300
nMaxAdjustUp_Version2 = 75
nAveragingInterval_Version2 = nInterval_Version2
# V3
nHeight_Difficulty_Version3 = 426000
nInterval_Version3 = 1
nMaxAdjustDown_Version3 = 3
nMaxAdjustUp_Version3 = 2
nAveragingInterval_Version3 = 6
class BitcoinTestnet(AbstractNet):
TESTNET = True
WIF_PREFIX = 0xef
ADDRTYPE_P2PKH = 111
ADDRTYPE_P2SH = 196
SEGWIT_HRP = "tb"
GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
ADDRTYPE_P2PKH = 115
ADDRTYPE_P2SH = 198
SEGWIT_HRP = "tltc"
GENESIS = "9b7bc86236c34b5e3a39367c036b7fe8807a966c22a7a1f0da2a198a27e03731"
DEFAULT_PORTS = {'t': '51001', 's': '51002'}
DEFAULT_SERVERS = read_json('servers_testnet.json', {})
CHECKPOINTS = read_json('checkpoints_testnet.json', [])
XPRV_HEADERS = {
'standard': 0x04358394, # tprv
'standard': 0x01343c23, # tprv
'p2wpkh-p2sh': 0x044a4e28, # uprv
'p2wsh-p2sh': 0x024285b5, # Uprv
'p2wpkh': 0x045f18bc, # vprv
'p2wsh': 0x02575048, # Vprv
}
XPUB_HEADERS = {
'standard': 0x043587cf, # tpub
'standard': 0x013440e2, # tpub
'p2wpkh-p2sh': 0x044a5262, # upub
'p2wsh-p2sh': 0x024289ef, # Upub
'p2wpkh': 0x045f1cf6, # vpub
'p2wsh': 0x02575483, # Vpub
}
BIP44_COIN_TYPE = 1
#Difficulty adjustments
nPowTargetSpacing = 40 # 40 block time
# V1
nTargetTimespan_Version1 = 60 * 60
nInterval_Version1 = nTargetTimespan_Version1 / nPowTargetSpacing;
nMaxAdjustUp_Version1 = 75
nMaxAdjustDown_Version1 = 300
nAveragingInterval_Version1 = nInterval_Version1
# V2
nHeight_Difficulty_Version2 = 50000
nInterval_Version2 = 15
nMaxAdjustDown_Version2 = 300
nMaxAdjustUp_Version2 = 75
nAveragingInterval_Version2 = nInterval_Version2
# V3
nHeight_Difficulty_Version3 = 60000
nInterval_Version3 = 1
nMaxAdjustDown_Version3 = 3
nMaxAdjustUp_Version3 = 2
nAveragingInterval_Version3 = 6
class BitcoinRegtest(BitcoinTestnet):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -941,6 +941,16 @@ class ElectrumWindow(App):
d = LabelDialog(_('Enter description'), text, callback)
d.open()
def flodata_dialog(self, screen):
from .uix.dialogs.label_dialog import LabelDialog
text = screen.flodata
def callback(text):
screen.flodata = text
d = LabelDialog(_('Enter FLO data'), text, callback)
d.open()
def amount_dialog(self, screen, show_max):
from .uix.dialogs.amount_dialog import AmountDialog
amount = screen.amount
@ -990,6 +1000,7 @@ class ElectrumWindow(App):
def on_fee(self, event, *arg):
self.fee_status = self.electrum_config.get_fee_status()
def protected(self, msg, f, args):
if self.wallet.has_password():
on_success = lambda pw: f(*(args + (pw,)))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -177,7 +177,7 @@ class SendScreen(CScreen):
try:
uri = electrum.util.parse_URI(text, self.app.on_pr)
except:
self.app.show_info(_("Not a Bitcoin URI"))
self.app.show_info(_("Not a FLO URI"))
return
amount = uri.get('amount')
self.screen.address = uri.get('address', '')
@ -185,6 +185,7 @@ class SendScreen(CScreen):
self.screen.amount = self.app.format_amount_and_units(amount) if amount else ''
self.payment_request = None
self.screen.is_pr = False
self.screen.flodata = uri.get('flodata', '')
def update(self):
if self.app.wallet and self.payment_request_queued:
@ -197,6 +198,7 @@ class SendScreen(CScreen):
self.screen.address = ''
self.payment_request = None
self.screen.is_pr = False
self.screen.flodata = ''
def set_request(self, pr):
self.screen.address = pr.get_requestor()
@ -248,10 +250,10 @@ class SendScreen(CScreen):
else:
address = str(self.screen.address)
if not address:
self.app.show_error(_('Recipient not specified.') + ' ' + _('Please scan a Bitcoin address or a payment request'))
self.app.show_error(_('Recipient not specified.') + ' ' + _('Please scan a FLO address or a payment request'))
return
if not bitcoin.is_address(address):
self.app.show_error(_('Invalid Bitcoin Address') + ':\n' + address)
self.app.show_error(_('Invalid FLO Address') + ':\n' + address)
return
try:
amount = self.app.get_amount(self.screen.amount)
@ -261,19 +263,20 @@ class SendScreen(CScreen):
outputs = [TxOutput(bitcoin.TYPE_ADDRESS, address, amount)]
message = self.screen.message
amount = sum(map(lambda x:x[2], outputs))
flodata = str(self.screen.flodata)
if self.app.electrum_config.get('use_rbf'):
from .dialogs.question import Question
d = Question(_('Should this transaction be replaceable?'), lambda b: self._do_send(amount, message, outputs, b))
d = Question(_('Should this transaction be replaceable?'), lambda b: self._do_send(amount, message, outputs, b, flodata))
d.open()
else:
self._do_send(amount, message, outputs, False)
self._do_send(amount, message, outputs, False, flodata)
def _do_send(self, amount, message, outputs, rbf):
def _do_send(self, amount, message, outputs, rbf, flodata):
# make unsigned transaction
config = self.app.electrum_config
coins = self.app.wallet.get_spendable_coins(None, config)
try:
tx = self.app.wallet.make_unsigned_transaction(coins, outputs, config, None)
tx = self.app.wallet.make_unsigned_transaction(coins, outputs, config, None, flodata=flodata)
except NotEnoughFunds:
self.app.show_error(_("Not enough funds"))
return
@ -379,7 +382,7 @@ class ReceiveScreen(CScreen):
def do_share(self):
uri = self.get_URI()
self.app.do_share(uri, _("Share Bitcoin Request"))
self.app.do_share(uri, _("Share FLO Request"))
def do_copy(self):
uri = self.get_URI()

View File

@ -11,6 +11,7 @@ SendScreen:
address: ''
amount: ''
message: ''
flodata: ''
is_pr: False
BoxLayout
padding: '12dp', '12dp', '12dp', '12dp'
@ -38,6 +39,24 @@ SendScreen:
CardSeparator:
opacity: int(not root.is_pr)
color: blue_bottom.foreground_color
BoxLayout:
id: flodata_selection
size_hint: 1, None
height: blue_bottom.item_height
spacing: '5dp'
Image:
source: 'atlas://electrum/gui/kivy/theming/light/pen'
size_hint: None, None
size: '22dp', '22dp'
pos_hint: {'center_y': .5}
BlueButton:
id: flodata
text: s.flodata if s.flodata else (_('No FLO data') if root.is_pr else _('FLO data'))
disabled: root.is_pr
on_release: Clock.schedule_once(lambda dt: app.flodata_dialog(s))
CardSeparator:
opacity: int(not root.is_pr)
color: blue_bottom.foreground_color
BoxLayout:
size_hint: 1, None
height: blue_bottom.item_height

View File

@ -459,13 +459,13 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
grid = QGridLayout()
grid.addWidget(QLabel(_("Start")), 0, 0)
grid.addWidget(QLabel(self.format_date(start_date)), 0, 1)
grid.addWidget(QLabel(str(h.get('start_fiat_value')) + '/BTC'), 0, 2)
grid.addWidget(QLabel(str(h.get('start_fiat_value')) + '/FLO'), 0, 2)
grid.addWidget(QLabel(_("Initial balance")), 1, 0)
grid.addWidget(QLabel(format_amount(h['start_balance'])), 1, 1)
grid.addWidget(QLabel(str(h.get('start_fiat_balance'))), 1, 2)
grid.addWidget(QLabel(_("End")), 2, 0)
grid.addWidget(QLabel(self.format_date(end_date)), 2, 1)
grid.addWidget(QLabel(str(h.get('end_fiat_value')) + '/BTC'), 2, 2)
grid.addWidget(QLabel(str(h.get('end_fiat_value')) + '/FLO'), 2, 2)
grid.addWidget(QLabel(_("Final balance")), 4, 0)
grid.addWidget(QLabel(format_amount(h['end_balance'])), 4, 1)
grid.addWidget(QLabel(str(h.get('end_fiat_balance'))), 4, 2)

View File

@ -433,8 +433,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
if self.wallet.is_watching_only():
msg = ' '.join([
_("This wallet is watching-only."),
_("This means you will not be able to spend Bitcoins with it."),
_("Make sure you own the seed phrase or the private keys, before you request Bitcoins to be sent to this wallet.")
_("This means you will not be able to spend FLO with it."),
_("Make sure you own the seed phrase or the private keys, before you request FLO to be sent to this wallet.")
])
self.show_warning(msg, title=_('Information'))
@ -596,11 +596,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def show_about(self):
QMessageBox.about(self, "Electrum",
(_("Version")+" %s" % ELECTRUM_VERSION + "\n\n" +
_("Electrum's focus is speed, with low resource usage and simplifying Bitcoin.") + " " +
_("Electrum's focus is speed, with low resource usage and simplifying FLO.") + " " +
_("You do not need to perform regular backups, because your wallet can be "
"recovered from a secret phrase that you can memorize or write on paper.") + " " +
_("Startup times are instant because it operates in conjunction with high-performance "
"servers that handle the most complicated parts of the Bitcoin system.") + "\n\n" +
"servers that handle the most complicated parts of the FLO system.") + "\n\n" +
_("Uses icons from the Icons8 icon pack (icons8.com).")))
def show_report_bug(self):
@ -835,7 +835,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.receive_address_e = ButtonsLineEdit()
self.receive_address_e.addCopyButton(self.app)
self.receive_address_e.setReadOnly(True)
msg = _('Bitcoin address where the payment should be received. Note that each payment request uses a different Bitcoin address.')
msg = _('FLO address where the payment should be received. Note that each payment request uses a different Bitcoin address.')
self.receive_address_label = HelpLabel(_('Receiving address'), msg)
self.receive_address_e.textChanged.connect(self.update_receive_qr)
self.receive_address_e.setFocusPolicy(Qt.ClickFocus)
@ -865,8 +865,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
msg = ' '.join([
_('Expiration date of your request.'),
_('This information is seen by the recipient if you send them a signed payment request.'),
_('Expired requests have to be deleted manually from your list, in order to free the corresponding Bitcoin addresses.'),
_('The bitcoin address never expires and will always be part of this electrum wallet.'),
_('Expired requests have to be deleted manually from your list, in order to free the corresponding FLO addresses.'),
_('The FLO address never expires and will always be part of this electrum wallet.'),
])
grid.addWidget(HelpLabel(_('Request expires'), msg), 3, 0)
grid.addWidget(self.expires_combo, 3, 1)
@ -1096,7 +1096,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.amount_e = BTCAmountEdit(self.get_decimal_point)
self.payto_e = PayToEdit(self)
msg = _('Recipient of the funds.') + '\n\n'\
+ _('You may enter a Bitcoin address, a label from your list of contacts (a list of completions will be proposed), or an alias (email-like address that forwards to a Bitcoin address)')
+ _('You may enter a FLO address, a label from your list of contacts (a list of completions will be proposed), or an alias (email-like address that forwards to a Bitcoin address)')
payto_label = HelpLabel(_('Pay to'), msg)
grid.addWidget(payto_label, 1, 0)
grid.addWidget(self.payto_e, 1, 1, 1, -1)
@ -1113,10 +1113,16 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.message_e = MyLineEdit()
grid.addWidget(self.message_e, 2, 1, 1, -1)
msg = _('This is where you write the FLO Data for the transaction')
flodata_label = HelpLabel(_('FLO Data'), msg)
grid.addWidget(flodata_label, 3, 0)
self.message_tx_e = MyLineEdit()
grid.addWidget(self.message_tx_e, 3, 1, 1, -1)
self.from_label = QLabel(_('From'))
grid.addWidget(self.from_label, 3, 0)
grid.addWidget(self.from_label, 4, 0)
self.from_list = FromList(self, self.from_list_menu)
grid.addWidget(self.from_list, 3, 1, 1, -1)
grid.addWidget(self.from_list, 4, 1, 1, -1)
self.set_pay_from([])
msg = _('Amount to be sent.') + '\n\n' \
@ -1124,24 +1130,24 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
+ _('Note that if you have frozen some of your addresses, the available funds will be lower than your total balance.') + '\n\n' \
+ _('Keyboard shortcut: type "!" to send all your coins.')
amount_label = HelpLabel(_('Amount'), msg)
grid.addWidget(amount_label, 4, 0)
grid.addWidget(self.amount_e, 4, 1)
grid.addWidget(amount_label, 5, 0)
grid.addWidget(self.amount_e, 5, 1)
self.fiat_send_e = AmountEdit(self.fx.get_currency if self.fx else '')
if not self.fx or not self.fx.is_enabled():
self.fiat_send_e.setVisible(False)
grid.addWidget(self.fiat_send_e, 4, 2)
grid.addWidget(self.fiat_send_e, 5, 2)
self.amount_e.frozen.connect(
lambda: self.fiat_send_e.setFrozen(self.amount_e.isReadOnly()))
self.max_button = EnterButton(_("Max"), self.spend_max)
self.max_button.setFixedWidth(140)
grid.addWidget(self.max_button, 4, 3)
grid.addWidget(self.max_button, 5, 3)
hbox = QHBoxLayout()
hbox.addStretch(1)
grid.addLayout(hbox, 4, 4)
grid.addLayout(hbox, 5, 4)
msg = _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
msg = _('FLO transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
+ _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
+ _('A suggested fee is automatically added to this field. You may override it. The suggested fee increases with the size of the transaction.')
self.fee_e_label = HelpLabel(_('Fee'), msg)
@ -1221,7 +1227,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
vbox_feelabel = QVBoxLayout()
vbox_feelabel.addWidget(self.fee_e_label)
vbox_feelabel.addStretch(1)
grid.addLayout(vbox_feelabel, 5, 0)
grid.addLayout(vbox_feelabel, 6, 0)
self.fee_adv_controls = QWidget()
hbox = QHBoxLayout(self.fee_adv_controls)
@ -1236,7 +1242,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
vbox_feecontrol.addWidget(self.fee_adv_controls)
vbox_feecontrol.addWidget(self.fee_slider)
grid.addLayout(vbox_feecontrol, 5, 1, 1, -1)
grid.addLayout(vbox_feecontrol, 6, 1, 1, -1)
if not self.config.get('show_fee', False):
self.fee_adv_controls.setVisible(False)
@ -1250,7 +1256,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
buttons.addWidget(self.clear_button)
buttons.addWidget(self.preview_button)
buttons.addWidget(self.send_button)
grid.addLayout(buttons, 6, 1, 1, 3)
grid.addLayout(buttons, 7, 1, 1, 3)
self.amount_e.shortcut.connect(self.spend_max)
self.payto_e.textChanged.connect(self.update_fee)
@ -1508,6 +1514,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.show_error(_('Payment request has expired'))
return
label = self.message_e.text()
flodata = self.message_tx_e.text()
if self.payment_request:
outputs = self.payment_request.get_outputs()
@ -1532,7 +1539,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
for o in outputs:
if o.address is None:
self.show_error(_('Bitcoin Address is None'))
self.show_error(_('FLO Address is None'))
return
if o.type == TYPE_ADDRESS and not bitcoin.is_address(o.address):
self.show_error(_('Invalid Bitcoin Address'))
@ -1543,7 +1550,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
fee_estimator = self.get_send_fee_estimator()
coins = self.get_coins()
return outputs, fee_estimator, label, coins
return outputs, fee_estimator, label, coins, flodata
def do_preview(self):
self.do_send(preview = True)
@ -1554,12 +1561,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
r = self.read_send_tab()
if not r:
return
outputs, fee_estimator, tx_desc, coins = r
outputs, fee_estimator, tx_desc, coins, flodata = r
try:
is_sweep = bool(self.tx_external_keypairs)
tx = self.wallet.make_unsigned_transaction(
coins, outputs, self.config, fixed_fee=fee_estimator,
is_sweep=is_sweep)
is_sweep=is_sweep, flodata=flodata)
except (NotEnoughFunds, NoDynamicFeeEstimates) as e:
self.show_message(str(e))
return
@ -1792,7 +1799,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.not_enough_funds = False
self.payment_request = None
self.payto_e.is_pr = False
for e in [self.payto_e, self.message_e, self.amount_e, self.fiat_send_e,
for e in [self.payto_e, self.message_e, self.message_tx_e, self.amount_e, self.fiat_send_e,
self.fee_e, self.feerate_e]:
e.setText('')
e.setFrozen(False)

View File

@ -114,6 +114,8 @@ class TxDialog(QDialog, MessageBoxMixin):
vbox.addWidget(self.size_label)
self.fee_label = QLabel()
vbox.addWidget(self.fee_label)
self.flodata_label = QLabel()
vbox.addWidget(self.flodata_label)
self.add_io(vbox)
@ -271,6 +273,7 @@ class TxDialog(QDialog, MessageBoxMixin):
self.amount_label.setText(amount_str)
self.fee_label.setText(fee_str)
self.size_label.setText(size_str)
self.flodata_label.setText("FLO data: " + self.tx.flodata)
run_hook('transaction_dialog_update', self)
def add_io(self, vbox):

View File

@ -337,7 +337,7 @@ class ElectrumGui:
def do_send(self):
if not is_address(self.str_recipient):
self.show_message(_('Invalid Bitcoin address'))
self.show_message(_('Invalid FLO address'))
return
try:
amount = int(Decimal(self.str_amount) * COIN)

View File

@ -37,7 +37,7 @@ def plot_history(history):
plt.subplots_adjust(bottom=0.2)
plt.xticks( rotation=25 )
ax = plt.gca()
plt.ylabel('BTC')
plt.ylabel('FLO')
plt.xlabel('Month')
xfmt = md.DateFormatter('%Y-%m-%d')
ax.xaxis.set_major_formatter(xfmt)

View File

@ -490,9 +490,9 @@ class DeviceMgr(ThreadJob, PrintError):
# or it is not pairable
raise DeviceUnpairableError(
_('Electrum cannot pair with your {}.\n\n'
'Before you request bitcoins to be sent to addresses in this '
'Before you request FLO to be sent to addresses in this '
'wallet, ensure you can pair with your device, or that you have '
'its seed (and passphrase, if any). Otherwise all bitcoins you '
'its seed (and passphrase, if any). Otherwise all FLO you '
'receive will be unspendable.').format(plugin.device))
def unpaired_device_infos(self, handler, plugin: 'HW_PluginBase', devices=None):

View File

@ -1,409 +1,5 @@
{
"3smoooajg7qqac2y.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"81-7-10-251.blue.kundencontroller.de": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"E-X.not.fyi": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"MEADS.hopto.org": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"VPS.hsmiths.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"b.ooze.cc": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"bauerjda5hnedjam.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"bauerjhejlv6di7s.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"bitcoin.corgi.party": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"bitcoin3nqy3db7c.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"bitcoins.sk": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"btc.cihar.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"btc.smsys.me": {
"pruning": "-",
"s": "995",
"version": "1.4"
},
"btc.xskyx.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"cashyes.zapto.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"currentlane.lovebitco.in": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"daedalus.bauerj.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"dedi.jochen-hoenicke.de": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"dragon085.startdedicated.de": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"e-1.claudioboxx.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"e.keff.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"elec.luggs.co": {
"pruning": "-",
"s": "443",
"version": "1.4"
},
"electrum-server.ninja": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum-unlimited.criptolayer.net": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"electrum.eff.ro": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum.festivaldelhumor.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum.hsmiths.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum.leblancnet.us": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum.mindspot.org": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"electrum.qtornado.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum.taborsky.cz": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"electrum.villocq.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum2.eff.ro": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum2.villocq.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrum3.hachre.de": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrumx.bot.nu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrumx.ddns.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrumx.ftp.sh": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"electrumx.ml": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrumx.nmdps.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrumx.soon.it": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"electrumxhqdsmlu.onion": {
"pruning": "-",
"t": "50001",
"version": "1.4"
},
"elx01.knas.systems": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"enode.duckdns.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"erbium1.sytes.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"fedaykin.goip.de": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"fn.48.org": {
"pruning": "-",
"s": "50002",
"t": "50003",
"version": "1.4"
},
"helicarrier.bauerj.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"hsmiths4fyqlw5xw.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"hsmiths5mjk6uijs.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"icarus.tetradrachm.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"kirsche.emzy.de": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"luggscoqbymhvnkp.onion": {
"pruning": "-",
"t": "80",
"version": "1.4"
},
"ndnd.selfhost.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"ndndword5lpb7eex.onion": {
"pruning": "-",
"t": "50001",
"version": "1.4"
},
"oneweek.duckdns.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"orannis.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"ozahtqwp25chjdjd.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"qtornadoklbgdyww.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"rbx.curalle.ovh": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"s7clinmo4cazmhul.onion": {
"pruning": "-",
"t": "50001",
"version": "1.4"
},
"tardis.bauerj.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"technetium.network": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"tomscryptos.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"ulrichard.ch": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"us.electrum.be": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"vmd27610.contaboserver.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"vmd30612.contaboserver.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"wsw6tua3xl24gsmi264zaep6seppjyrkyucpsmuxnjzyt3f3j6swshad.onion": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.4"
},
"xray587.startdedicated.de": {
"pruning": "-",
"s": "50002",
"version": "1.4"
},
"yuio.top": {
"localhost": {
"pruning": "-",
"s": "50002",
"t": "50001",

View File

@ -202,7 +202,7 @@ class SimpleConfig(PrintError):
base_unit = self.user_config.get('base_unit')
if isinstance(base_unit, str):
self._set_key_in_user_config('base_unit', None)
map_ = {'btc':8, 'mbtc':5, 'ubtc':2, 'bits':2, 'sat':0}
map_ = {'flo':8, 'mflo':5, 'uflo':2, 'bits':2, 'sat':0}
decimal_point = map_.get(base_unit.lower())
self._set_key_in_user_config('decimal_point', decimal_point)

View File

@ -27,6 +27,8 @@
# Note: The deserialization code originally comes from ABE.
import codecs
import struct
import traceback
import sys
@ -642,6 +644,10 @@ def deserialize(raw: str, force_full_parse=False) -> dict:
txin = d['inputs'][i]
parse_witness(vds, txin, full_parse=full_parse)
d['lockTime'] = vds.read_uint32()
if d['version'] >= 2:
d['flodata'] = vds.read_string()
else:
d['flodata'] = ""
if vds.can_read_more():
raise SerializationError('extra junk at the end')
return d
@ -679,6 +685,7 @@ class Transaction:
self._inputs = None
self._outputs = None # type: List[TxOutput]
self.locktime = 0
self.flodata = ""
self.version = 1
# by default we assume this is a partial txn;
# this value will get properly set when deserializing
@ -782,16 +789,18 @@ class Transaction:
self._outputs = [TxOutput(x['type'], x['address'], x['value']) for x in d['outputs']]
self.locktime = d['lockTime']
self.version = d['version']
self.flodata = d['flodata']
self.is_partial_originally = d['partial']
self._segwit_ser = d['segwit_ser']
return d
@classmethod
def from_io(klass, inputs, outputs, locktime=0):
def from_io(klass, inputs, outputs, locktime=0, flodata=""):
self = klass(None)
self._inputs = inputs
self._outputs = outputs
self.locktime = locktime
self.flodata = flodata
self.BIP69_sort()
return self
@ -1042,6 +1051,8 @@ class Transaction:
nVersion = int_to_hex(self.version, 4)
nHashType = int_to_hex(1, 4)
nLocktime = int_to_hex(self.locktime, 4)
nFloData = var_int(len(self.flodata)) + str(codecs.encode(bytes(self.flodata, 'utf-8'), 'hex_codec'),
'utf-8')
inputs = self.inputs()
outputs = self.outputs()
txin = inputs[i]
@ -1055,11 +1066,17 @@ class Transaction:
scriptCode = var_int(len(preimage_script) // 2) + preimage_script
amount = int_to_hex(txin['value'], 8)
nSequence = int_to_hex(txin.get('sequence', 0xffffffff - 1), 4)
preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nHashType
if self.version >= 2:
preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nFloData + nHashType
else:
preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nHashType
else:
txins = var_int(len(inputs)) + ''.join(self.serialize_input(txin, self.get_preimage_script(txin) if i==k else '') for k, txin in enumerate(inputs))
txouts = var_int(len(outputs)) + ''.join(self.serialize_output(o) for o in outputs)
preimage = nVersion + txins + txouts + nLocktime + nHashType
if self.version >= 2:
preimage = nVersion + txins + txouts + nLocktime + nFloData + nHashType
else:
preimage = nVersion + txins + txouts + nLocktime + nHashType
return preimage
def is_segwit(self, guess_for_address=False):
@ -1081,6 +1098,8 @@ class Transaction:
self.deserialize()
nVersion = int_to_hex(self.version, 4)
nLocktime = int_to_hex(self.locktime, 4)
nFloData = var_int(len(self.flodata)) + str(codecs.encode(bytes(self.flodata, 'utf-8'), 'hex_codec'),
'utf-8')
inputs = self.inputs()
outputs = self.outputs()
txins = var_int(len(inputs)) + ''.join(self.serialize_input(txin, self.input_script(txin, estimate_size)) for txin in inputs)
@ -1093,9 +1112,15 @@ class Transaction:
marker = '00'
flag = '01'
witness = ''.join(self.serialize_witness(x, estimate_size) for x in inputs)
return nVersion + marker + flag + txins + txouts + witness + nLocktime
if self.version >= 2:
return nVersion + marker + flag + txins + txouts + witness + nLocktime + nFloData
else:
return nVersion + marker + flag + txins + txouts + witness + nLocktime
else:
return nVersion + txins + txouts + nLocktime
if self.version >= 2:
return nVersion + txins + txouts + nLocktime + nFloData
else:
return nVersion + txins + txouts + nLocktime
def txid(self):
self.deserialize()

View File

@ -62,11 +62,11 @@ def inv_dict(d):
ca_path = certifi.where()
base_units = {'BTC':8, 'mBTC':5, 'bits':2, 'sat':0}
base_units = {'FLO':8, 'mFLO':5, 'bits':2, 'sat':0}
base_units_inverse = inv_dict(base_units)
base_units_list = ['BTC', 'mBTC', 'bits', 'sat'] # list(dict) does not guarantee order
base_units_list = ['FLO', 'mFLO', 'bits', 'sat'] # list(dict) does not guarantee order
DECIMAL_POINT_DEFAULT = 5 # mBTC
DECIMAL_POINT_DEFAULT = 5 # mFLO
class UnknownBaseUnit(Exception): pass
@ -149,7 +149,7 @@ class Satoshis(object):
return 'Satoshis(%d)'%self.value
def __str__(self):
return format_satoshis(self.value) + " BTC"
return format_satoshis(self.value) + " FLO"
def __eq__(self, other):
return self.value == other.value
@ -715,12 +715,12 @@ def parse_URI(uri: str, on_pr: Callable=None) -> dict:
if ':' not in uri:
if not bitcoin.is_address(uri):
raise Exception("Not a bitcoin address")
raise Exception("Not a FLO address")
return {'address': uri}
u = urllib.parse.urlparse(uri)
if u.scheme != 'bitcoin':
raise Exception("Not a bitcoin URI")
raise Exception("Not a FLO URI")
address = u.path
# python for android fails to parse query
@ -737,7 +737,7 @@ def parse_URI(uri: str, on_pr: Callable=None) -> dict:
out = {k: v[0] for k, v in pq.items()}
if address:
if not bitcoin.is_address(address):
raise Exception("Invalid bitcoin address:" + address)
raise Exception("Invalid FLO address:" + address)
out['address'] = address
if 'amount' in out:
am = out['amount']
@ -786,7 +786,7 @@ def create_URI(addr, amount, message):
query.append('amount=%s'%format_satoshis_plain(amount))
if message:
query.append('message=%s'%urllib.parse.quote(message))
p = urllib.parse.ParseResult(scheme='bitcoin', netloc='', path=addr, params='', query='&'.join(query), fragment='')
p = urllib.parse.ParseResult(scheme='flo', netloc='', path=addr, params='', query='&'.join(query), fragment='')
return urllib.parse.urlunparse(p)
@ -917,6 +917,7 @@ class TxMinedInfo(NamedTuple):
timestamp: Optional[int] = None # timestamp of block that mined tx
txpos: Optional[int] = None # position of tx in serialized block
header_hash: Optional[str] = None # hash of block that mined tx
flodata: Optional[str] = None # flodata of the tx
def make_aiohttp_session(proxy: dict, headers=None, timeout=None):

View File

@ -124,10 +124,12 @@ class SPV(NetworkJobOnDefaultServer):
except KeyError: pass
self.print_error("verified %s" % tx_hash)
header_hash = hash_header(header)
flodata = self.wallet.get_flodata(tx_hash)
tx_info = TxMinedInfo(height=tx_height,
timestamp=header.get('timestamp'),
txpos=pos,
header_hash=header_hash)
header_hash=header_hash,
flodata=flodata)
self.wallet.add_verified_tx(tx_hash, tx_info)
if self.is_up_to_date() and self.wallet.is_up_to_date():
self.wallet.save_verified_tx(write=True)

View File

@ -603,7 +603,7 @@ class Abstract_Wallet(AddressSynchronizer):
return candidate
def make_unsigned_transaction(self, coins, outputs, config, fixed_fee=None,
change_addr=None, is_sweep=False):
change_addr=None, is_sweep=False, flodata=None):
# check outputs
i_max = None
for i, o in enumerate(outputs):
@ -621,6 +621,9 @@ class Abstract_Wallet(AddressSynchronizer):
for item in coins:
self.add_input_info(item)
if flodata is None:
flodata=''
# change address
# if we leave it empty, coin_chooser will set it
change_addrs = []
@ -693,6 +696,10 @@ class Abstract_Wallet(AddressSynchronizer):
# Timelock tx to current height.
tx.locktime = self.get_local_height()
# Transactions with transaction comments/floData are version 2
if flodata != "":
tx.version = 2
tx.flodata = "text:" + flodata
run_hook('make_unsigned_transaction', self, tx)
return tx
@ -962,7 +969,7 @@ class Abstract_Wallet(AddressSynchronizer):
if not r:
return
out = copy.copy(r)
out['URI'] = 'bitcoin:' + addr + '?amount=' + format_satoshis(out.get('amount'))
out['URI'] = 'flo:' + addr + '?amount=' + format_satoshis(out.get('amount'))
status, conf = self.get_request_status(addr)
out['status'] = status
if conf is not None:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 144 KiB