Skip to content

Commit

Permalink
Namecoin: Add name_new command
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyRand committed Oct 3, 2018
1 parent a12b951 commit 1ca766d
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 6 deletions.
47 changes: 43 additions & 4 deletions electrum_nmc/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,20 @@
from . import bitcoin
from .bitcoin import is_address, hash_160, COIN, TYPE_ADDRESS
from .i18n import _
from .names import name_identifier_to_scripthash
from .names import build_name_new, name_identifier_to_scripthash
from .transaction import Transaction, multisig_script, TxOutput
from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED
from .plugin import run_hook

known_commands = {}


class NameNotFoundError(Exception):
pass

class NameAlreadyExistsError(Exception):
pass

def satoshis(amount):
# satoshi conversion must not be performed by the parser
return int(COIN*Decimal(amount)) if amount not in ['!', None] else amount
Expand Down Expand Up @@ -408,16 +414,24 @@ def verifymessage(self, address, signature, message):
message = util.to_bytes(message)
return ecc.verify_message_with_address(address, sig, message)

def _mktx(self, outputs, fee, change_addr, domain, nocheck, unsigned, rbf, password, locktime=None):
def _mktx(self, outputs, fee, change_addr, domain, nocheck, unsigned, rbf, password, locktime=None, name_outputs=[]):
self.nocheck = nocheck
change_addr = self._resolver(change_addr)
domain = None if domain is None else map(self._resolver, domain)
final_outputs = []
for address, amount, name_op in name_outputs:
if address is None:
# TODO: add a memo argument to addrequest
address = self.addrequest(None)['address']
address = self._resolver(address)
amount = satoshis(amount)
final_outputs.append(TxOutput(TYPE_ADDRESS, address, amount, name_op))
for address, amount in outputs:
address = self._resolver(address)
amount = satoshis(amount)
final_outputs.append(TxOutput(TYPE_ADDRESS, address, amount))

# TODO: add the name input if the outputs include a name_firstupdate or name_update transaction
coins = self.wallet.get_spendable_coins(domain, self.config)
tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr)
if locktime != None:
Expand Down Expand Up @@ -446,6 +460,28 @@ def paytomany(self, outputs, fee=None, from_addr=None, change_addr=None, nocheck
tx = self._mktx(outputs, tx_fee, change_addr, domain, nocheck, unsigned, rbf, password, locktime)
return tx.as_dict()

@command('wp')
def name_new(self, identifier, destination=None, amount=0.0, fee=None, from_addr=None, change_addr=None, nocheck=False, unsigned=False, rbf=None, password=None, locktime=None, allow_existing=False):
"""Create a name_new transaction. """
if not allow_existing:
name_exists = True
try:
show = self.name_show(identifier)
except NameNotFoundError:
name_exists = False
if name_exists:
raise NameAlreadyExistsError("The name is already registered")

tx_fee = satoshis(fee)
domain = from_addr.split(',') if from_addr else None

# TODO: support non-ASCII encodings
identifier_bytes = identifier.encode("ascii")
name_op, rand = build_name_new(identifier_bytes)

tx = self._mktx([], tx_fee, change_addr, domain, nocheck, unsigned, rbf, password, locktime, name_outputs=[(destination, amount, name_op)])
return {"tx": tx.as_dict(), "txid": tx.txid(), "rand": bh2u(rand)}

@command('w')
def history(self, year=None, show_addresses=False, show_fiat=False):
"""Wallet history. Returns the transaction history of your wallet."""
Expand Down Expand Up @@ -696,7 +732,7 @@ def name_show(self, identifier):
tx_best = tx_candidate
break
if tx_best is None:
raise Exception("Invalid height")
raise NameNotFoundError("Name never existed, is expired, or is unconfirmed")
txid = tx_best["tx_hash"]
height = tx_best["height"]

Expand Down Expand Up @@ -810,7 +846,10 @@ def help(self):
'show_fiat': (None, "Show fiat value of transactions"),
'year': (None, "Show history for a given year"),
'fee_method': (None, "Fee estimation method to use"),
'fee_level': (None, "Float between 0.0 and 1.0, representing fee slider position")
'fee_level': (None, "Float between 0.0 and 1.0, representing fee slider position"),
'destination': (None, "Namecoin address, contact or alias"),
'amount': (None, "Amount to be sent (in NMC). Type \'!\' to send the maximum available."),
'allow_existing': (None, "Allow pre-registering a name that already is registered"),
}


Expand Down
10 changes: 10 additions & 0 deletions electrum_nmc/names.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ def name_op_to_script(name_op):
raise BitcoinException('unknown name op: {}'.format(name_op))
return script

def build_name_new(identifier, rand = None):
if rand is None:
rand = os.urandom(20)

to_hash = rand + identifier
commitment = hash_160(to_hash)

return {"op": OP_NAME_NEW, "hash": commitment}, rand

def name_identifier_to_scripthash(identifier_bytes):
name_op = {"op": OP_NAME_UPDATE, "name": identifier_bytes, "value": bytes([])}
Expand Down Expand Up @@ -254,9 +262,11 @@ def get_wallet_name_delta(wallet, tx):


import binascii
import os
import re

from .bitcoin import push_script, script_to_scripthash
from .crypto import hash_160
from .transaction import MalformedBitcoinScript, match_decoded, opcodes, script_GetOp
from .util import bh2u

Expand Down
15 changes: 13 additions & 2 deletions electrum_nmc/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
NO_SIGNATURE = 'ff'
PARTIAL_TXN_HEADER_MAGIC = b'EPTF\xff'

NAMECOIN_VERSION = 0x7100


class SerializationError(Exception):
""" Thrown when there's a problem deserializing or serializing """
Expand Down Expand Up @@ -800,6 +802,9 @@ def from_io(klass, inputs, outputs, locktime=0):
self = klass(None)
self._inputs = inputs
self._outputs = outputs
for o in outputs:
if o.name_op is not None:
self.version = NAMECOIN_VERSION
self.locktime = locktime
self.BIP69_sort()
return self
Expand Down Expand Up @@ -1142,7 +1147,13 @@ def input_value(self):
return sum(x['value'] for x in self.inputs())

def output_value(self):
return sum(val for tp, addr, val, name_op in self.outputs())
newly_locked_amount = 0
for o in self.outputs():
if o.name_op is None:
continue
if o.name_op['op'] == OP_NAME_NEW:
newly_locked_amount += COIN // 100
return newly_locked_amount + sum(val for tp, addr, val, name_op in self.outputs())

def get_fee(self):
return self.input_value() - self.output_value()
Expand Down Expand Up @@ -1304,5 +1315,5 @@ def tx_from_str(txt):
return tx_dict["hex"]


from .names import get_name_op_from_output_script, name_op_to_script, split_name_script
from .names import get_name_op_from_output_script, name_op_to_script, OP_NAME_NEW, split_name_script

0 comments on commit 1ca766d

Please sign in to comment.