Add NFC pushtx from any existing file
This commit is contained in:
parent
85c1a8cc63
commit
de41770853
@ -1412,13 +1412,22 @@ Erases and reformats MicroSD card. This is not a secure erase but more of a quic
|
||||
|
||||
|
||||
async def nfc_share_file(*A):
|
||||
# Mk4: Share txt, txn and PSBT files over NFC.
|
||||
# Share txt, txn and PSBT files over NFC.
|
||||
from glob import NFC
|
||||
try:
|
||||
await NFC.share_file()
|
||||
except Exception as e:
|
||||
await ux_show_story(title="ERROR", msg="Failed to share file. %s" % str(e))
|
||||
|
||||
async def nfc_pushtx_file(*A):
|
||||
# Share a signed txn over NFC using PushTx technology
|
||||
# - requires a signed txn, perhaps from another system on SD card
|
||||
from glob import NFC
|
||||
try:
|
||||
await NFC.push_tx_from_file()
|
||||
except Exception as e:
|
||||
await ux_show_story(title="ERROR", msg="Failed to share file. %s" % str(e))
|
||||
|
||||
|
||||
async def nfc_show_address(*A):
|
||||
from glob import NFC
|
||||
|
||||
@ -806,7 +806,7 @@ class ApproveTransaction(UserAuthorizedAction):
|
||||
except BaseException as exc:
|
||||
return await self.failure("PSBT output failed", exc)
|
||||
|
||||
from glob import NFC, settings
|
||||
from glob import NFC, settings, PSRAM
|
||||
|
||||
if self.do_finalize and txid and not hsm_active:
|
||||
|
||||
@ -814,7 +814,8 @@ class ApproveTransaction(UserAuthorizedAction):
|
||||
url = settings.get('ptxurl', False)
|
||||
if NFC and url:
|
||||
try:
|
||||
await NFC.share_push_tx(url, txid, TXN_OUTPUT_OFFSET, self.result[0], self.result[1])
|
||||
data = PSRAM.read_at(TXN_OUTPUT_OFFSET, self.result[0])
|
||||
await NFC.share_push_tx(url, txid, data, self.result[1])
|
||||
return
|
||||
except:
|
||||
# continue normally if it fails, perhaps too big?
|
||||
|
||||
@ -329,6 +329,7 @@ NFCToolsMenu = [
|
||||
MenuItem('Verify Address', f=nfc_address_verify),
|
||||
MenuItem('File Share', f=nfc_share_file),
|
||||
MenuItem('Import Multisig', f=import_multisig_nfc),
|
||||
MenuItem('NFC Push Tx', f=nfc_pushtx_file, predicate=lambda: settings.get("ptxurl", False)),
|
||||
]
|
||||
|
||||
AdvancedNormalMenu = [
|
||||
|
||||
@ -239,16 +239,13 @@ class NFCHandler:
|
||||
|
||||
return await self.share_start(n)
|
||||
|
||||
async def share_push_tx(self, url, txid, file_offset, txn_len, txn_sha):
|
||||
async def share_push_tx(self, url, txid, txn, txn_sha, line2=None):
|
||||
# Given a signed TXN, we convert to URL which a web backend can broadcast directly
|
||||
# - using base64url encoding
|
||||
# - just appends to provided URL
|
||||
# - keeps showing it until they press CANCEL
|
||||
# - may fail late if txn is too big.. not clear what limit is
|
||||
#
|
||||
if (txn_len * 1.4) >= MAX_NFC_SIZE:
|
||||
raise ValueError('too big')
|
||||
|
||||
from glob import PSRAM
|
||||
from utils import b2a_base64url
|
||||
from chains import current_chain
|
||||
|
||||
@ -256,8 +253,7 @@ class NFCHandler:
|
||||
if is_https:
|
||||
url = url[8:]
|
||||
|
||||
url += 't=' + b2a_base64url(PSRAM.read_at(file_offset, txn_len)) \
|
||||
+ '&c=' + b2a_base64url(txn_sha[-8:])
|
||||
url += 't=' + b2a_base64url(txn) + '&c=' + b2a_base64url(txn_sha[-8:])
|
||||
|
||||
ch = current_chain()
|
||||
if ch.ctype != 'BTC':
|
||||
@ -266,12 +262,69 @@ class NFCHandler:
|
||||
n = ndef.ndefMaker()
|
||||
n.add_url(url, https=is_https)
|
||||
|
||||
if line2 is None:
|
||||
line2 = "Signed TXID: %s⋯%s" % (txid[0:8], txid[-8:])
|
||||
|
||||
while 1:
|
||||
done = await self.share_start(n, prompt="Tap to broadcast, CANCEL when done",
|
||||
line2="Signed TXID: %s⋯%s" % (txid[0:8], txid[-8:]))
|
||||
line2=line2)
|
||||
|
||||
if done: break
|
||||
|
||||
async def push_tx_from_file(self):
|
||||
# Pick (signed txn) file from SD card and broadcast via PushTx
|
||||
# - assumes .txn extension (required)
|
||||
# - hex encoding or binary
|
||||
# - txid is filename, if 64 chars long; else shown on-screen
|
||||
# - assumes txn on same chain as this CC is; ie. not testnet typically
|
||||
from actions import file_picker
|
||||
from files import CardSlot, CardMissingError, needs_microsd
|
||||
from glob import settings
|
||||
|
||||
def is_suitable(fname):
|
||||
return fname.lower().endswith('.txn')
|
||||
|
||||
url = settings.get('ptxurl', False)
|
||||
assert url # or else not in menu, cant get here.
|
||||
|
||||
while 1:
|
||||
fn = await file_picker(min_size=10, max_size=MAX_NFC_SIZE*2, taster=is_suitable)
|
||||
if not fn: return
|
||||
|
||||
basename = fn.split('/')[-1]
|
||||
|
||||
try:
|
||||
with CardSlot() as card:
|
||||
with open(fn, 'rb') as fp:
|
||||
data = fp.read(MAX_NFC_SIZE*2)
|
||||
assert len(data) < MAX_NFC_SIZE*2, "bad read"
|
||||
except CardMissingError:
|
||||
await needs_microsd()
|
||||
return
|
||||
|
||||
# maybe decode
|
||||
if data[2:6] == b'000000':
|
||||
# it's a txn, and we wrote as hex
|
||||
data = a2b_hex(data)
|
||||
elif data[1:4] == bytes(3):
|
||||
# looks like binary
|
||||
pass
|
||||
else:
|
||||
raise ValueError("Doesn't look like txn?")
|
||||
|
||||
sha = ngu.hash.sha256s(data)
|
||||
|
||||
txid = basename[0:64]
|
||||
line2 = None
|
||||
if len(txid) != 64:
|
||||
# assume a r random filename, and not easy to recalc txid here
|
||||
# so show filename instead
|
||||
line2 = 'File: ' + basename
|
||||
if len(line2) > 34: # CHARS_W
|
||||
line2 = line2[:32]+'⋯' # 34-2=32 => because double-width char
|
||||
|
||||
await self.share_push_tx(url, txid, data, sha, line2=line2)
|
||||
|
||||
async def share_psbt(self, file_offset, psbt_len, psbt_sha, label=None):
|
||||
# we just signed something, share it over NFC
|
||||
if psbt_len >= MAX_NFC_SIZE:
|
||||
@ -562,7 +615,7 @@ class NFCHandler:
|
||||
if not fn: return
|
||||
|
||||
basename = fn.split('/')[-1]
|
||||
ctype = fn.split('.')[-1].lower()
|
||||
ext = fn.split('.')[-1].lower()
|
||||
|
||||
try:
|
||||
with CardSlot() as card:
|
||||
@ -573,24 +626,24 @@ class NFCHandler:
|
||||
await needs_microsd()
|
||||
return
|
||||
|
||||
if data[2:6] == b'000000' and ctype == 'txn':
|
||||
if data[2:6] == b'000000' and ext == 'txn':
|
||||
# it's a txn, and we wrote as hex
|
||||
data = a2b_hex(data)
|
||||
|
||||
if ctype == 'psbt':
|
||||
if ext == 'psbt':
|
||||
sha = ngu.hash.sha256s(data)
|
||||
await self.share_psbt(data, len(data), sha, label="PSBT file: " + basename)
|
||||
elif ctype == 'txn':
|
||||
elif ext == 'txn':
|
||||
sha = ngu.hash.sha256s(data)
|
||||
txid = basename[0:64]
|
||||
if len(txid) != 64:
|
||||
# maybe some other txn file?
|
||||
txid = None
|
||||
await self.share_signed_txn(txid, data, len(data), sha)
|
||||
elif ctype == 'txt':
|
||||
elif ext == 'txt':
|
||||
await self.share_text(data.decode())
|
||||
else:
|
||||
raise ValueError(ctype)
|
||||
raise ValueError(ext)
|
||||
|
||||
async def import_multisig_nfc(self, *a):
|
||||
# user is pushing a file downloaded from another CC over NFC
|
||||
|
||||
@ -9,6 +9,36 @@ TAG_DATA = bytearray(8196)
|
||||
# unix/work/nfc-dump.ndef
|
||||
DATA_FILE = 'nfc-dump.ndef'
|
||||
|
||||
def debug_dump_nfc(data):
|
||||
import ndef, sys
|
||||
from utils import B2A
|
||||
|
||||
# Debug aid: pull out URL's and maybe other things
|
||||
|
||||
try:
|
||||
st, ll, *_ = ndef.ccfile_decode(data)
|
||||
|
||||
data = data[st:st+ll]
|
||||
|
||||
if not data:
|
||||
print("sim_nfc: CCFILE is empty (ie. blank tag, no NDEF records)")
|
||||
else:
|
||||
for urn, msg, meta in ndef.record_parser(data):
|
||||
try:
|
||||
msg = bytes(msg).decode() # from memory view
|
||||
except UnicodeError:
|
||||
msg = B2A(msg)
|
||||
|
||||
if urn == 'urn:nfc:wkt:U':
|
||||
prefix = 'https://' if meta.get('prefix') == 4 else 'http://'
|
||||
print("sim_nfc: NDEF URL record:\n\n%s%s\n" % (prefix, msg))
|
||||
else:
|
||||
print("sim_nfc: NDEF record: %s %s meta=%r" % (urn, msg, meta))
|
||||
|
||||
except Exception as exc:
|
||||
print("sim_nfc: Failed to decode CCFILE/NDEFS contained?")
|
||||
sys.print_exception(exc)
|
||||
|
||||
class SimulatedNFCHandler(NFCHandler):
|
||||
def __init__(self):
|
||||
self.rf_on = False
|
||||
@ -34,6 +64,8 @@ class SimulatedNFCHandler(NFCHandler):
|
||||
self._atime = atime
|
||||
print("%d bytes of NDEF written to work/nfc-dump.ndef .. Ctrl-N or touch or read that file to simulate taps" % n)
|
||||
|
||||
debug_dump_nfc(data)
|
||||
|
||||
async def wipe(self, full_wipe):
|
||||
print("NFC chip wiped (full=%d)" % int(full_wipe))
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user