24 from jsonrpc
import ServiceProxy, json
26 BASE_FEE=Decimal(
"0.001")
29 """Make sure json library being used does not lose precision converting BTC values""" 30 n = Decimal(
"20000000.00000003")
31 satoshis = int(json.loads(json.dumps(float(n)))*1.0e8)
32 if satoshis != 2000000000000003:
33 raise RuntimeError(
"JSON encode/decode loses precision")
36 """Return the default location of the Dash Core data directory""" 37 if platform.system() ==
"Darwin":
38 return os.path.expanduser(
"~/Library/Application Support/DashCore/")
39 elif platform.system() ==
"Windows":
40 return os.path.join(os.environ[
'APPDATA'],
"DashCore")
41 return os.path.expanduser(
"~/.dashcore")
44 """Read the dash.conf file from dbdir, returns dictionary of settings""" 45 from ConfigParser
import SafeConfigParser
47 class FakeSecHead(object):
48 def __init__(self, fp):
50 self.sechead =
'[all]\n' 53 try:
return self.sechead
54 finally: self.sechead =
None 56 s = self.fp.readline()
58 s = s[0:s.find(
'#')].strip() +
"\n" 61 config_parser = SafeConfigParser()
62 config_parser.readfp(FakeSecHead(open(os.path.join(dbdir,
"dash.conf"))))
63 return dict(config_parser.items(
"all"))
66 """Connect to a Dash Core JSON-RPC server""" 67 testnet = config.get(
'testnet',
'0')
68 testnet = (int(testnet) > 0)
69 if not 'rpcport' in config:
70 config[
'rpcport'] = 19998
if testnet
else 9998
71 connect =
"http://%s:%s@127.0.0.1:%s"%(config[
'rpcuser'], config[
'rpcpassword'], config[
'rpcport'])
73 result = ServiceProxy(connect)
76 if result.getmininginfo()[
'testnet'] != testnet:
77 sys.stderr.write(
"RPC server at "+connect+
" testnet setting mismatch\n")
81 sys.stderr.write(
"Error connecting to RPC server at "+connect+
"\n")
85 info = dashd.getinfo()
86 if 'unlocked_until' not in info:
88 t = int(info[
'unlocked_until'])
91 passphrase = getpass.getpass(
"Wallet is locked; enter passphrase: ")
92 dashd.walletpassphrase(passphrase, 5)
94 sys.stderr.write(
"Wrong passphrase\n")
96 info = dashd.getinfo()
97 return int(info[
'unlocked_until']) > time.time()
100 address_summary = dict()
102 address_to_account = dict()
103 for info
in dashd.listreceivedbyaddress(0):
104 address_to_account[info[
"address"]] = info[
"account"]
106 unspent = dashd.listunspent(0)
107 for output
in unspent:
109 rawtx = dashd.getrawtransaction(output[
'txid'], 1)
110 vout = rawtx[
"vout"][output[
'vout']]
111 pk = vout[
"scriptPubKey"]
115 if pk[
"type"] !=
"pubkeyhash" and pk[
"type"] !=
"scripthash":
118 address = pk[
"addresses"][0]
119 if address
in address_summary:
120 address_summary[address][
"total"] += vout[
"value"]
121 address_summary[address][
"outputs"].append(output)
123 address_summary[address] = {
124 "total" : vout[
"value"],
125 "outputs" : [output],
126 "account" : address_to_account.get(address,
"")
129 return address_summary
134 have = Decimal(
"0.0")
136 while have < needed
and n < len(inputs):
137 outputs.append({
"txid":inputs[n][
"txid"],
"vout":inputs[n][
"vout"]})
138 have += inputs[n][
"amount"]
140 return (outputs, have-needed)
142 def create_tx(dashd, fromaddresses, toaddress, amount, fee):
145 total_available = Decimal(
"0.0")
147 potential_inputs = []
148 for addr
in fromaddresses:
149 if addr
not in all_coins:
151 potential_inputs.extend(all_coins[addr][
"outputs"])
152 total_available += all_coins[addr][
"total"]
154 if total_available < needed:
155 sys.stderr.write(
"Error, only %f BTC available, need %f\n"%(total_available, needed));
164 outputs = { toaddress : float(amount) }
165 (inputs, change_amount) =
select_coins(needed, potential_inputs)
166 if change_amount > BASE_FEE:
167 change_address = fromaddresses[-1]
168 if change_address
in outputs:
169 outputs[change_address] += float(change_amount)
171 outputs[change_address] = float(change_amount)
173 rawtx = dashd.createrawtransaction(inputs, outputs)
174 signed_rawtx = dashd.signrawtransaction(rawtx)
175 if not signed_rawtx[
"complete"]:
176 sys.stderr.write(
"signrawtransaction failed\n")
178 txdata = signed_rawtx[
"hex"]
183 result = Decimal(
"0.0")
184 for vin
in txinfo[
'vin']:
185 in_info = dashd.getrawtransaction(vin[
'txid'], 1)
186 vout = in_info[
'vout'][vin[
'vout']]
187 result = result + vout[
'value']
191 result = Decimal(
"0.0")
192 for vout
in txinfo[
'vout']:
193 result = result + vout[
'value']
197 class FeeError(RuntimeError):
200 txinfo = dashd.decoderawtransaction(txdata_hex)
203 if total_in-total_out > max_fee:
204 raise FeeError(
"Rejecting transaction, unreasonable fee of "+str(total_in-total_out))
206 tx_size = len(txdata_hex)/2
208 if kb > 1
and fee < BASE_FEE:
209 raise FeeError(
"Rejecting no-fee transaction, larger than 1000 bytes")
210 if total_in < 0.01
and fee < BASE_FEE:
211 raise FeeError(
"Rejecting no-fee, tiny-amount transaction")
215 except FeeError
as err:
216 sys.stderr.write((str(err)+
"\n"))
222 parser = optparse.OptionParser(usage=
"%prog [options]")
223 parser.add_option(
"--from", dest=
"fromaddresses", default=
None,
224 help=
"addresses to get dashs from")
225 parser.add_option(
"--to", dest=
"to", default=
None,
226 help=
"address to get send dashs to")
227 parser.add_option(
"--amount", dest=
"amount", default=
None,
228 help=
"amount to send")
229 parser.add_option(
"--fee", dest=
"fee", default=
"0.0",
230 help=
"fee to include")
232 help=
"location of dash.conf file with RPC username/password (default: %default)")
233 parser.add_option(
"--testnet", dest=
"testnet", default=
False, action=
"store_true",
234 help=
"Use the test network")
235 parser.add_option(
"--dry_run", dest=
"dry_run", default=
False, action=
"store_true",
236 help=
"Don't broadcast the transaction, just create and print the transaction data")
238 (options, args) = parser.parse_args()
242 if options.testnet: config[
'testnet'] =
True 245 if options.amount
is None:
247 for address,info
in address_summary.iteritems():
248 n_transactions = len(info[
'outputs'])
249 if n_transactions > 1:
250 print(
"%s %.8f %s (%d transactions)"%(address, info[
'total'], info[
'account'], n_transactions))
252 print(
"%s %.8f %s"%(address, info[
'total'], info[
'account']))
254 fee = Decimal(options.fee)
255 amount = Decimal(options.amount)
258 txdata =
create_tx(dashd, options.fromaddresses.split(
","), options.to, amount, fee)
263 txid = dashd.sendrawtransaction(txdata)
266 if __name__ ==
'__main__':
def select_coins(needed, inputs)
def check_json_precision()
def compute_amount_in(dashd, txinfo)
def create_tx(dashd, fromaddresses, toaddress, amount, fee)
def list_available(dashd)
def sanity_test_fee(dashd, txdata_hex, max_fee)
def read_bitcoin_config(dbdir)
def compute_amount_out(txinfo)