monero 0.0.0.8 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,49 @@
1
+ class MoneroRPC::Client
2
+ include MoneroRPC::Wallet
3
+ include MoneroRPC::Transfer
4
+
5
+ attr_accessor :host, :port, :username, :password, :debug, :in_transfer_clazz, :out_transfer_clazz
6
+
7
+ def initialize(args= {})
8
+ self.host = args.fetch(:host, MoneroRPC.config.host)
9
+ self.port = args.fetch(:port, MoneroRPC.config.port)
10
+ self.username = args.fetch(:username, MoneroRPC.config.username)
11
+ self.password = args.fetch(:password, MoneroRPC.config.password)
12
+ self.debug = args.fetch(:debug, MoneroRPC.config.debug)
13
+ self.in_transfer_clazz = args.fetch(:in_transfer_clazz, MoneroRPC.config.in_transfer_clazz || "MoneroRPC::IncomingTransfer")
14
+ self.out_transfer_clazz = args.fetch(:out_transfer_clazz, MoneroRPC.config.out_transfer_clazz || "MoneroRPC::OutgoingTransfer")
15
+ end
16
+
17
+ def close!
18
+ request("stop_wallet")
19
+ end
20
+
21
+ def request(method, params="")
22
+ # TODO
23
+ # who can implement digest auth with net/http?
24
+ # this is really ugly!
25
+ data = '{"jsonrpc":"2.0","id":"0","method": "'+method+'", "params": '+params.to_json+' }'
26
+ args = ""
27
+ args << " -s"
28
+ args << " -u #{username}:#{password} --digest"
29
+ args << " -X POST #{base_uri}/json_rpc"
30
+ args << " -d '#{data}'"
31
+ args << " -H 'Content-Type: application/json'"
32
+
33
+ p "curl #{args}" if debug
34
+ json = JSON.parse(`curl #{args}`)
35
+
36
+ # Error handling
37
+ if json["error"]
38
+ raise "#{json["error"]["message"]} | code: #{json["error"]["code"]}"
39
+ end
40
+ json["result"]
41
+ end
42
+
43
+ private
44
+
45
+ def base_uri
46
+ URI.parse("http://#{host}:#{port}")
47
+ end
48
+
49
+ end
@@ -0,0 +1,13 @@
1
+ require 'singleton'
2
+ module MoneroRPC
3
+ class Config
4
+ include Singleton
5
+ attr_accessor :host, :port, :username, :password, :debug, :in_transfer_clazz, :out_transfer_clazz
6
+
7
+ def initialize
8
+ self.in_transfer_clazz = "MoneroRPC::IncomingTransfer"
9
+ self.out_transfer_clazz = "MoneroRPC::OutgoingTransfer"
10
+ end
11
+
12
+ end
13
+ end
@@ -1,6 +1,4 @@
1
- module RPC
2
-
1
+ module MoneroRPC
3
2
  class Payment
4
3
  end
5
-
6
4
  end
@@ -22,34 +22,29 @@
22
22
  # get_tx_hex - boolean; Return the transaction as hex string after sending
23
23
 
24
24
 
25
- module RPC
26
- # => {"integrated_address"=>"A7TmpAyaPeZLnugTKRSwGJhW4vnYv8RAVdRvYyvbistbHUnojyTHyHcYpbZvbTZHDsi4rF1EK5TiYgnCN6FWM9HjfwGRvbCHYCZAaKSzDx", "payment_id"=>"c7e7146b3335aa54"}
27
-
28
- class Transfer
29
-
30
- def self.create(address, amount, args={})
31
- send_bulk([address: address, amount: amount], args)
32
- end
33
-
34
- def self.send_bulk(destinations, args={})
25
+ module MoneroRPC::Transfer
26
+ def create_transfer(address, amount, args={})
27
+ send_bulk_transfer([address: address, amount: amount], args)
28
+ end
35
29
 
36
- mixin = args.fetch(:mixin, 4)
37
- fee = args.fetch(:fee, 1) # ignored anyways
38
- unlock_time = args.fetch(:unlock_time, 0)
39
- payment_id = args.fetch(:payment_id, nil)
40
- get_tx_key = args.fetch(:get_tx_key, true)
41
- priority = args.fetch(:priority, 0)
42
- do_not_relay = args.fetch(:do_not_relay, false)
43
- get_tx_hex = args.fetch(:get_tx_hex, false)
30
+ def send_bulk_transfer(destinations, args={})
44
31
 
32
+ mixin = args.fetch(:mixin, 7)
33
+ fee = args.fetch(:fee, 1) # ignored anyways
34
+ unlock_time = args.fetch(:unlock_time, 0)
35
+ payment_id = args.fetch(:payment_id, nil)
36
+ get_tx_key = args.fetch(:get_tx_key, true)
37
+ priority = args.fetch(:priority, 0)
38
+ do_not_relay = args.fetch(:do_not_relay, false)
39
+ get_tx_hex = args.fetch(:get_tx_hex, false)
45
40
 
46
- options = {
47
- destinations: destinations, fee: fee, mixin: mixin, unlock_time: unlock_time,
48
- payment_id: payment_id, get_tx_key: get_tx_key, priority: priority, do_not_relay: do_not_relay, get_tx_hex: get_tx_hex
49
- }
50
41
 
51
- RPC::Client.request("transfer", options)
52
- end
42
+ options = {
43
+ destinations: destinations, fee: fee, mixin: mixin, unlock_time: unlock_time,
44
+ payment_id: payment_id, get_tx_key: get_tx_key, priority: priority, do_not_relay: do_not_relay, get_tx_hex: get_tx_hex
45
+ }
53
46
 
47
+ request("transfer", options)
54
48
  end
49
+
55
50
  end
@@ -0,0 +1,30 @@
1
+ module MoneroRPC
2
+ class TransferClass
3
+
4
+ attr_accessor :address, :amount, :double_spend_seen, :fee, :height, :note,
5
+ :payment_id, :subaddr_index, :timestamp, :txid, :type, :unlock_time,
6
+ :destinations, :confirmations, :suggested_confirmations_threshold, :subaddr_indices
7
+
8
+ def initialize(args={})
9
+ args.each do |k,v|
10
+ self.send("#{k}=", v)
11
+ end
12
+ end
13
+
14
+ def confirmed?
15
+ confirmations >= 10
16
+ end
17
+
18
+ def pending?
19
+ height == 0
20
+ end
21
+
22
+ def locked?
23
+ raise "TODO"
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ class MoneroRPC::IncomingTransfer < MoneroRPC::TransferClass; end
30
+ class MoneroRPC::OutgoingTransfer < MoneroRPC::TransferClass; end
@@ -0,0 +1,3 @@
1
+ module MoneroRPC
2
+ VERSION = "0.0.12"
3
+ end
@@ -0,0 +1,157 @@
1
+ module MoneroRPC::Wallet
2
+
3
+ def create_address(label="")
4
+ request("create_address", label: label)
5
+ end
6
+
7
+ def get_address
8
+ get_addresses(0, [0]).first["address"]
9
+ end
10
+ alias_method :address, :get_address
11
+
12
+ def get_addresses(account_index=0, address_index=[0])
13
+ request("get_address", {account_index: account_index, address_index: address_index})["addresses"]
14
+ end
15
+
16
+ def getbalance
17
+ request("getbalance")
18
+ end
19
+
20
+ def balance
21
+ XMR.new(getbalance["balance"])
22
+ end
23
+
24
+ def unlocked_balance
25
+ XMR.new(getbalance["unlocked_balance"])
26
+ end
27
+
28
+ def getaddress
29
+ request("getaddress")["address"]
30
+ end
31
+
32
+ def getheight
33
+ request("getheight")["height"]
34
+ end
35
+
36
+ def query_key(type)
37
+ raise ArgumentError unless ["mnemonic", "view_key"].include?(type.to_s)
38
+ request("query_key", {key_type: type})["key"]
39
+ end
40
+ def view_key; query_key(:view_key); end
41
+ def mnemonic_seed; query_key(:mnemonic); end
42
+
43
+
44
+ def make_integrated_address(payment_id = "")
45
+ # TODO
46
+ # Check if payment_id is correct format
47
+ request("make_integrated_address", {payment_id: payment_id})
48
+ end
49
+
50
+ def split_integrated_address(address)
51
+ request("split_integrated_address", {integrated_address: address})
52
+ end
53
+
54
+ def incoming_transfers(type)
55
+ raise ArgumentError unless ["all", "available", "unavailable"].include?(type.to_s)
56
+ json = request("incoming_transfers", {transfer_type: type})
57
+ json["transfers"] || []
58
+ end
59
+
60
+ def get_payments(payment_id)
61
+ payments = request("get_payments", {payment_id: payment_id})["payments"] || []
62
+ # TODO
63
+ # make it a MoneroRPC::Payment that hase a amount as XMR and confirmations (getheight - tx.block_height)
64
+ payments.map{|x| Payment.from_raw(x) }
65
+ end
66
+
67
+ def get_bulk_payments(payment_ids, min_block_height)
68
+ payments = request("get_bulk_payments", {"payment_ids": payment_ids, "min_block_height": min_block_height})
69
+ return payments
70
+ end
71
+
72
+
73
+ # in - boolean;
74
+ # out - boolean;
75
+ # pending - boolean;
76
+ # failed - boolean;
77
+ # pool - boolean;
78
+ # filter_by_height - boolean;
79
+ # min_height - unsigned int;
80
+ # max_height - unsigned int;
81
+ def get_transfers(args={})
82
+ f_in = args.fetch(:in, true)
83
+ out = args.fetch(:out, false)
84
+ pending = args.fetch(:pending, true)
85
+ failed = args.fetch(:failed, false)
86
+ pool = args.fetch(:pool, true)
87
+ filter_by_height = args.fetch(:filter_by_height, false)
88
+ min_height = args.fetch(:min_height, 0)
89
+ max_height = args.fetch(:max_height, 0)
90
+
91
+ options = {in: f_in, out: out, pending: pending, failed: failed, pool: pool, filter_by_height: filter_by_height, min_height: min_height}
92
+ options[:max_height] = max_height if max_height > min_height
93
+
94
+ h = Hash.new
95
+ json = request("get_transfers", options)
96
+ json.map{|k, v|
97
+ h[k] = v.collect{|transfer|
98
+ if k == "in"
99
+ in_transfer_clazz.constantize.new(transfer)
100
+ else
101
+ out_transfer_clazz.constantize.new(transfer)
102
+ end
103
+ }
104
+ }
105
+ return h
106
+ end
107
+
108
+ def get_all_incoming_transfers(args={})
109
+ pending = args.fetch(:pending, true)
110
+ min_height = args.fetch(:min_height, 0)
111
+ max_height = args.fetch(:max_height, 0)
112
+
113
+ all = get_transfers(filter_by_height: true, min_height: min_height, max_height: max_height, in: true, out: false, pending: true, pool: true)
114
+ [ all["in"], all["pending"], all["pool"]].flatten.compact
115
+ end
116
+
117
+ def get_all_outgoing_transfers(args={})
118
+ pending = args.fetch(:pending, true)
119
+ min_height = args.fetch(:min_height, 0)
120
+ max_height = args.fetch(:max_height, 0)
121
+
122
+ all = get_transfers(filter_by_height: true, min_height: min_height, max_height: max_height, in: false, out: true, pending: pending, pool: true)
123
+ [ all["out"], all["pending"], all["pool"]].flatten.compact
124
+ end
125
+
126
+ def get_transfer_by_txid(txid)
127
+ request("get_transfer_by_txid", {txid: txid })
128
+ end
129
+
130
+ # creates a wallet and uses it
131
+ # if wallet exists, will automatically uses it!
132
+ def create_wallet(filename, password, language="English")
133
+ # TODO
134
+ # language correct format?
135
+ options = { filename: filename, password: password, language: language }
136
+ !! request("create_wallet", options)
137
+ end
138
+ alias_method :create, :create_wallet
139
+
140
+ # returns current balance if open successfull
141
+ def open_wallet(filename, password="")
142
+ options = { filename: filename, password: password}
143
+ if request("open_wallet", options)
144
+ balance
145
+ else
146
+ false
147
+ end
148
+ end
149
+ alias_method :open, :open_wallet
150
+
151
+ # stops current MoneroRPC process!
152
+ def stop_wallet
153
+ close!
154
+ end
155
+ alias_method :close, :stop_wallet
156
+
157
+ end
@@ -1,11 +1,11 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'rpc/version'
4
+ require 'monero_rpc/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "monero"
8
- spec.version = RPC::VERSION
8
+ spec.version = MoneroRPC::VERSION
9
9
  spec.authors = ["Tim Kretschmer"]
10
10
  spec.email = ["tim@krtschmr.de"]
11
11
  spec.description = %q{A Monero-Wallet-RPC ruby client}
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler"
22
22
  spec.add_development_dependency "rake"
23
- spec.add_development_dependency 'sqlite3'
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
24
 
25
25
  spec.required_rubygems_version = '>= 1.3.6'
26
26
 
@@ -0,0 +1,109 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe MoneroRPC do
4
+ let(:monero_rpc) { MoneroRPC.new }
5
+
6
+ it "has a version number" do
7
+ expect(MoneroRPC::VERSION).not_to be nil
8
+ end
9
+
10
+ it "gets the current address" do
11
+ address = monero_rpc.address
12
+ expect(address.length).to be 95
13
+ end
14
+
15
+ it "creates a new subaddress with a label" do
16
+ subaddress = monero_rpc.create_address "wallet1"
17
+ expect(subaddress['address'].length).to be 95
18
+ expect(subaddress['address_index']).to be_truthy
19
+ end
20
+
21
+ it "creates an integrated address for a payment" do
22
+ address = monero_rpc.make_integrated_address
23
+ payment_id = address['payment_id']
24
+ expect(address['integrated_address'].length).to be 106
25
+ expect(payment_id.length).to be 16
26
+ end
27
+
28
+ it "lists all addresses of the wallet" do
29
+ addresses = monero_rpc.get_addresses
30
+ addresses.each do |adr|
31
+ expect(adr['address']).to be_a(String)
32
+ expect(adr['address'].length).to be 95
33
+ expect(adr['address_index']).to be_an(Integer)
34
+ expect(adr['label']).to be_a(String)
35
+ expect(adr['used']).to be(true).or(be(false))
36
+ end
37
+ end
38
+
39
+ it "gets the balance of the current wallet" do
40
+ balance = monero_rpc.balance
41
+ expect(balance).to be_a(Money)
42
+ expect(balance.fractional).to be_an(Integer)
43
+ expect(balance.currency).to be_a(Money::Currency)
44
+ end
45
+
46
+ it "returns a formatted balance with dot as decimal separator" do
47
+ balance = monero_rpc.balance.format
48
+ expect(balance).to be_a(String)
49
+ expect(balance).to include('.')
50
+ end
51
+
52
+ it "gets the unlocked balance" do
53
+ balance = monero_rpc.unlocked_balance
54
+ expect(balance).to be_a(Money)
55
+ expect(balance.fractional).to be_an(Integer)
56
+ expect(balance.currency).to be_a(Money::Currency)
57
+ end
58
+
59
+ it "gets the balance and unlocked balance" do
60
+ balance = monero_rpc.getbalance
61
+ expect(balance['balance']).to be_an(Integer)
62
+ expect(balance['unlocked_balance']).to be_an(Integer)
63
+ end
64
+
65
+ it "gets the current block height" do
66
+ height = monero_rpc.getheight
67
+ expect(height).to be_an(Integer)
68
+ end
69
+
70
+ # the wallet locks for approx 10 blocks so this test will fail if
71
+ # balance is not yet unlocked
72
+ it "sends XMR to a standard address" do
73
+ subaddress = monero_rpc.create_address "receiving_wallet"
74
+ amount = 20075
75
+ transfer = monero_rpc.create_transfer(subaddress['address'], amount)
76
+ expect(transfer['amount']).to eq(amount)
77
+ expect(transfer['fee']).to be_an(Integer)
78
+ expect(transfer['multisig_txset']).to be_empty
79
+ expect(transfer['tx_blob']).to be_empty
80
+ expect(transfer['tx_hash']).to be_truthy
81
+ expect(transfer['tx_key']).to be_truthy
82
+ expect(transfer['tx_metadata']).to be_empty
83
+ expect(transfer['unsigned_txset']).to be_empty
84
+ end
85
+
86
+ it "sends XMR to multiple recipients" do
87
+ subaddress1 = monero_rpc.create_address "receiving_wallet"
88
+ subaddress2 = monero_rpc.create_address "receiving_wallet2"
89
+ amount1 = 20075
90
+ amount2 = 30075
91
+
92
+ recipients = [
93
+ { address: subaddress1['address'], amount: amount1 },
94
+ { address: subaddress2['address'], amount: amount2 }
95
+ ]
96
+
97
+ transfer = monero_rpc.send_bulk_transfer(recipients)
98
+
99
+ expect(transfer['amount']).to eq(amount1+amount2)
100
+ expect(transfer['fee']).to be_an(Integer)
101
+ expect(transfer['multisig_txset']).to be_empty
102
+ expect(transfer['tx_blob']).to be_empty
103
+ expect(transfer['tx_hash']).to be_truthy
104
+ expect(transfer['tx_key']).to be_truthy
105
+ expect(transfer['tx_metadata']).to be_empty
106
+ expect(transfer['unsigned_txset']).to be_empty
107
+ end
108
+
109
+ end