Compare commits

...

56 Commits

Author SHA1 Message Date
Vivek Teega
5d39cce124
Merge pull request #10 from bitspill/master
flo basic trezor support
2019-03-02 01:14:10 +05:30
Jeremiah Buddenhagen
5975c105d6 flo basic trezor support 2019-02-22 13:43:37 -08:00
Vivek Teega
9ab1aab6f8 Upstream Merge 2019-02-16 10:43:21 +05:30
SomberNight
0de954546a
test python version in main script
for better error text on old python

related: #5008, #5129
2019-02-15 22:06:10 +01:00
SomberNight
92bf409bf0
scripts: add "quick_start" to showcase some basic functionality 2019-02-15 21:14:08 +01:00
ThomasV
25a460f855 Merge branch 'network_walk' 2019-02-15 17:37:59 +01:00
ThomasV
52520490c5 improve network send_multiple_requests 2019-02-15 17:37:15 +01:00
SomberNight
5313591c28
synchronizer: disconnect from server if cannot deserialize txn 2019-02-15 17:22:24 +01:00
SomberNight
7b8114f865
synchronizer: allow server not finding txn sometimes
User has wallet file with history that includes some txid; corresponding
raw tx is not in the "transactions" dict in the file however.
When the synchronizer starts up, it requests this "missing" txn from
the server... but what if the server does not know about it?
Maybe it was reorged and is not in the new best chain,
and not even in mempool. This was not handled previously.

fix #5122
2019-02-14 20:54:55 +01:00
ThomasV
f60d1e7cb6 fix variable name in contrib/sign_version 2019-02-13 23:25:58 +01:00
Vivek Teega
8310195453 Update authors & uncomment scrypt checking 2019-01-31 21:02:37 +05:30
Vivek Teega
cdaf874228 Fixing exchanges error 2019-01-31 20:30:57 +05:30
Vivek Teega
1a111b98d4 Minor fixes 2019-01-31 18:50:02 +05:30
Vivek Teega
0cd2f6e170 Merge remote-tracking branch 'upstream/master' into upstreamMerge 2019-01-31 00:41:06 +05:30
Vivek Teega
e89ec0d1d5 GUI and unique identification for packaging 2019-01-30 13:38:16 +00:00
Vivek Teega
eb418f1d3e Scrypt verification 2019-01-29 04:57:29 +00:00
Vivek Teega
e28b8806ac Changes to FLO data scrollview and color of 'blocks' field 2019-01-28 18:35:47 +00:00
Vivek Teega
501371c001 FLO data input widget behavior 2019-01-27 18:50:07 +05:30
Vivek Teega
94b7eb3015 Fix exchanges and GUI quirks 2019-01-26 16:39:54 +05:30
Vivek Teega
1e1caf602d Send tab GUI change 2019-01-26 13:04:01 +05:30
Vivek Teega
ed1421da8e Fix android startup 2019-01-25 14:30:10 +05:30
Vivek Teega
8f6a1880e9 Fix GUI 2019-01-25 11:50:32 +05:30
Vivek Teega
2cda8b266f Merge upstream 2019-01-24 14:00:37 +05:30
Vivek Teega
f66ab7ce57 Adding scrypt modules and their SHA256 hash in build requirements 2019-01-15 07:57:01 +05:30
Vivek Teega
89cc1efc41 Changing default data directory and testing scrypt packages 2019-01-12 18:15:06 +05:30
Vivek Teega
8702ed096b Merge remote-tracking branch 'upstream/master' into upstreamMerge 2019-01-03 18:46:09 +05:30
Vivek Teega
091b8bc73f Fixed upstreamMerge problems 2018-12-26 12:16:14 +05:30
Vivek Teega
cd8be3f586 Pulling upstream changes 2018-12-25 13:47:12 +05:30
Vivek Teega
3c0f05ae93 Replacing scrypt module with hashlib standard library 2018-12-25 13:01:18 +05:30
Vivek Teega
300241428c Sync Upstream
Pulling upstream changes in Electrum BTC and integrating them for FLO
2018-12-21 00:52:07 +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
56 changed files with 73203 additions and 5414 deletions

13
AUTHORS
View File

@ -1,3 +1,5 @@
Electrum-BTC
------------
ThomasV - Creator and maintainer.
Animazing / Tachikoma - Styled the new GUI. Mac version.
Azelphur - GUI stuff.
@ -9,4 +11,13 @@ Genjix - Porting pro-mode functionality to lite-gui and worked on server
Slush - Work on the server. Designed the original Stratum spec.
Julian Toash (Tuxavant) - Various fixes to the client.
rdymac - Website and translations.
kyuupichan - Miscellaneous.
kyuupichan - Miscellaneous.
FLO-Electrum
------------
vivekteega - Maintainer and remaining stuff
Bitspill - Bootstraped the project with core FLO changes
Rohit Tripathy - Ideation and problem solving
akhil2015 - Flodata and scrypt hashing

View File

@ -140,3 +140,16 @@ yarl==1.3.0 \
colorama==0.4.1 \
--hash=sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d \
--hash=sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48
pylibscrypt==1.7.1 \
--hash=sha256:7aa9424e211a12106c67ea884ccfe609856289372b900d3702faaf66e87f79ac
scrypt==0.8.6 \
--hash=sha256:85919f023148cd9fb01d75ad4e3e061928c298fa6249a0cd6cd469c4b947595e \
--hash=sha256:4ad7188f2e42dbee2ff1cd72e3da40b170ba41847effbf0d726444f62ae60f3a \
--hash=sha256:bc131f74a688fa09993c518ca666a2ebd4268b207e039cbab03a034228140d3e \
--hash=sha256:232acdbc3434d2de55def8d5dbf1bc4b9bfc50da7c5741df2a6eebc4e18d3720 \
--hash=sha256:971db040d3963ebe4b919a203fe10d7d6659951d3644066314330983dc175ed4 \
--hash=sha256:475ac80239b3d788ae71a09c3019ca915e149aaa339adcdd1c9eef121293dc88 \
--hash=sha256:18ccbc63d87c6f89b753194194bb37aeaf1abc517e4b989461d115c1d93ce128 \
--hash=sha256:c23daecee405cb036845917295c76f8d747fc890158df40cb304b4b3c3640079 \
--hash=sha256:f8239b2d47fa1d40bc27efd231dc7083695d10c1c2ac51a99380360741e0362d

View File

@ -1,5 +1,5 @@
Cython>=0.27
trezor[hidapi]>=0.11.0
trezor[hidapi]>=0.11.1
safet[hidapi]>=0.1.0
keepkey
btchip-python>=0.1.26

View File

@ -9,3 +9,4 @@ aiorpcx>=0.9,<0.11
aiohttp>=3.3.0
aiohttp_socks
certifi
pylibscrypt==1.7.1

View File

@ -1,4 +1,4 @@
#!/bin/bash
version=`git describe --tags --dirty --always`
version=`python3 -c "import electrum; print(electrum.version.ELECTRUM_VERSION)"`
sig=`./run_electrum -w $SIGNING_WALLET signmessage $SIGNING_ADDRESS $version`
echo "{ \"version\":\"$version\", \"signatures\":{ \"$addr\":\"$sig\"}}"
echo "{ \"version\":\"$version\", \"signatures\":{ \"$SIGNING_ADDRESS\":\"$sig\"}}"

View File

@ -73,12 +73,13 @@ class AddressSynchronizer(PrintError):
# Verified transactions. txid -> TxMinedInfo. Access with self.lock.
verified_tx = storage.get('verified_tx3', {})
self.verified_tx = {} # type: Dict[str, TxMinedInfo]
for txid, (height, timestamp, txpos, header_hash) in verified_tx.items():
for txid, (height, timestamp, txpos, header_hash, flodata) in verified_tx.items():
self.verified_tx[txid] = TxMinedInfo(height=height,
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
@ -454,7 +455,7 @@ class AddressSynchronizer(PrintError):
verified_tx_to_save = {}
for txid, tx_info in self.verified_tx.items():
verified_tx_to_save[txid] = (tx_info.height, tx_info.timestamp,
tx_info.txpos, tx_info.header_hash)
tx_info.txpos, tx_info.header_hash, tx_info.flodata)
self.storage.put('verified_tx3', verified_tx_to_save)
if write:
self.storage.write()
@ -645,6 +646,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[5]
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
@ -794,7 +812,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

@ -124,9 +124,8 @@ class BaseWizard(object):
])
wallet_kinds = [
('standard', _("Standard wallet")),
('2fa', _("Wallet with two-factor authentication")),
('multisig', _("Multi-signature wallet")),
('imported', _("Import Bitcoin addresses or private keys")),
('imported', _("Import FLO addresses or private keys")),
]
choices = [pair for pair in wallet_kinds if pair[0] in wallet_types]
self.choice_dialog(title=title, message=message, choices=choices, run_next=self.on_wallet_type)
@ -199,8 +198,8 @@ class BaseWizard(object):
def import_addresses_or_keys(self):
v = lambda x: keystore.is_address_list(x) or keystore.is_private_key_list(x)
title = _("Import Bitcoin Addresses")
message = _("Enter a list of Bitcoin addresses (this will create a watching-only wallet), or a list of private keys.")
title = _("Import FLO Addresses")
message = _("Enter a list of FLO addresses (this will create a watching-only wallet), or a list of private keys.")
self.add_xpub_dialog(title=title, message=message, run_next=self.on_import,
is_valid=v, allow_multi=True, show_wif_help=True)
@ -570,14 +569,10 @@ class BaseWizard(object):
title = _('Choose Seed type')
if message is None:
message = ' '.join([
_("The type of addresses used by your wallet will depend on your seed."),
_("Segwit wallets use bech32 addresses, defined in BIP173."),
_("Please note that websites and other wallets may not support these addresses yet."),
_("Thus, you might want to keep using a non-segwit wallet in order to be able to receive bitcoins during the transition period.")
_("The type of addresses used by your wallet will depend on your seed.")
])
if choices is None:
choices = [
('create_segwit_seed', _('Segwit')),
('create_standard_seed', _('Legacy')),
]
self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)

View File

@ -31,9 +31,15 @@ from . import constants
from .util import bfh, bh2u
from .simple_config import SimpleConfig
try:
import pylibscrypt
getPoWHash = lambda x: pylibscrypt.scrypt(password=x, salt=x, N=1024, r=1, p=1, olen=32)
except ImportError:
util.print_msg("Warning: package pylibscrypt not available")
HEADER_SIZE = 80 # bytes
MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
MAX_TARGET = 0x00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
class MissingHeader(Exception):
@ -147,6 +153,10 @@ def read_blockchains(config: 'SimpleConfig'):
instantiate_chain(filename)
def pow_hash_header(header):
return hash_encode(getPoWHash(bfh(serialize_header(header))))
def get_best_chain() -> 'Blockchain':
return blockchains[constants.net.GENESIS]
@ -251,9 +261,13 @@ class Blockchain(util.PrintError):
p = self.path()
self._size = os.path.getsize(p)//HEADER_SIZE if os.path.exists(p) else 0
#def pow_hash_header(header):
# return hash_encode(getPoWHash(bfh(serialize_header(header))))
@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'):
@ -261,27 +275,63 @@ class Blockchain(util.PrintError):
if constants.net.TESTNET:
return
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}")
target_val = cls.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
print('chunk ' + str(index))
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):
@ -451,40 +501,70 @@ 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:
bitsN = (bits >> 24) & 0xff
if not (0x03 <= bitsN <= 0x1d):
raise Exception("First part of bits should be in [0x03, 0x1d]")
if not (0x03 <= bitsN <= 0x1e):
raise Exception("First part of bits should be in [0x03, 0x1e]")
bitsBase = bits & 0xffffff
if not (0x8000 <= bitsBase <= 0x7fffff):
raise Exception("Second part of bits should be in [0x8000, 0x7fffff]")
return bitsBase << (8 * (bitsN-3))
return bitsBase << (8 * (bitsN - 3))
@classmethod
def target_to_bits(cls, target: int) -> int:
@ -547,7 +627,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:
@ -578,6 +658,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 = 94
SEGWIT_HRP = "flo"
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 = 216
# 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 = 58
SEGWIT_HRP = "tflo"
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):

View File

@ -1,823 +1,44 @@
{
"BitFlyer": [
"JPY"
],
"BitPay": [
"CoinMarketcap": [
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BCH",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BTC",
"BTN",
"BWP",
"BZD",
"KHR",
"CAD",
"CDF",
"CHF",
"CLF",
"CLP",
"CNY",
"COP",
"CRC",
"CUP",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EGP",
"ETB",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"INR",
"IQD",
"IRR",
"ISK",
"JEP",
"JMD",
"JOD",
"INR",
"IDR",
"IQD",
"ILS",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KPW",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRU",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"MXN",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SDG",
"SEK",
"SGD",
"SHP",
"SLL",
"SOS",
"SRD",
"STN",
"SVC",
"SYP",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VEF",
"VND",
"VUV",
"WST",
"XAF",
"XAG",
"XAU",
"XCD",
"XOF",
"XPF",
"YER",
"ZAR",
"ZMW",
"ZWL"
],
"BitStamp": [
"USD"
],
"Bitbank": [
"JPY"
],
"BitcoinAverage": [
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BTN",
"BWP",
"BYN",
"BZD",
"CAD",
"CDF",
"CHF",
"CLF",
"CLP",
"CNH",
"CNY",
"COP",
"CRC",
"CUC",
"CUP",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EGP",
"ERN",
"ETB",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GGP",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"IMP",
"INR",
"IQD",
"IRR",
"ISK",
"JEP",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KPW",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRO",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SDG",
"SEK",
"SGD",
"SHP",
"SLL",
"SOS",
"SRD",
"SSP",
"STD",
"SVC",
"SYP",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VEF",
"VND",
"VUV",
"WST",
"XAF",
"XAG",
"XAU",
"XCD",
"XDR",
"XOF",
"XPD",
"XPF",
"XPT",
"YER",
"ZAR",
"ZMW",
"ZWL"
],
"Bitcointoyou": [
"BRL"
],
"BitcoinVenezuela": [
"ARS",
"EUR",
"USD",
"VEF"
],
"Bitmarket": [
"PLN"
],
"Bitso": [
"MXN"
],
"Bitvalor": [
"BRL"
],
"BlockchainInfo": [
"AUD",
"BRL",
"CAD",
"CHF",
"CLP",
"CNY",
"DKK",
"EUR",
"GBP",
"HKD",
"INR",
"ISK",
"JPY",
"KRW",
"NZD",
"PLN",
"RUB",
"SEK",
"SGD",
"THB",
"TWD",
"USD"
],
"CoinDesk": [
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BTC",
"BTN",
"BWP",
"BYR",
"BZD",
"CAD",
"CDF",
"CHF",
"CLF",
"CLP",
"CNY",
"COP",
"CRC",
"CUP",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EEK",
"EGP",
"ERN",
"ETB",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"INR",
"IQD",
"IRR",
"ISK",
"JEP",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KPW",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LTL",
"LVL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRO",
"MTL",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SDG",
"SEK",
"SGD",
"SHP",
"SLL",
"SOS",
"SRD",
"STD",
"SVC",
"SYP",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VEF",
"VND",
"VUV",
"WST",
"XAF",
"XAG",
"XAU",
"XBT",
"XCD",
"XDR",
"XOF",
"XPF",
"YER",
"ZAR",
"ZMK",
"ZMW",
"ZWL"
],
"Coinbase": [
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BCH",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BTN",
"BWP",
"BYN",
"BYR",
"BZD",
"CAD",
"CDF",
"CHF",
"CLF",
"CLP",
"CNH",
"CNY",
"COP",
"CRC",
"CUC",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EEK",
"EGP",
"ERN",
"ETB",
"ETH",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GGP",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"IMP",
"INR",
"IQD",
"ISK",
"JEP",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LTC",
"LTL",
"LVL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRO",
"MTL",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SEK",
"SGD",
"SHP",
"SLL",
"SOS",
"SRD",
"SSP",
"STD",
"SVC",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VEF",
"VND",
"VUV",
"WST",
"XAF",
"XAG",
"XAU",
"XCD",
"XDR",
"XOF",
"XPD",
"XPF",
"XPT",
"YER",
"ZAR",
"ZMK",
"ZMW",
"ZWL"
],
"Foxbit": [
"BRL"
],
"Kraken": [
"CAD",
"EUR",
"GBP",
"JPY",
"USD"
],
"LocalBitcoins": [
"AED",
"ARS",
"AUD",
"BDT",
"BHD",
"BOB",
"BRL",
"BWP",
"BYN",
"CAD",
"CHF",
"CLP",
"CNY",
"COP",
"CRC",
"CZK",
"DKK",
"DOP",
"EGP",
"ETH",
"EUR",
"GBP",
"GEL",
"GHS",
"HKD",
"HRK",
"HUF",
"IDR",
"ILS",
"INR",
"IRR",
"JOD",
"JPY",
"KES",
"KRW",
"KZT",
"LKR",
"LTC",
"MAD",
"MXN",
"MYR",
"NGN",
"NOK",
"NZD",
"PAB",
"PEN",
"PHP",
"PKR",
"PLN",
"PYG",
"RON",
"RSD",
"GBP",
"QAR",
"RUB",
"SAR",
"SEK",
"SGD",
"SZL",
"SEK",
"CHF",
"THB",
"TRY",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"VEF",
"VND",
"XAF",
"XRP",
"ZAR",
"ZMW"
],
"MercadoBitcoin": [
"BRL"
],
"NegocieCoins": [
"BRL"
],
"TheRockTrading": [
"EUR"
],
"WEX": [
"EUR",
"RUB",
"USD"
],
"itBit": []
}
"VND"
]
}

View File

@ -280,6 +280,14 @@ class CoinDesk(ExchangeBase):
return json['bpi']
class CoinMarketcap(ExchangeBase):
async def get_rates(self, ccy):
json = await self.get_json('pro-api.coinmarketcap.com','/v1/cryptocurrency/quotes/latest?symbol=FLO&CMC_PRO_API_KEY=194ee8a1-5a58-4f3e-ba07-a1d6bc633210&convert=%s' % ccy)
result = {ccy: Decimal(json['data']['FLO']['quote'][ccy]['price'])}
return result
class Coinsecure(ExchangeBase):
async def get_rates(self, ccy):
@ -520,7 +528,7 @@ class FxThread(ThreadJob):
return self.config.get("currency", "EUR")
def config_exchange(self):
return self.config.get('use_exchange', 'BitcoinAverage')
return self.config.get('use_exchange', 'CoinMarketcap')
def show_history(self):
return self.is_enabled() and self.get_history_config() and self.ccy in self.exchange.history_ccys()
@ -605,4 +613,4 @@ class FxThread(ThreadJob):
def timestamp_rate(self, timestamp):
from .util import timestamp_to_datetime
date = timestamp_to_datetime(timestamp)
return self.history_rate(date)
return self.history_rate(date)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 KiB

After

Width:  |  Height:  |  Size: 39 KiB

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: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -151,6 +151,20 @@
data: ''
text: ' '.join(map(''.join, zip(*[iter(self.data)]*4))) if self.data else ''
<FLODataLabel>
font_size: '6pt'
name: ''
data: ''
text: self.data
touched: False
padding: '10dp', '10dp'
canvas.before:
Color:
rgb: .3, .3, .3
Rectangle:
size: self.size
pos: self.pos
<InfoBubble>
size_hint: None, None
width: '270dp' if root.fs else min(self.width, dp(270))
@ -193,6 +207,18 @@
size: self.size
pos: self.pos
<FLODataBlueBottom@BoxLayout>
item_height: dp(60)
foreground_color: .843, .914, .972, 1
padding: '12dp', 0
canvas.before:
Color:
rgba: 0.192, .498, 0.745, 1
BorderImage:
source: 'atlas://electrum/gui/kivy/theming/light/card_bottom'
size: self.size
pos: self.pos
<AddressFilter@GridLayout>
item_height: dp(42)
@ -247,6 +273,36 @@
size: self.size
pos: self.pos
<FLODataButton@Button>:
background_color: 1, .585, .878, 0
halign: 'left'
text_size: (self.width-10, None)
size_hint: 1, 1
default_text: ''
text: self.default_text
text_color: self.foreground_color
disabled_color: 1, 1, 1, 1
foreground_color: 1, 1, 1, 1
canvas.before:
Color:
rgba: (0.9, .498, 0.745, 1) if self.state == 'down' else self.background_color
Rectangle:
size: self.size
pos: self.pos
<ClickableLabel@ButtonBehavior+Label>:
padding: '10dp', '10dp'
text_color: self.foreground_color
disabled_color: 1, 1, 1, 1
foreground_color: 1, 1, 1, 1
canvas.before:
Color:
rgba: (0.9, .498, 0.745, 1) if self.state == 'down' else self.background_color
Rectangle:
size: self.size
pos: self.pos
<BlueButton@Button>:
background_color: 1, .585, .878, 0
halign: 'left'
@ -266,6 +322,17 @@
size: self.size
pos: self.pos
<ClickableInput@ButtonBehavior+TextInput>:
readonly: True
keyboard_mode: 'managed'
canvas:
Color:
rgba: 1, 0, 0, .3
Rectangle:
size: self.size
pos: self.pos
<AddressButton@Button>:
background_color: 1, .585, .878, 0
halign: 'center'
@ -449,6 +516,7 @@ BoxLayout:
ActionOvrButton:
name: 'network'
text: _('Network')
text: _('Network')
ActionOvrButton:
name: 'settings'
text: _('Settings')

View File

@ -40,7 +40,7 @@ from kivy.lang import Builder
from .uix.dialogs.installwizard import InstallWizard
from .uix.dialogs import InfoBubble, crash_reporter
from .uix.dialogs import OutputList, OutputItem
from .uix.dialogs import TopLabel, RefLabel
from .uix.dialogs import TopLabel, RefLabel, FLODataLabel
#from kivy.core.window import Window
#Window.softinput_mode = 'below_target'
@ -160,7 +160,7 @@ class ElectrumWindow(App):
self.send_screen.set_URI(uri)
def on_new_intent(self, intent):
if intent.getScheme() != 'bitcoin':
if intent.getScheme() != 'flo':
return
uri = intent.getDataString()
self.set_URI(uri)
@ -953,6 +953,16 @@ class ElectrumWindow(App):
d = LabelDialog(_('Enter description'), text, callback)
d.open()
def flodata_dialog(self, screen):
from .uix.dialogs.flodata_dialog import FLODataDialog
text = screen.flodata
def callback(text):
screen.flodata = text
d = FLODataDialog(_('Enter FLO data'), text, callback)
d.open()
def amount_dialog(self, screen, show_max):
from .uix.dialogs.amount_dialog import AmountDialog
amount = screen.amount
@ -1002,6 +1012,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,)))

View File

@ -165,20 +165,20 @@ class ScannerAndroid(NFCBase):
def create_AAR(self):
'''Create the record responsible for linking our application to the tag.
'''
return NdefRecord.createApplicationRecord(JString("org.electrum.kivy"))
return NdefRecord.createApplicationRecord(JString("org.electrum_flo.kivy"))
def create_TNF_EXTERNAL(self, data):
'''Create our actual payload record.
'''
if BUILDVERSION >= 14:
domain = "org.electrum"
domain = "org.electrum_flo"
stype = "externalType"
extRecord = NdefRecord.createExternal(domain, stype, data)
else:
# Creating the NdefRecord manually:
extRecord = NdefRecord(
NdefRecord.TNF_EXTERNAL_TYPE,
"org.electrum:externalType",
"org.electrum_flo:externalType",
'',
data)
return extRecord
@ -213,7 +213,7 @@ class ScannerAndroid(NFCBase):
# Create record
ndef_record = NdefRecord(
NdefRecord.TNF_MIME_MEDIA,
'org.electrum.kivy', '', data)
'org.electrum_flo.kivy', '', data)
# Create message
ndef_message = NdefMessage([ndef_record])

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,13 +1,13 @@
[app]
# (str) Title of your application
title = Electrum
title = FLO-Electrum
# (str) Package name
package.name = Electrum
# (str) Package domain (needed for android/ios packaging)
package.domain = org.electrum
package.domain = org.electrum_flo
# (str) Source code where the main.py live
source.dir = .

View File

@ -218,3 +218,6 @@ class TopLabel(Factory.Label):
class RefLabel(TopLabel):
pass
class FLODataLabel(TopLabel):
pass

View File

@ -0,0 +1,65 @@
from kivy.app import App
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.textinput import TextInput
from kivy.properties import NumericProperty
Builder.load_string('''
<FLODataDialog@Popup>
id: popup
title: ''
size_hint: 0.8, 0.5
pos_hint: {'top':0.9}
BoxLayout:
orientation: 'vertical'
Widget:
size_hint: 1, 0.1
LimitedInput:
id:input
padding: '5dp'
size_hint: 1, 1
height: '27dp'
max_characters: 1022
pos_hint: {'center_y':.5}
text:''
multiline: True
background_normal: 'atlas://electrum/gui/kivy/theming/light/tab_btn'
background_active: 'atlas://electrum/gui/kivy/theming/light/tab_btn'
hint_text_color: self.foreground_color
foreground_color: 1, 1, 1, 1
font_size: '16dp'
focus: True
Widget:
size_hint: 1, 0.2
BoxLayout:
orientation: 'horizontal'
size_hint: 1, 0.5
Button:
text: 'Cancel'
size_hint: 0.5, None
height: '48dp'
on_release: popup.dismiss()
Button:
text: 'OK'
size_hint: 0.5, None
height: '48dp'
on_release:
root.callback(input.text)
popup.dismiss()
''')
class LimitedInput(TextInput):
max_characters = NumericProperty(0)
def insert_text(self, substring, from_undo=False):
if len(self.text) > self.max_characters and self.max_characters > 0:
substring = ""
TextInput.insert_text(self, substring, from_undo)
class FLODataDialog(Factory.Popup):
def __init__(self, title, text, callback):
Factory.Popup.__init__(self)
self.ids.input.text = text
self.callback = callback
self.title = title

View File

@ -26,6 +26,7 @@ Builder.load_string('''
status_str: ''
description: ''
outputs_str: ''
flodata: ''
BoxLayout:
orientation: 'vertical'
ScrollView:
@ -66,6 +67,11 @@ Builder.load_string('''
text: _('Outputs') + ':'
OutputList:
id: output_list
TopLabel:
text: _('FLO data') + ':'
FLODataLabel:
data: root.flodata
name: _('FLO data')
Widget:
size_hint: 1, 0.1
@ -109,6 +115,7 @@ class TxDialog(Factory.Popup):
def update(self):
format_amount = self.app.format_amount_and_units
tx_hash, self.status_str, self.description, self.can_broadcast, self.can_rbf, amount, fee, height, conf, timestamp, exp_n = self.wallet.get_tx_info(self.tx)
self.flodata = self.wallet.get_flodata(tx_hash)
self.tx_hash = tx_hash or ''
if timestamp:
self.date_label = _('Date')

View File

@ -137,6 +137,7 @@ class HistoryScreen(CScreen):
status, status_str = self.app.wallet.get_tx_status(tx_hash, tx_mined_status)
icon = "atlas://electrum/gui/kivy/theming/light/" + TX_ICONS[status]
label = self.app.wallet.get_label(tx_hash) if tx_hash else _('Pruned transaction outputs')
flodata = self.app.wallet.get_flodata(tx_hash)
ri = {}
ri['screen'] = self
ri['tx_hash'] = tx_hash
@ -144,6 +145,7 @@ class HistoryScreen(CScreen):
ri['date'] = status_str
ri['message'] = label
ri['confirmations'] = tx_mined_status.conf
ri['flodata'] = flodata
if value is not None:
ri['is_mine'] = value < 0
if value < 0: value = - value
@ -177,7 +179,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 +187,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 +200,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 +252,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 +265,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 +384,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

@ -1,7 +1,7 @@
#:import VERSION electrum.version.ELECTRUM_VERSION
Popup:
title: _("About Electrum")
title: _("About FLO-Electrum")
BoxLayout:
orientation: 'vertical'
spacing: '10dp'
@ -26,19 +26,25 @@ Popup:
size_hint_x: 0.4
TopLabel:
markup: True
text: '[color=6666ff][ref=x]https://electrum.org[/ref][/color]'
text: '[color=6666ff][ref=x]Ranchi Mall Github[/ref][/color]'
on_ref_press:
import webbrowser
webbrowser.open("https://electrum.org")
webbrowser.open("https://github.com/ranchimall/flo-electrum")
size_hint_x: 0.6
TopLabel:
text: _('Developers')
text: _('FLO-Electrum Developers')
size_hint_x: 0.4
TopLabel:
text: '\n'.join(['Vivek Teega', 'Bitspill', 'Rohit Tripathy', 'Akhil Bharti'])
size_hint_x: 0.6
TopLabel:
text: _('BTC-Electrum Developers')
size_hint_x: 0.4
TopLabel:
text: '\n'.join(['Thomas Voegtlin', 'Neil Booth', 'Akshay Arora'])
size_hint_x: 0.6
TopLabel:
text: _('Distributed by Electrum Technologies GmbH')
text: _('Developed by Ranchi Mall FLO Blockchain Contract')
padding: '0dp', '20dp'
Widget:
size_hint: None, 0.5

View File

@ -26,6 +26,7 @@
confirmations: 0
date: ''
quote_text: ''
flodata: ''
Image:
id: icon
source: root.icon
@ -46,12 +47,17 @@
font_size: '14sp'
shorten: True
text: root.date + ' ' + root.message
CardLabel:
color: .799, .799, .799, 1
font_size: '14sp'
shorten: True
text: root.flodata if root.flodata else ''
Widget
<HistoryRecycleView>:
viewclass: 'HistoryItem'
RecycleBoxLayout:
default_size: None, dp(56)
default_size: None, dp(70)
default_size_hint: 1, None
size_hint: 1, None
height: self.minimum_height
@ -63,15 +69,24 @@ HistoryScreen:
content: history_container
BoxLayout:
orientation: 'vertical'
Button:
background_color: 0, 0, 0, 0
text: app.fiat_balance if app.is_fiat else app.balance
markup: True
color: .9, .9, .9, 1
font_size: '30dp'
bold: True
RelativeLayout:
size_hint: 1, 0.25
on_release: app.is_fiat = not app.is_fiat if app.fx.is_enabled() else False
Button:
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
background_color: 0, 0, 0, 0
text: app.fiat_balance if app.is_fiat else app.balance
markup: True
color: .9, .9, .9, 1
font_size: '30dp'
bold: True
on_release: app.is_fiat = not app.is_fiat if app.fx.is_enabled() else False
Label:
pos_hint: {'center_x': 0.5, 'center_y': 0.17}
background_color: 0, 0, 0, 0
markup: True
color: .699, .699, .699, 1
font_size: '14dp'
text: "blocks : %d" % app.num_blocks
HistoryRecycleView:
id: history_container
scroll_type: ['bars', 'content']

View File

@ -11,6 +11,7 @@ SendScreen:
address: ''
amount: ''
message: ''
flodata: ''
is_pr: False
BoxLayout
padding: '12dp', '12dp', '12dp', '12dp'
@ -90,6 +91,28 @@ SendScreen:
default_text: _('Fee')
text: app.fee_status
on_release: Clock.schedule_once(lambda dt: app.fee_dialog(s, True))
FLODataBlueBottom:
item_height: dp(60)
spacing: '5dp'
size_hint: 1, None
height: self.minimum_height
BoxLayout:
size_hint: 1, None
height: dp(84)
spacing: '5dp'
Image:
source: 'atlas://electrum/gui/kivy/theming/light/page-pen'
opacity: 0.7
size_hint: None, None
size: '22dp', '22dp'
pos_hint: {'center_y': .5}
ScrollView:
ClickableLabel:
text: s.flodata if s.flodata else (_('No FLO data') if root.is_pr else _('FLO data'))
text_size: (self.width, None) if s.flodata else (self.width,self.height/2)
size_hint_y: None if s.flodata else 1
height: max(dp(82), self.texture_size[1])
on_release: Clock.schedule_once(lambda dt: app.flodata_dialog(s))
BoxLayout:
size_hint: 1, None
height: '48dp'
@ -125,3 +148,4 @@ SendScreen:
size_hint: 1, 1

View File

@ -57,6 +57,8 @@ except:
print_error("qt/history_list: could not import electrum.plot. This feature needs matplotlib to be installed.")
plot_history = None
import pdb
# note: this list needs to be kept in sync with another in kivy
TX_ICONS = [
"unconfirmed.png",
@ -81,6 +83,7 @@ class HistoryColumns(IntEnum):
FIAT_ACQ_PRICE = 6
FIAT_CAP_GAINS = 7
TXID = 8
FLO_DATA = 9
class HistorySortModel(QSortFilterProxyModel):
def lessThan(self, source_left: QModelIndex, source_right: QModelIndex):
@ -132,6 +135,7 @@ class HistoryModel(QAbstractItemModel, PrintError):
conf = tx_item['confirmations']
txpos = tx_item['txpos_in_block'] or 0
height = tx_item['height']
flodata = self.parent.wallet.get_flodata(tx_hash)
try:
status, status_str = self.tx_status_cache[tx_hash]
except KeyError:
@ -155,6 +159,7 @@ class HistoryModel(QAbstractItemModel, PrintError):
HistoryColumns.FIAT_CAP_GAINS:
tx_item['capital_gain'].value if 'capital_gain' in tx_item else None,
HistoryColumns.TXID: tx_hash,
HistoryColumns.FLO_DATA: flodata,
}
return QVariant(d[col])
if role not in (Qt.DisplayRole, Qt.EditRole):
@ -204,6 +209,8 @@ class HistoryModel(QAbstractItemModel, PrintError):
return QVariant(self.parent.fx.format_fiat(cg))
elif col == HistoryColumns.TXID:
return QVariant(tx_hash)
elif col == HistoryColumns.FLO_DATA:
return QVariant(flodata)
return QVariant()
def parent(self, index: QModelIndex):
@ -336,6 +343,7 @@ class HistoryModel(QAbstractItemModel, PrintError):
HistoryColumns.FIAT_ACQ_PRICE: fiat_acq_title,
HistoryColumns.FIAT_CAP_GAINS: fiat_cg_title,
HistoryColumns.TXID: 'TXID',
HistoryColumns.FLO_DATA: _('FLO Data'),
}[section]
def flags(self, idx):
@ -482,13 +490,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)

72445
electrum/gui/qt/icons_rc.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -466,8 +466,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'))
@ -630,11 +630,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_update_check(self, version=None):
@ -872,7 +872,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)
@ -902,8 +902,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)
@ -1133,7 +1133,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)
@ -1150,10 +1150,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' \
@ -1161,24 +1167,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)
@ -1258,7 +1264,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)
@ -1273,7 +1279,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)
@ -1287,7 +1293,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)
@ -1545,6 +1551,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()
@ -1569,7 +1576,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'))
@ -1580,7 +1587,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)
@ -1591,12 +1598,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
@ -1833,7 +1840,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

@ -117,6 +117,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)
@ -274,6 +276,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 + str("\""))
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

@ -780,7 +780,7 @@ class Network(PrintError):
try:
return await func(self, *args, **kwargs)
except aiorpcx.jsonrpc.CodeMessageError as e:
raise UntrustedServerReturnedError(original_exception=e)
raise UntrustedServerReturnedError(original_exception=e) from e
return wrapper
@best_effort_reliable
@ -1188,24 +1188,21 @@ class Network(PrintError):
return parse_servers(await session.send_request('server.peers.subscribe'))
async def send_multiple_requests(self, servers: List[str], method: str, params: Sequence):
num_connecting = len(self.connecting)
for server in servers:
self._start_interface(server)
# sleep a bit
for _ in range(10):
if len(self.connecting) < num_connecting:
break
await asyncio.sleep(1)
responses = dict()
async def get_response(iface: Interface):
async def get_response(server):
interface = Interface(self, server, self.proxy)
timeout = self.get_network_timeout_seconds(NetworkTimeout.Urgent)
try:
res = await iface.session.send_request(method, params, timeout=10)
await asyncio.wait_for(interface.ready, timeout)
except BaseException as e:
await interface.close()
return
try:
res = await interface.session.send_request(method, params, timeout=10)
except Exception as e:
res = e
responses[iface.server] = res
responses[interface.server] = res
async with TaskGroup() as group:
for server in servers:
interface = self.interfaces.get(server)
if interface:
await group.spawn(get_response(interface))
await group.spawn(get_response(server))
return responses

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

@ -496,9 +496,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

@ -154,7 +154,7 @@ class KeepKeyPlugin(HW_PluginBase):
return client
def get_coin_name(self):
return "Testnet" if constants.net.TESTNET else "Bitcoin"
return "Testnet" if constants.net.TESTNET else "Florincoin"
def initialize_device(self, device_id, wizard, handler):
# Initialization method

View File

@ -30,7 +30,7 @@ except ImportError:
MSG_NEEDS_FW_UPDATE_GENERIC = _('Firmware version too old. Please update at') + \
' https://www.ledgerwallet.com'
MSG_NEEDS_FW_UPDATE_SEGWIT = _('Firmware version (or "Bitcoin" app) too old for Segwit support. Please update at') + \
MSG_NEEDS_FW_UPDATE_SEGWIT = _('Firmware version (or "Florincoin" app) too old for Segwit support. Please update at') + \
' https://www.ledgerwallet.com'
MULTI_OUTPUT_SUPPORT = '1.1.4'
SEGWIT_SUPPORT = '1.1.10'

View File

@ -159,7 +159,7 @@ class SafeTPlugin(HW_PluginBase):
return client
def get_coin_name(self):
return "Testnet" if constants.net.TESTNET else "Bitcoin"
return "Testnet" if constants.net.TESTNET else "Florincoin"
def initialize_device(self, device_id, wizard, handler):
# Initialization method

View File

@ -160,7 +160,7 @@ class TrezorPlugin(HW_PluginBase):
return client
def get_coin_name(self):
return "Testnet" if constants.net.TESTNET else "Bitcoin"
return "Testnet" if constants.net.TESTNET else "Florincoin"
def initialize_device(self, device_id, wizard, handler):
# Initialization method

View File

@ -0,0 +1,40 @@
import os
from electrum.simple_config import SimpleConfig
from electrum import constants
from electrum.daemon import Daemon
from electrum.storage import WalletStorage
from electrum.wallet import Wallet
from electrum.commands import Commands
config = SimpleConfig({"testnet": True}) # to use ~/.electrum/testnet as datadir
constants.set_testnet() # to set testnet magic bytes
daemon = Daemon(config, listen_jsonrpc=False)
network = daemon.network
assert network.asyncio_loop.is_running()
command_runner = Commands(config, wallet=None, network=network)
# get wallet on disk
wallet_dir = os.path.dirname(config.get_wallet_path())
wallet_path = os.path.join(wallet_dir, "test_wallet")
if not os.path.exists(wallet_path):
config.set_key('wallet_path', wallet_path)
command_runner.create(segwit=True)
# open wallet
storage = WalletStorage(wallet_path)
wallet = Wallet(storage)
wallet.start_network(network)
# you can use ~CLI commands by accessing command_runner
command_runner.wallet = wallet
print("balance", command_runner.getbalance())
print("addr", command_runner.getunusedaddress())
print("gettx", command_runner.gettransaction("bd3a700b2822e10a034d110c11a596ee7481732533eb6aca7f9ca02911c70a4f"))
# but you might as well interact with the underlying methods directly
print("balance", wallet.get_balance())
print("addr", wallet.get_unused_address())
print("gettx", network.run_from_another_thread(network.get_transaction("bd3a700b2822e10a034d110c11a596ee7481732533eb6aca7f9ca02911c70a4f")))

View File

@ -1,403 +1,17 @@
{
"3smoooajg7qqac2y.onion": {
"ranchimall.duckdns.org": {
"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": {
"ranchimall1.duckdns.org": {
"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"
},
"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": {
"snowflake.oip.fun": {
"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

@ -32,12 +32,16 @@ from aiorpcx import TaskGroup, run_in_thread
from .transaction import Transaction
from .util import bh2u, make_aiohttp_session, NetworkJobOnDefaultServer
from .bitcoin import address_to_scripthash, is_address
from .network import UntrustedServerReturnedError
if TYPE_CHECKING:
from .network import Network
from .address_synchronizer import AddressSynchronizer
class SynchronizerFailure(Exception): pass
def history_status(h):
if not h:
return None
@ -78,7 +82,7 @@ class SynchronizerBase(NetworkJobOnDefaultServer):
asyncio.run_coroutine_threadsafe(self._add_address(addr), self.asyncio_loop)
async def _add_address(self, addr: str):
if not is_address(addr): raise ValueError(f"invalid bitcoin address {addr}")
if not is_address(addr): raise ValueError(f"invalid FLO address {addr}")
if addr in self.requested_addrs: return
self.requested_addrs.add(addr)
await self.add_queue.put(addr)
@ -165,7 +169,7 @@ class Synchronizer(SynchronizerBase):
# Remove request; this allows up_to_date to be True
self.requested_histories.pop(addr)
async def _request_missing_txs(self, hist):
async def _request_missing_txs(self, hist, *, allow_server_not_finding_tx=False):
# "hist" is a list of [tx_hash, tx_height] lists
transaction_hashes = []
for tx_hash, tx_height in hist:
@ -179,24 +183,33 @@ class Synchronizer(SynchronizerBase):
if not transaction_hashes: return
async with TaskGroup() as group:
for tx_hash in transaction_hashes:
await group.spawn(self._get_transaction, tx_hash)
await group.spawn(self._get_transaction(tx_hash, allow_server_not_finding_tx=allow_server_not_finding_tx))
async def _get_transaction(self, tx_hash):
result = await self.network.get_transaction(tx_hash)
async def _get_transaction(self, tx_hash, *, allow_server_not_finding_tx=False):
try:
result = await self.network.get_transaction(tx_hash)
except UntrustedServerReturnedError as e:
# most likely, "No such mempool or blockchain transaction"
if allow_server_not_finding_tx:
self.requested_tx.pop(tx_hash)
return
else:
raise
tx = Transaction(result)
try:
tx.deserialize()
except Exception:
self.print_msg("cannot deserialize transaction, skipping", tx_hash)
return
tx.deserialize() # see if raises
except Exception as e:
# possible scenarios:
# 1: server is sending garbage
# 2: there is a bug in the deserialization code
# 3: there was a segwit-like upgrade that changed the tx structure
# that we don't know about
raise SynchronizerFailure(f"cannot deserialize transaction {tx_hash}") from e
if tx_hash != tx.txid():
self.print_error("received tx does not match expected txid ({} != {})"
.format(tx_hash, tx.txid()))
return
raise SynchronizerFailure(f"received tx does not match expected txid ({tx_hash} != {tx.txid()})")
tx_height = self.requested_tx.pop(tx_hash)
self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
self.print_error("received tx %s height: %d bytes: %d" %
(tx_hash, tx_height, len(tx.raw)))
self.print_error(f"received tx {tx_hash} height: {tx_height} bytes: {len(tx.raw)}")
# callbacks
self.wallet.network.trigger_callback('new_transaction', self.wallet, tx)
@ -207,7 +220,7 @@ class Synchronizer(SynchronizerBase):
# Old electrum servers returned ['*'] when all history for the address
# was pruned. This no longer happens but may remain in old wallets.
if history == ['*']: continue
await self._request_missing_txs(history)
await self._request_missing_txs(history, allow_server_not_finding_tx=True)
# add addresses to bootstrap
for addr in self.wallet.get_addresses():
await self._add_address(addr)

View File

@ -41,6 +41,7 @@ from .bitcoin import (TYPE_ADDRESS, TYPE_PUBKEY, TYPE_SCRIPT, hash_160,
push_script, int_to_hex, push_script, b58_address_to_hash160)
from .crypto import sha256d
from .keystore import xpubkey_to_address, xpubkey_to_pubkey
import codecs
NO_SIGNATURE = 'ff'
@ -68,12 +69,10 @@ class TxOutput(NamedTuple):
address: str
value: Union[int, str] # str when the output is set to max: '!'
class TxOutputForUI(NamedTuple):
address: str
value: int
class TxOutputHwInfo(NamedTuple):
address_index: Tuple
sorted_xpubs: Iterable[str]
@ -125,7 +124,7 @@ class BCDataStream(object):
self.read_cursor += length
return result
except IndexError:
raise SerializationError("attempt to read past end of buffer")
raise SerializationError("attempt to read past end of buffer") from None
def can_read_more(self) -> bool:
if not self.input:
@ -159,8 +158,8 @@ class BCDataStream(object):
elif size == 255:
size = self._read_num('<Q')
return size
except IndexError:
raise SerializationError("attempt to read past end of buffer")
except IndexError as e:
raise SerializationError("attempt to read past end of buffer") from e
def write_compact_size(self, size):
if size < 0:
@ -182,7 +181,7 @@ class BCDataStream(object):
(i,) = struct.unpack_from(format, self.input, self.read_cursor)
self.read_cursor += struct.calcsize(format)
except Exception as e:
raise SerializationError(e)
raise SerializationError(e) from e
return i
def _write_num(self, format, num):
@ -642,6 +641,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,7 +682,8 @@ class Transaction:
self._inputs = None
self._outputs = None # type: List[TxOutput]
self.locktime = 0
self.version = 2
self.flodata = ""
self.version = 1
# by default we assume this is a partial txn;
# this value will get properly set when deserializing
self.is_partial_originally = True
@ -782,16 +786,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, version=None):
def from_io(klass, inputs, outputs, locktime=0, version=None, flodata=""):
self = klass(None)
self._inputs = inputs
self._outputs = outputs
self.locktime = locktime
self.flodata = flodata
if version is not None:
self.version = version
self.BIP69_sort()
@ -1044,6 +1050,7 @@ 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]
@ -1057,11 +1064,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):
@ -1083,6 +1096,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)
@ -1095,9 +1110,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

@ -60,12 +60,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
@ -148,7 +147,13 @@ 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
def __ne__(self, other):
return not (self == other)
def __eq__(self, other):
return self.value == other.value
@ -483,11 +488,11 @@ def user_dir():
if 'ANDROID_DATA' in os.environ:
return android_data_dir()
elif os.name == 'posix':
return os.path.join(os.environ["HOME"], ".electrum")
return os.path.join(os.environ["HOME"], ".electrum-flo")
elif "APPDATA" in os.environ:
return os.path.join(os.environ["APPDATA"], "Electrum")
return os.path.join(os.environ["APPDATA"], "Electrum-FLO")
elif "LOCALAPPDATA" in os.environ:
return os.path.join(os.environ["LOCALAPPDATA"], "Electrum")
return os.path.join(os.environ["LOCALAPPDATA"], "Electrum-FLO")
else:
#raise Exception("No home directory found in environment variables.")
return
@ -646,56 +651,14 @@ def time_difference(distance_in_time, include_seconds):
return "over %d years" % (round(distance_in_minutes / 525600))
mainnet_block_explorers = {
'Bitupper Explorer': ('https://bitupper.com/en/explorer/bitcoin/',
{'tx': 'transactions/', 'addr': 'addresses/'}),
'Biteasy.com': ('https://www.biteasy.com/blockchain/',
{'tx': 'transactions/', 'addr': 'addresses/'}),
'Bitflyer.jp': ('https://chainflyer.bitflyer.jp/',
{'tx': 'Transaction/', 'addr': 'Address/'}),
'Blockchain.info': ('https://blockchain.info/',
{'tx': 'tx/', 'addr': 'address/'}),
'blockchainbdgpzk.onion': ('https://blockchainbdgpzk.onion/',
{'tx': 'tx/', 'addr': 'address/'}),
'Blockr.io': ('https://btc.blockr.io/',
{'tx': 'tx/info/', 'addr': 'address/info/'}),
'Blockstream.info': ('https://blockstream.info/',
{'tx': 'tx/', 'addr': 'address/'}),
'Blocktrail.com': ('https://www.blocktrail.com/BTC/',
{'tx': 'tx/', 'addr': 'address/'}),
'BTC.com': ('https://chain.btc.com/',
{'tx': 'tx/', 'addr': 'address/'}),
'Chain.so': ('https://www.chain.so/',
{'tx': 'tx/BTC/', 'addr': 'address/BTC/'}),
'Insight.is': ('https://insight.bitpay.com/',
{'tx': 'tx/', 'addr': 'address/'}),
'TradeBlock.com': ('https://tradeblock.com/blockchain/',
{'tx': 'tx/', 'addr': 'address/'}),
'BlockCypher.com': ('https://live.blockcypher.com/btc/',
{'tx': 'tx/', 'addr': 'address/'}),
'Blockchair.com': ('https://blockchair.com/bitcoin/',
{'tx': 'transaction/', 'addr': 'address/'}),
'blockonomics.co': ('https://www.blockonomics.co/',
{'tx': 'api/tx?txid=', 'addr': '#/search?q='}),
'OXT.me': ('https://oxt.me/',
{'tx': 'transaction/', 'addr': 'address/'}),
'smartbit.com.au': ('https://www.smartbit.com.au/',
'florincoin.info': ('https://florincoin.info/',
{'tx': 'tx/', 'addr': 'address/'}),
'system default': ('blockchain:/',
{'tx': 'tx/', 'addr': 'address/'}),
}
testnet_block_explorers = {
'Blocktrail.com': ('https://www.blocktrail.com/tBTC/',
{'tx': 'tx/', 'addr': 'address/'}),
'BlockCypher.com': ('https://live.blockcypher.com/btc-testnet/',
{'tx': 'tx/', 'addr': 'address/'}),
'Blockchain.info': ('https://testnet.blockchain.info/',
{'tx': 'tx/', 'addr': 'address/'}),
'Blockstream.info': ('https://blockstream.info/testnet/',
{'tx': 'tx/', 'addr': 'address/'}),
'BTC.com': ('https://tchain.btc.com/',
{'tx': '', 'addr': ''}),
'smartbit.com.au': ('https://testnet.smartbit.com.au/',
'Florincoin.info': ('https://testnet.florincoin.info/',
{'tx': 'tx/', 'addr': 'address/'}),
'system default': ('blockchain://000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943/',
{'tx': 'tx/', 'addr': 'address/'}),
@ -707,7 +670,7 @@ def block_explorer_info():
def block_explorer(config: 'SimpleConfig') -> str:
from . import constants
default_ = 'Blockstream.info'
default_ = 'florincoin.info'
be_key = config.get('block_explorer', default_)
be = block_explorer_info().get(be_key)
return be_key if be is not None else default_
@ -736,12 +699,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")
if u.scheme != 'flo':
raise Exception("Not a FLO URI")
address = u.path
# python for android fails to parse query
@ -758,7 +721,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']
@ -807,7 +770,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)
@ -938,6 +901,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 that mined tx
def make_aiohttp_session(proxy: dict, headers=None, timeout=None):
@ -946,7 +910,6 @@ def make_aiohttp_session(proxy: dict, headers=None, timeout=None):
if timeout is None:
timeout = aiohttp.ClientTimeout(total=10)
ssl_context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=ca_path)
if proxy:
connector = SocksConnector(
socks_ver=SocksVer.SOCKS5 if proxy['mode'] == 'socks5' else SocksVer.SOCKS4,

View File

@ -130,10 +130,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

@ -636,8 +636,7 @@ class Abstract_Wallet(AddressSynchronizer):
return tx
return candidate
def make_unsigned_transaction(self, coins, outputs, config, fixed_fee=None,
change_addr=None, is_sweep=False):
def make_unsigned_transaction(self, coins, outputs, config, fixed_fee=None, change_addr=None, is_sweep=False, flodata=None):
# check outputs
i_max = None
for i, o in enumerate(outputs):
@ -655,6 +654,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 = []
@ -727,6 +729,10 @@ class Abstract_Wallet(AddressSynchronizer):
# Timelock tx to current height.
tx.locktime = get_locktime_for_new_transaction(self.network)
# 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
@ -998,7 +1004,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:

View File

@ -26,6 +26,15 @@
import os
import sys
MIN_PYTHON_VERSION = "3.6.1" # FIXME duplicated from setup.py
_min_python_version_tuple = tuple(map(int, (MIN_PYTHON_VERSION.split("."))))
if sys.version_info[:3] < _min_python_version_tuple:
sys.exit("Error: Electrum requires Python version >= %s..." % MIN_PYTHON_VERSION)
script_dir = os.path.dirname(os.path.realpath(__file__))
is_bundle = getattr(sys, 'frozen', False)
is_local = not is_bundle and os.path.exists(os.path.join(script_dir, "electrum.desktop"))

View File

@ -17,7 +17,7 @@ _min_python_version_tuple = tuple(map(int, (MIN_PYTHON_VERSION.split("."))))
if sys.version_info[:3] < _min_python_version_tuple:
sys.exit("Error: Electrum requires Python version >= {}...".format(MIN_PYTHON_VERSION))
sys.exit("Error: Electrum requires Python version >= %s..." % MIN_PYTHON_VERSION)
with open('contrib/requirements/requirements.txt') as f:
requirements = f.read().splitlines()