counterparty_ruby 0.9.0 → 1.1.0
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.
- data/Gemfile.lock +16 -6
- data/README.md +75 -6
- data/counterparty_ruby.gemspec +1 -1
- data/lib/blockr_io.rb +42 -0
- data/lib/counterparty/connection.rb +3 -135
- data/lib/counterparty/raw_tx.rb +1 -1
- data/lib/counterparty/resource.rb +36 -65
- data/lib/counterparty/resources.rb +21 -2
- data/lib/counterparty/version.rb +1 -1
- data/lib/counterparty_ruby.rb +14 -2
- data/spec/blockr_io_spec.rb +68 -0
- data/spec/connection_spec.rb +4 -4
- data/spec/exceptions_spec.rb +7 -2
- data/spec/rawtx_decode_spec.rb +0 -4
- data/spec/resource_create_spec.rb +14 -11
- data/spec/resource_read_spec.rb +4 -1
- data/spec/spec_helper.rb +15 -3
- metadata +6 -3
data/Gemfile.lock
CHANGED
@@ -1,24 +1,31 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
counterparty_ruby (
|
4
|
+
counterparty_ruby (1.1.0)
|
5
5
|
bitcoin-ruby
|
6
6
|
ffi
|
7
|
-
|
7
|
+
rest-client
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
12
|
bitcoin-ruby (0.0.6)
|
13
13
|
diff-lcs (1.2.5)
|
14
|
-
|
14
|
+
domain_name (0.5.23)
|
15
|
+
unf (>= 0.0.5, < 1.0.0)
|
16
|
+
ffi (1.9.8)
|
17
|
+
http-cookie (1.0.2)
|
18
|
+
domain_name (~> 0.5)
|
15
19
|
json (1.8.1)
|
16
|
-
|
20
|
+
mime-types (2.4.3)
|
21
|
+
netrc (0.10.3)
|
17
22
|
rake (10.3.2)
|
18
23
|
rdoc (4.2.0)
|
19
24
|
json (~> 1.4)
|
20
|
-
|
21
|
-
|
25
|
+
rest-client (1.8.0)
|
26
|
+
http-cookie (>= 1.0.2, < 2.0)
|
27
|
+
mime-types (>= 1.16, < 3.0)
|
28
|
+
netrc (~> 0.7)
|
22
29
|
rspec (3.1.0)
|
23
30
|
rspec-core (~> 3.1.0)
|
24
31
|
rspec-expectations (~> 3.1.0)
|
@@ -34,6 +41,9 @@ GEM
|
|
34
41
|
rspec-mocks (3.1.3)
|
35
42
|
rspec-support (~> 3.1.0)
|
36
43
|
rspec-support (3.1.2)
|
44
|
+
unf (0.1.4)
|
45
|
+
unf_ext
|
46
|
+
unf_ext (0.0.6)
|
37
47
|
|
38
48
|
PLATFORMS
|
39
49
|
ruby
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
counterparty_ruby
|
1
|
+
counterparty_ruby [](http://badge.fury.io/rb/counterparty_ruby)
|
2
2
|
=================
|
3
3
|
A ruby gem for communicating with a Counterparty (Bitcoin / XCP) API server.
|
4
4
|
|
@@ -25,10 +25,22 @@ setting everything up.
|
|
25
25
|
|
26
26
|

|
27
27
|
|
28
|
+
## Changelog
|
29
|
+
_Version 1.1_
|
30
|
+
* Minor Updates to support changes in counterpartyd 1.1
|
31
|
+
* Removed support for relaying transactions through counterparty
|
32
|
+
* Implemented bitcoin-ruby based transaction signing
|
33
|
+
* Added a blockr.io library for relaying signed transactions via http
|
34
|
+
* save() syntax now requires that a private key be provided to persist transactions
|
35
|
+
* Default test and production counterparty servers were changed to the vennd.io public servers
|
36
|
+
|
37
|
+
_Version 0.9_
|
38
|
+
* First release!
|
39
|
+
|
28
40
|
## Examples
|
29
41
|
Documentation on the objects is available via:
|
30
42
|
* [counterparty_ruby's rubydoc](http://www.rubydoc.info/github/brighton36/counterparty_ruby/master),
|
31
|
-
* [The Counterparty official API guide](
|
43
|
+
* [The Counterparty official API guide](http://counterparty.io/docs/api/).
|
32
44
|
|
33
45
|
#### Find the first burn
|
34
46
|
Here we retrieve burns from the blockchain using ActiveRecord style method calls.
|
@@ -78,7 +90,7 @@ Here we create an asset and persist that asset intothe blockchain using ActiveRe
|
|
78
90
|
divisible: false,
|
79
91
|
quantity: 100 )
|
80
92
|
|
81
|
-
transaction_id = first_asset.save!
|
93
|
+
transaction_id = first_asset.save!('private-key-here')
|
82
94
|
|
83
95
|
puts "Transaction %s has been entered into the mempool" % transaction_id
|
84
96
|
```
|
@@ -125,7 +137,64 @@ an outcome to the network.
|
|
125
137
|
|
126
138
|
puts "Gold was broadcast down in transaction %s" % tx_id
|
127
139
|
```
|
140
|
+
#### Broadcast a Feed, Place Bets, Resolve the Bet
|
141
|
+
In this example, we declare a bet that open, and have two people bet each other
|
142
|
+
on the outcome. The bet is then resolved.
|
143
|
+
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
require 'counterparty_ruby'
|
147
|
+
require 'active_support/time'
|
148
|
+
require "active_support/core_ext/numeric/time"
|
149
|
+
|
150
|
+
TEAM_BLUE_WINS = 1
|
151
|
+
TEAM_RED_WINS = 2
|
152
|
+
|
153
|
+
ALICE_ADDRESS = 'n4m2u8GwmFB8VDE1szCTkX5ikEEQLiR2Kj'
|
154
|
+
JOHN_ADDRESS = 'mu23MfDNhYmQkJF36aJZ783dLDMrqUi9Fa'
|
155
|
+
|
156
|
+
ORACLE_ADDRESS = 'msCXwsPVbv1Q1pc5AjXd5TdVwy3a1fSYB2'
|
157
|
+
|
158
|
+
Counterparty.test!
|
159
|
+
|
160
|
+
broadcast_text = "Winner of game, %s. %s=red %s=blue" % [
|
161
|
+
Time.now.strftime("%I%p %Z %b%-d"), TEAM_RED_WINS, TEAM_BLUE_WINS]
|
162
|
+
|
163
|
+
# Announce the availability of a Bet:
|
164
|
+
# NOTE: All times are in UTC
|
165
|
+
tx_init = Counterparty::Broadcast.new( source: ORACLE_ADDRESS,
|
166
|
+
value: Counterparty::Broadcast::OPEN_BROADCAST, timestamp: Time.now.to_i,
|
167
|
+
text: broadcast_text, fee_fraction: 0.00, allow_unconfirmed_inputs: true ).save!('private-key-here')
|
168
|
+
|
169
|
+
# Alice Bets on Blue:
|
170
|
+
tx_bet_on_blue = Counterparty::Bet.new(source: ALICE_ADDRESS,
|
171
|
+
feed_address: ORACLE_ADDRESS, bet_type: Counterparty::Bet::EQUAL,
|
172
|
+
deadline: 10.minutes.from_now.to_i, wager_quantity: 5,
|
173
|
+
counterwager_quantity: 1, expiration: 5,
|
174
|
+
target_value: TEAM_BLUE_WINS, leverage: Counterparty::Bet::LEVERAGE_BASIS,
|
175
|
+
allow_unconfirmed_inputs: true ).save!('private-key-here')
|
176
|
+
|
177
|
+
puts "Alice on Blue: %s" % tx_bet_on_blue
|
128
178
|
|
179
|
+
# John Bets on Red:
|
180
|
+
tx_bet_on_red = Counterparty::Bet.new(source: JOHN_ADDRESS,
|
181
|
+
feed_address: ORACLE_ADDRESS, bet_type: Counterparty::Bet::EQUAL,
|
182
|
+
deadline: 10.minutes.from_now.to_i, wager_quantity: 5,
|
183
|
+
counterwager_quantity: 1, expiration: 5,
|
184
|
+
target_value: TEAM_RED_WINS, leverage: Counterparty::Bet::LEVERAGE_BASIS,
|
185
|
+
allow_unconfirmed_inputs: true ).save!('private-key-here')
|
186
|
+
|
187
|
+
puts "John on Red: %s" % tx_bet_on_red
|
188
|
+
|
189
|
+
# Close the broadcast : Team Blue wins!
|
190
|
+
tx_outcome = Counterparty::Broadcast.new( source: ORACLE_ADDRESS,
|
191
|
+
value: TEAM_BLUE_WINS, timestamp: 20.minutes.from_now.to_i,
|
192
|
+
fee_fraction: 0.00,
|
193
|
+
text: broadcast_text, allow_unconfirmed_inputs: true ).save!('private-key-here')
|
194
|
+
|
195
|
+
puts "Oracle says: %s" % tx_outcome
|
196
|
+
|
197
|
+
```
|
129
198
|
#### Compile, Publish and Execute a Serpent Contract
|
130
199
|
This is still beta behavior, and only supported on testnet, but here's a quick
|
131
200
|
example of how Smart Contracts are published and executed. Note that we require
|
@@ -171,19 +240,19 @@ the serpent CLI executable is installed on the running system
|
|
171
240
|
|
172
241
|
contract_id = Counterparty::Publish.new( source: SOURCE_ADDRESS,
|
173
242
|
code_hex: compiled_script, gasprice: 1, startgas: 1000000, endowment: 0,
|
174
|
-
allow_unconfirmed_inputs: true ).save!
|
243
|
+
allow_unconfirmed_inputs: true ).save!('private-key-here')
|
175
244
|
|
176
245
|
datalist = serpent.encode_datalist '53'
|
177
246
|
|
178
247
|
execute_id = Counterparty::Execute.new( source: SOURCE_ADDRESS,
|
179
248
|
contract_id: contract_id, payload_hex: datalist, gasprice: 5,
|
180
|
-
startgas: 160000, value: 10, allow_unconfirmed_inputs: true ).save!
|
249
|
+
startgas: 160000, value: 10, allow_unconfirmed_inputs: true ).save!('private-key-here')
|
181
250
|
|
182
251
|
puts "Executed Transaction ID: %s" % execute_id
|
183
252
|
```
|
184
253
|
|
185
254
|
## Have questions?
|
186
|
-
The _best_ place to start is the [Counterparty API reference](
|
255
|
+
The _best_ place to start is the [Counterparty API reference](http://counterparty.io/docs/api/).
|
187
256
|
You'll soon find that this gem is merely a wrapper around the official
|
188
257
|
counterpartyd json API.
|
189
258
|
|
data/counterparty_ruby.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.required_ruby_version = '>= 1.9'
|
17
17
|
gem.license = 'LGPL'
|
18
18
|
|
19
|
-
['
|
19
|
+
['rest-client', 'bitcoin-ruby', 'ffi'].each do |dependency|
|
20
20
|
gem.add_runtime_dependency dependency
|
21
21
|
end
|
22
22
|
['rspec', 'rspec-its', 'rake', 'rdoc'].each do |dependency|
|
data/lib/blockr_io.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# This is a 'driver' meant to emulate bitcoin-client calls, by way of the blockr.io
|
2
|
+
# API
|
3
|
+
class BlockrIo
|
4
|
+
class ResponseError < StandardError; end
|
5
|
+
|
6
|
+
def initialize(is_testing = false)
|
7
|
+
@is_testing = is_testing
|
8
|
+
end
|
9
|
+
|
10
|
+
def api_url
|
11
|
+
'http://%s.blockr.io/api/v1' % (is_testing? ? 'tbtc' : 'btc')
|
12
|
+
end
|
13
|
+
|
14
|
+
def getrawtransaction(tx_id)
|
15
|
+
json_get('tx', 'raw', tx_id.to_s)['data']['tx']['hex']
|
16
|
+
end
|
17
|
+
|
18
|
+
def sendrawtransaction(raw_tx)
|
19
|
+
request('tx', 'push'){|req| req.post( {hex: raw_tx}.to_json,
|
20
|
+
accept: 'json', content_type: 'json' ) }['data']
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_testing?
|
24
|
+
@is_testing
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def request(*path, &block)
|
30
|
+
json = JSON.parse(block.call(client(*path)))
|
31
|
+
raise ResponseError unless json['status'] == 'success' && json['code'] == 200
|
32
|
+
json
|
33
|
+
end
|
34
|
+
|
35
|
+
def client(*path_parts)
|
36
|
+
RestClient::Resource.new( ([api_url]+path_parts).join('/') )
|
37
|
+
end
|
38
|
+
|
39
|
+
def json_get(*path)
|
40
|
+
request(*path){ |req| req.get content_type: 'json' }
|
41
|
+
end
|
42
|
+
end
|
@@ -13,7 +13,7 @@ module Counterparty
|
|
13
13
|
# which means the library will wait indefinitely before timing out
|
14
14
|
attr_writer :timeout
|
15
15
|
|
16
|
-
def initialize(port=
|
16
|
+
def initialize(port=4000, username='counterparty', password='1234', host='xcp-dev.vennd.io')
|
17
17
|
@host,@port,@username,@password=host.to_s,port.to_i,username.to_s,password.to_s
|
18
18
|
@timeout = DEFAULT_TIMEOUT
|
19
19
|
end
|
@@ -28,17 +28,12 @@ module Counterparty
|
|
28
28
|
request 'sign_tx', unsigned_tx_hex: raw_tx, privkey: private_key
|
29
29
|
end
|
30
30
|
|
31
|
-
# Broadcasts a signed transaction onto the bitcoin blockchain
|
32
|
-
def broadcast_tx(signed_tx)
|
33
|
-
request 'broadcast_tx', signed_tx_hex: signed_tx
|
34
|
-
end
|
35
|
-
|
36
31
|
# Issue a request to the counterpartyd server for the given method, with the
|
37
32
|
# given params.
|
38
33
|
def request(method, params)
|
39
34
|
client = RestClient::Resource.new api_url, :timeout => @timeout
|
40
|
-
|
41
|
-
|
35
|
+
request = { method: method, params: params, jsonrpc: '2.0', id: '0' }.to_json
|
36
|
+
response = JSON.parse client.post(request,
|
42
37
|
user: @username, password: @password, accept: 'json',
|
43
38
|
content_type: 'json' )
|
44
39
|
|
@@ -53,136 +48,9 @@ module Counterparty
|
|
53
48
|
# as to be future-proof here going forward
|
54
49
|
def self.resource_request(klass) # :nodoc:
|
55
50
|
define_method(klass.to_get_request){ |params| klass.find params }
|
56
|
-
define_method(klass.to_do_request){ |params| klass.create(params).save! }
|
57
51
|
define_method(klass.to_create_request){ |params| klass.create(params).to_raw_tx }
|
58
52
|
end
|
59
53
|
|
60
|
-
# :method: do_balance
|
61
|
-
# # Sends a do_balance call to the counterpartyd server, given the provided params
|
62
|
-
|
63
|
-
# :method: create_balance
|
64
|
-
# # Sends a create_balance call to the counterpartyd server, given the provided params
|
65
|
-
|
66
|
-
# :method: do_bet
|
67
|
-
# # Sends a do_bet call to the counterpartyd server, given the provided params
|
68
|
-
|
69
|
-
# :method: create_bet
|
70
|
-
# # Sends a create_bet call to the counterpartyd server, given the provided params
|
71
|
-
|
72
|
-
# :method: do_betmatch
|
73
|
-
# # Sends a do_betmatch call to the counterpartyd server, given the provided params
|
74
|
-
|
75
|
-
# :method: create_betmatch
|
76
|
-
# # Sends a create_betmatch call to the counterpartyd server, given the provided params
|
77
|
-
|
78
|
-
# :method: do_broadcast
|
79
|
-
# # Sends a do_broadcast call to the counterpartyd server, given the provided params
|
80
|
-
|
81
|
-
# :method: create_broadcast
|
82
|
-
# # Sends a create_broadcast call to the counterpartyd server, given the provided params
|
83
|
-
|
84
|
-
# :method: do_btcpays
|
85
|
-
# # Sends a do_btcpays call to the counterpartyd server, given the provided params
|
86
|
-
|
87
|
-
# :method: create_btcpay
|
88
|
-
# # Sends a create_btcpay call to the counterpartyd server, given the provided params
|
89
|
-
|
90
|
-
# :method: do_burn
|
91
|
-
# # Sends a do_burn call to the counterpartyd server, given the provided params
|
92
|
-
|
93
|
-
# :method: create_burn
|
94
|
-
# # Sends a create_burn call to the counterpartyd server, given the provided params
|
95
|
-
|
96
|
-
# :method: do_callback
|
97
|
-
# # Sends a do_callback call to the counterpartyd server, given the provided params
|
98
|
-
|
99
|
-
# :method: create_callback
|
100
|
-
# # Sends a create_callback call to the counterpartyd server, given the provided params
|
101
|
-
|
102
|
-
# :method: do_cancel
|
103
|
-
# # Sends a do_cancel call to the counterpartyd server, given the provided params
|
104
|
-
|
105
|
-
# :method: create_cancel
|
106
|
-
# # Sends a create_cancel call to the counterpartyd server, given the provided params
|
107
|
-
|
108
|
-
# :method: do_credit
|
109
|
-
# # Sends a do_credit call to the counterpartyd server, given the provided params
|
110
|
-
|
111
|
-
# :method: create_credit
|
112
|
-
# # Sends a create_credit call to the counterpartyd server, given the provided params
|
113
|
-
|
114
|
-
# :method: do_debit
|
115
|
-
# # Sends a do_debit call to the counterpartyd server, given the provided params
|
116
|
-
|
117
|
-
# :method: create_debit
|
118
|
-
# # Sends a create_debit call to the counterpartyd server, given the provided params
|
119
|
-
|
120
|
-
# :method: do_dividend
|
121
|
-
# # Sends a do_dividend call to the counterpartyd server, given the provided params
|
122
|
-
|
123
|
-
# :method: create_dividend
|
124
|
-
# # Sends a create_dividend call to the counterpartyd server, given the provided params
|
125
|
-
|
126
|
-
# :method: do_issuance
|
127
|
-
# # Sends a do_issuance call to the counterpartyd server, given the provided params
|
128
|
-
|
129
|
-
# :method: create_issuance
|
130
|
-
# # Sends a create_issuance call to the counterpartyd server, given the provided params
|
131
|
-
|
132
|
-
# :method: do_order
|
133
|
-
# # Sends a do_order call to the counterpartyd server, given the provided params
|
134
|
-
|
135
|
-
# :method: create_order
|
136
|
-
# # Sends a create_order call to the counterpartyd server, given the provided params
|
137
|
-
|
138
|
-
# :method: do_ordermatch
|
139
|
-
# # Sends a do_ordermatch call to the counterpartyd server, given the provided params
|
140
|
-
|
141
|
-
# :method: create_ordermatch
|
142
|
-
# # Sends a create_ordermatch call to the counterpartyd server, given the provided params
|
143
|
-
|
144
|
-
# :method: do_send
|
145
|
-
# # Sends a do_send call to the counterpartyd server, given the provided params
|
146
|
-
|
147
|
-
# :method: create_send
|
148
|
-
# # Sends a create_send call to the counterpartyd server, given the provided params
|
149
|
-
|
150
|
-
# :method: do_message
|
151
|
-
# # Sends a do_message call to the counterpartyd server, given the provided params
|
152
|
-
|
153
|
-
# :method: create_message
|
154
|
-
# # Sends a create_message call to the counterpartyd server, given the provided params
|
155
|
-
|
156
|
-
# :method: do_callback
|
157
|
-
# # Sends a do_callback call to the counterpartyd server, given the provided params
|
158
|
-
|
159
|
-
# :method: create_callback
|
160
|
-
# # Sends a create_callback call to the counterpartyd server, given the provided params
|
161
|
-
|
162
|
-
# :method: do_betexpiration
|
163
|
-
# # Sends a do_betexpiration call to the counterpartyd server, given the provided params
|
164
|
-
|
165
|
-
# :method: create_betexpiration
|
166
|
-
# # Sends a create_betexpiration call to the counterpartyd server, given the provided params
|
167
|
-
|
168
|
-
# :method: do_orderexpiration
|
169
|
-
# # Sends a do_orderexpiration call to the counterpartyd server, given the provided params
|
170
|
-
|
171
|
-
# :method: create_orderexpiration
|
172
|
-
# # Sends a create_orderexpiration call to the counterpartyd server, given the provided params
|
173
|
-
|
174
|
-
# :method: do_betmatchexpiration
|
175
|
-
# # Sends a do_betmatchexpiration call to the counterpartyd server, given the provided params
|
176
|
-
|
177
|
-
# :method: create_betmatchexpiration
|
178
|
-
# # Sends a create_betmatchexpiration call to the counterpartyd server, given the provided params
|
179
|
-
|
180
|
-
# :method: do_ordermatchexpiration
|
181
|
-
# # Sends a do_ordermatchexpiration call to the counterpartyd server, given the provided params
|
182
|
-
|
183
|
-
# :method: create_ordermatchexpiration
|
184
|
-
# # Sends a create_ordermatchexpiration call to the counterpartyd server, given the provided params
|
185
|
-
|
186
54
|
# Go ahead and setup the defined resources, and throw them into the native-style
|
187
55
|
# api methods:
|
188
56
|
Counterparty.constants.each do |c|
|
data/lib/counterparty/raw_tx.rb
CHANGED
@@ -65,7 +65,7 @@ class RawTx
|
|
65
65
|
# Convert an array of 8 bit numbers into an unsigned int,
|
66
66
|
# Remember that for each input byte, the most significant nibble comes last.
|
67
67
|
def self.bytes_to_ui(bytes)
|
68
|
-
nibbles = bytes.collect{|b| [b & 0x0f, b >> 4]}.flatten
|
68
|
+
nibbles = bytes.collect{|b| [b & 0x0f, (b & 0xf0) >> 4]}.flatten
|
69
69
|
nibbles.each_with_index.inject(0){|sum,(b,i)| sum += b * 16**i}
|
70
70
|
end
|
71
71
|
|
@@ -47,9 +47,6 @@ module Counterparty
|
|
47
47
|
|
48
48
|
# Given the provided private key, this method returns a signed transaction
|
49
49
|
# suitable for broadcasting on the network.
|
50
|
-
#
|
51
|
-
# NOTE: This method communicates your private key to the counterpartyd
|
52
|
-
# server, which might not be what you want!
|
53
50
|
def to_signed_tx(private_key)
|
54
51
|
sign_tx to_raw_tx, private_key
|
55
52
|
end
|
@@ -57,12 +54,8 @@ module Counterparty
|
|
57
54
|
# Commit this object to the blockchain. If a private key is passed, the
|
58
55
|
# transaction is signed using this key via a create_ call and a subsequent
|
59
56
|
# sign_tx call.
|
60
|
-
|
61
|
-
|
62
|
-
def save!(private_key = nil)
|
63
|
-
(private_key) ?
|
64
|
-
connection.broadcast_tx( to_signed_tx(private_key) ) :
|
65
|
-
connection.request(self.class.to_do_request, to_params)
|
57
|
+
def save!(private_key)
|
58
|
+
bitcoin.sendrawtransaction to_signed_tx(private_key)
|
66
59
|
end
|
67
60
|
|
68
61
|
private
|
@@ -71,59 +64,36 @@ module Counterparty
|
|
71
64
|
# is a stub for when we decide in the future to Use the bitcoin-client gem
|
72
65
|
# to perform signatures
|
73
66
|
def sign_tx(raw_tx, pkey_wif)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
i = int(i)
|
105
|
-
if not re.match('^[0-9a-fA-F]*$', tx):
|
106
|
-
return binascii.unhexlify(sign(binascii.hexlify(tx), i, priv))
|
107
|
-
if len(priv) <= 33:
|
108
|
-
priv = binascii.hexlify(priv)
|
109
|
-
pub = privkey_to_pubkey(priv)
|
110
|
-
address = pubkey_to_address(pub)
|
111
|
-
signing_tx = signature_form(tx, i, mk_pubkey_script(address), hashcode)
|
112
|
-
sig = ecdsa_tx_sign(signing_tx, priv, hashcode)
|
113
|
-
txobj = deserialize(tx)
|
114
|
-
txobj["ins"][i]["script"] = serialize_script([sig, pub])
|
115
|
-
return serialize(txobj)
|
116
|
-
=end
|
117
|
-
|
118
|
-
|
119
|
-
scriptSig = Bitcoin.sign_data(key,
|
120
|
-
raw_tx_hash["in"][0]["scriptSig"] ).unpack('h*').first
|
121
|
-
# TODO: We may have to iterate over each input
|
122
|
-
raw_tx_hash["in"][0]["scriptSig"] = scriptSig
|
123
|
-
|
124
|
-
ret = Bitcoin::Protocol::Tx.from_hash(raw_tx_hash).to_payload.unpack('h*').first
|
125
|
-
|
126
|
-
ret
|
67
|
+
# Seems like this is your quintessential reference:
|
68
|
+
# http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html
|
69
|
+
|
70
|
+
# I think this is the right way to do it...
|
71
|
+
Bitcoin.network = (bitcoin.is_testing?) ? :testnet3 : :bitcoin
|
72
|
+
|
73
|
+
# This parses the binary-encoded raw transaction:
|
74
|
+
tx = Bitcoin::P::Tx.new [raw_tx].pack('H*')
|
75
|
+
|
76
|
+
# This is the input transaction, which we'll need for signining:
|
77
|
+
prev_hash = tx.in[0].prev_out.reverse_hth
|
78
|
+
|
79
|
+
# let's parse the keys:
|
80
|
+
key = Bitcoin::Key.from_base58 pkey_wif
|
81
|
+
|
82
|
+
pubkey = [key.pub].pack('H*')
|
83
|
+
|
84
|
+
# And parse the input transaction:
|
85
|
+
prev_tx = Bitcoin::P::Tx.new [bitcoin.getrawtransaction(prev_hash)].pack('H*')
|
86
|
+
|
87
|
+
# And, now we're ready to sign:
|
88
|
+
subscript = tx.signature_hash_for_input 0, prev_tx
|
89
|
+
sig = Bitcoin.sign_data Bitcoin.open_key(key.priv), subscript
|
90
|
+
tx.in[0].script_sig = Bitcoin::Script.to_signature_pubkey_script sig, pubkey
|
91
|
+
|
92
|
+
tx.to_payload.unpack('H*')[0]
|
93
|
+
end
|
94
|
+
|
95
|
+
def bitcoin
|
96
|
+
self.class.bitcoin
|
127
97
|
end
|
128
98
|
|
129
99
|
def connection
|
@@ -153,9 +123,10 @@ def sign(tx, i, priv, hashcode=SIGHASH_ALL):
|
|
153
123
|
@connection || Counterparty.connection
|
154
124
|
end
|
155
125
|
|
156
|
-
# Returns the
|
157
|
-
|
158
|
-
|
126
|
+
# Returns the currently assigned connection object, or if one hasn't
|
127
|
+
# been set, the default specified in the Counterparty module
|
128
|
+
def bitcoin
|
129
|
+
@bitcoin || Counterparty.bitcoin
|
159
130
|
end
|
160
131
|
|
161
132
|
# Returns the method name of a create_* request for this resource
|
@@ -14,6 +14,22 @@ module Counterparty
|
|
14
14
|
|
15
15
|
# An object that describes a specific bet.
|
16
16
|
class Bet < CounterResource
|
17
|
+
# Bet Type: Bullish CFD
|
18
|
+
BULLISH_CFD = 0
|
19
|
+
|
20
|
+
# Bet Type: Bearish CFD
|
21
|
+
BEARISH_CFD = 1
|
22
|
+
|
23
|
+
# Bet Type: Equal
|
24
|
+
EQUAL = 2
|
25
|
+
|
26
|
+
# Bet Type: Not Equal
|
27
|
+
NOT_EQUAL = 3
|
28
|
+
|
29
|
+
# Denominator by which the leverage integer is divided by. It's effectively
|
30
|
+
# a 'magic' number that allows us to keep the leverage parameter an integer
|
31
|
+
LEVERAGE_BASIS = 5040
|
32
|
+
|
17
33
|
# (integer): The transaction index
|
18
34
|
attr_accessor :tx_index
|
19
35
|
|
@@ -67,11 +83,11 @@ module Counterparty
|
|
67
83
|
attr_accessor :validity
|
68
84
|
|
69
85
|
# (integer): The quantity of XCP to wager. (Only used in Create)
|
70
|
-
attr_accessor :
|
86
|
+
attr_accessor :wager_quantity
|
71
87
|
|
72
88
|
# (integer): The minimum quantity of XCP to be
|
73
89
|
# wagered against, for the bets to match. (Only used in Create)
|
74
|
-
attr_accessor :
|
90
|
+
attr_accessor :counterwager_quantity
|
75
91
|
end
|
76
92
|
|
77
93
|
# An object that describes a specific occurance of two bets being matched
|
@@ -147,6 +163,9 @@ module Counterparty
|
|
147
163
|
# An object that describes a specific occurance of a broadcast event
|
148
164
|
# (i.e. creating/extending a feed)
|
149
165
|
class Broadcast < CounterResource
|
166
|
+
# Value: Open Broadcast
|
167
|
+
OPEN_BROADCAST = -1.0
|
168
|
+
|
150
169
|
# (integer): The transaction index
|
151
170
|
attr_accessor :tx_index
|
152
171
|
|
data/lib/counterparty/version.rb
CHANGED
data/lib/counterparty_ruby.rb
CHANGED
@@ -3,6 +3,7 @@ require 'rest_client'
|
|
3
3
|
require 'open-uri'
|
4
4
|
require 'bitcoin'
|
5
5
|
|
6
|
+
require 'blockr_io'
|
6
7
|
require 'counterparty/raw_tx'
|
7
8
|
require 'counterparty/version'
|
8
9
|
require 'counterparty/resource'
|
@@ -46,6 +47,9 @@ module Counterparty
|
|
46
47
|
end
|
47
48
|
|
48
49
|
class << self
|
50
|
+
# Sets/Gets the default bitcoin (connection) object
|
51
|
+
attr_writer :bitcoin
|
52
|
+
|
49
53
|
# Sets/Gets the default connection object
|
50
54
|
attr_writer :connection
|
51
55
|
|
@@ -55,16 +59,24 @@ module Counterparty
|
|
55
59
|
@connection || Connection.new
|
56
60
|
end
|
57
61
|
|
62
|
+
# Returns the current default bitcoin object, or creates a new test-mode
|
63
|
+
# connection, if none has been defined
|
64
|
+
def bitcoin
|
65
|
+
@bitcoin || BlockrIo.new
|
66
|
+
end
|
67
|
+
|
58
68
|
# Establishes the default connection for new objects as being the default
|
59
69
|
# counterparty production mode port/user/ip
|
60
70
|
def production!
|
61
|
-
@connection = Connection.new
|
71
|
+
@connection = Connection.new
|
72
|
+
@bitcoin = BlockrIo.new
|
62
73
|
end
|
63
74
|
|
64
75
|
# Establishes the default connection for new objects as being the default
|
65
76
|
# counterparty test mode port/user/ip
|
66
77
|
def test!
|
67
|
-
@connection = Connection.new
|
78
|
+
@connection = Connection.new 14000
|
79
|
+
@bitcoin = BlockrIo.new true
|
68
80
|
end
|
69
81
|
end
|
70
82
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# This is a fairly stand-alone test, and doesn't require much from the
|
4
|
+
# helpers.
|
5
|
+
describe BlockrIo do
|
6
|
+
include_context 'globals'
|
7
|
+
|
8
|
+
let(:blockr_main) { BlockrIo.new }
|
9
|
+
let(:blockr_test) { BlockrIo.new true }
|
10
|
+
|
11
|
+
describe "#new" do
|
12
|
+
# It should default to the test network parameters
|
13
|
+
subject{BlockrIo.new}
|
14
|
+
|
15
|
+
its(:is_testing?){ should eq(false) }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "testnet" do
|
19
|
+
describe "#getrawtransaction" do
|
20
|
+
it "should get raw testnet transactions" do
|
21
|
+
tx_hash = '686c2e3c9d4b681b3589aa4ce2ba9ecb99c6d7dcbc1c754441ee0762f463f47a'
|
22
|
+
|
23
|
+
expect(blockr_test.getrawtransaction(tx_hash)).to eq(
|
24
|
+
'01000000017a2bf2ea931670ecb165f1f3a2586d9d2d338a50066d1096a3fc8e'+
|
25
|
+
'b52e9667f8020000006a47304402201e92eebc6d9737472d085fc46f6ab45e8e'+
|
26
|
+
'c6696ad3efb79261e5559ab0c517ac02200a905c54ebb9ac28e8402e6651ddf8'+
|
27
|
+
'e067fa1305cd8c8685a40cb432bb7ef5f601210286b1e4f15de57fd34bde19cf'+
|
28
|
+
'64ad9302e454c4c377677581a951579a124b86e7ffffffff03781e0000000000'+
|
29
|
+
'0069512102a12853504fc4e79fea5e996f87e00e3e8e1d6a79c1e85c367e4ed4'+
|
30
|
+
'4d4120f14521032247831fc423ee7c96a44fa088e64231b5f00cbc9fc755bf3b'+
|
31
|
+
'dd41f64b80cbf9210286b1e4f15de57fd34bde19cf64ad9302e454c4c3776775'+
|
32
|
+
'81a951579a124b86e753ae781e00000000000069512103852853504fc4e79fea'+
|
33
|
+
'6fa406f7d61deeae390f1aee8b33580d3a98060111a18c2102502ee07ae44c88'+
|
34
|
+
'5cf1cb23c4a4c67303f4bd2ce9cb8475f25aaf229e7aaeebb2210286b1e4f15d'+
|
35
|
+
'e57fd34bde19cf64ad9302e454c4c377677581a951579a124b86e753aea0be00'+
|
36
|
+
'00000000001976a9148025b288cb325d88bcd7ef5d1ab1f8827778d5ee88ac00'+
|
37
|
+
'000000' )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "mainnet" do
|
43
|
+
describe "#getrawtransaction" do
|
44
|
+
it "should retrieve the first burn" do
|
45
|
+
tx_hash = '685623401c3f5e9d2eaaf0657a50454e56a270ee7630d409e98d3bc257560098'
|
46
|
+
|
47
|
+
expect(blockr_main.getrawtransaction(tx_hash)).to eq(
|
48
|
+
'010000000150326ab8ff04aa5bfd83f3dfc78b45716fd46864c872e2bd424fef'+
|
49
|
+
'd580d5cf3e010000006b483045022100bf49d487f78345aa450c6f5fcb67a732'+
|
50
|
+
'53d23cce1ae557f3edc6ed58a383f16a022032a4a4736b15bcf0d4c208cd8c5c'+
|
51
|
+
'e43ced848ccc2b91acb82f4c87d95eb67a960121035bceeb417f25beaa28d133'+
|
52
|
+
'ee7b28faa1e4f5c2f76b8daf12c3fab18261718790ffffffff0350c300000000'+
|
53
|
+
'00001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000'+
|
54
|
+
'00000000196a17434e5452505254590000003c50726f6f664f664275726e2030'+
|
55
|
+
'0500000000001976a914f8195d523aa0c10d9d20eca785041815257f3ec888ac'+
|
56
|
+
'00000000' )
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should fail on bad transaction" do
|
60
|
+
expect{blockr_main.getrawtransaction('bad_tx')}.to raise_error(RestClient::ResourceNotFound)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#sendrawtransaction" do
|
65
|
+
# I think the way to test this is simply to do so in the resource_create
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/spec/connection_spec.rb
CHANGED
@@ -5,10 +5,10 @@ describe Counterparty::Connection do
|
|
5
5
|
# It should default to the test network parameters
|
6
6
|
subject{Counterparty::Connection.new}
|
7
7
|
|
8
|
-
its(:host){ should eq('
|
9
|
-
its(:port){ should eq(
|
10
|
-
its(:username){ should eq('
|
8
|
+
its(:host){ should eq('xcp-dev.vennd.io') }
|
9
|
+
its(:port){ should eq(4000) }
|
10
|
+
its(:username){ should eq('counterparty') }
|
11
11
|
its(:password){ should eq('1234') }
|
12
|
-
its(:api_url){ should eq('http://
|
12
|
+
its(:api_url){ should eq('http://counterparty:1234@xcp-dev.vennd.io:4000/api/') }
|
13
13
|
end
|
14
14
|
end
|
data/spec/exceptions_spec.rb
CHANGED
@@ -3,6 +3,11 @@ require 'spec_helper'
|
|
3
3
|
describe Counterparty::ResponseError do
|
4
4
|
include_context 'globals'
|
5
5
|
|
6
|
+
before(:all) do
|
7
|
+
Counterparty.test!
|
8
|
+
Counterparty.connection = local_counterpartyd :test if use_local_counterpartyd?
|
9
|
+
end
|
10
|
+
|
6
11
|
let(:bad_issuance) do
|
7
12
|
Counterparty::Issuance.new source: source_address,
|
8
13
|
asset: 'THISASSETNAMEISFARTOOLONGANDINVALID',
|
@@ -15,12 +20,12 @@ describe Counterparty::ResponseError do
|
|
15
20
|
end
|
16
21
|
|
17
22
|
it "should fail on save!" do
|
18
|
-
expect{ bad_issuance.save! }.to raise_error Counterparty::ResponseError
|
23
|
+
expect{ bad_issuance.save!(source_privkey) }.to raise_error Counterparty::ResponseError
|
19
24
|
end
|
20
25
|
|
21
26
|
subject do
|
22
27
|
begin
|
23
|
-
bad_issuance.save!
|
28
|
+
bad_issuance.save!(source_privkey)
|
24
29
|
rescue => error
|
25
30
|
error
|
26
31
|
end
|
data/spec/rawtx_decode_spec.rb
CHANGED
@@ -11,8 +11,6 @@ describe RawTx do
|
|
11
11
|
'2E0EA1F61DEB649F6BC3F4CEF38C4F35504E51EC112DE5C384DF7BA0B8D578A4C70'+
|
12
12
|
'2B6BF11D5FAC00000000'
|
13
13
|
genesis_double_sha = '4A5E1E4BAAB89F3A32518A88C31BC87F618F76673E2CC77AB2127B7AFDEDA33B'
|
14
|
-
|
15
|
-
pending
|
16
14
|
end
|
17
15
|
|
18
16
|
describe ".nibbles_to_ui" do
|
@@ -114,8 +112,6 @@ describe RawTx do
|
|
114
112
|
"ecd13e658bbecc0b2b4c87306f637828917838c02a5d95d0e1bdff9b040000000000" +
|
115
113
|
"0000002f73733331312f00906b570400000000e4050000ffffffff01bf2087950000" +
|
116
114
|
"00001976a9145399c3093d31e4b0af4be1215d59b857b861ad5d88ac00000000").to_hash }
|
117
|
-
|
118
|
-
pending
|
119
115
|
end
|
120
116
|
|
121
117
|
end
|
@@ -7,12 +7,10 @@ require 'spec_helper'
|
|
7
7
|
describe Counterparty do
|
8
8
|
include_context 'globals'
|
9
9
|
|
10
|
-
before(:all)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#expect(bitcoin.getreceivedbyaddress(source_address)).to be > 0
|
15
|
-
#end
|
10
|
+
before(:all) do
|
11
|
+
Counterparty.test!
|
12
|
+
Counterparty.connection = local_counterpartyd :test if use_local_counterpartyd?
|
13
|
+
end
|
16
14
|
|
17
15
|
describe "Ensure test account has XCP" do
|
18
16
|
subject do
|
@@ -35,7 +33,10 @@ describe Counterparty do
|
|
35
33
|
end
|
36
34
|
|
37
35
|
its(:to_raw_tx) { should_not be_empty }
|
38
|
-
|
36
|
+
|
37
|
+
it "should persist asset send" do
|
38
|
+
expect(subject.save!(source_privkey)).to_not be_empty
|
39
|
+
end
|
39
40
|
end
|
40
41
|
|
41
42
|
describe "#do_issuance" do
|
@@ -46,7 +47,10 @@ describe Counterparty do
|
|
46
47
|
end
|
47
48
|
|
48
49
|
its(:to_raw_tx) { should_not be_empty }
|
49
|
-
|
50
|
+
|
51
|
+
it "should persist issuance" do
|
52
|
+
expect(subject.save!(source_privkey)).to_not be_empty
|
53
|
+
end
|
50
54
|
end
|
51
55
|
|
52
56
|
describe "signed #create_broadcast" do
|
@@ -54,14 +58,13 @@ describe Counterparty do
|
|
54
58
|
# We want the save(private_key) syntax here
|
55
59
|
Counterparty::Broadcast.new source: source_address, fee_fraction: 0.05,
|
56
60
|
text: "Price of gold, 12AM UTC March1. 1=inc 2=dec/const", value: 2.0,
|
57
|
-
timestamp:
|
61
|
+
timestamp: Time.now.to_i,
|
58
62
|
allow_unconfirmed_inputs: true
|
59
63
|
end
|
60
64
|
|
61
65
|
its(:to_raw_tx) { should_not be_empty }
|
62
66
|
|
63
|
-
it "should persist
|
64
|
-
# TODO: Make this work
|
67
|
+
it "should persist broadcast" do
|
65
68
|
expect(subject.save!(source_privkey)).to_not be_empty
|
66
69
|
end
|
67
70
|
end
|
data/spec/resource_read_spec.rb
CHANGED
@@ -5,7 +5,10 @@ require 'spec_helper'
|
|
5
5
|
describe Counterparty do
|
6
6
|
include_context 'globals'
|
7
7
|
|
8
|
-
before(:all)
|
8
|
+
before(:all) do
|
9
|
+
Counterparty.production!
|
10
|
+
Counterparty.connection = local_counterpartyd :main if use_local_counterpartyd?
|
11
|
+
end
|
9
12
|
|
10
13
|
# Get all burns between blocks 280537 and 280539 where greater than .2 BTC was
|
11
14
|
# burned, sorting by tx_hash (ascending order) With this (and the rest of the
|
data/spec/spec_helper.rb
CHANGED
@@ -4,10 +4,22 @@ require 'yaml'
|
|
4
4
|
require 'rspec/its'
|
5
5
|
require 'counterparty_ruby'
|
6
6
|
|
7
|
+
def config_yaml(file = 'config.yml')
|
8
|
+
YAML.load File.open([File.dirname(__FILE__),file].join('/')).read
|
9
|
+
end
|
10
|
+
|
11
|
+
def local_counterpartyd(network)
|
12
|
+
config = config_yaml('config.local.yml')
|
13
|
+
args = %w(port username password host).collect{ |a| config[network.to_s][a] }
|
14
|
+
Counterparty::Connection.new *args
|
15
|
+
end
|
16
|
+
|
17
|
+
def use_local_counterpartyd?
|
18
|
+
File.exists? [File.dirname(__FILE__),'config.local.yml'].join('/')
|
19
|
+
end
|
20
|
+
|
7
21
|
shared_context 'globals' do
|
8
|
-
let(:config)
|
9
|
-
YAML.load File.open([File.dirname(__FILE__),'config.yml'].join('/')).read
|
10
|
-
end
|
22
|
+
let(:config){ config_yaml }
|
11
23
|
|
12
24
|
let(:source_address) { config['source_address'] }
|
13
25
|
let(:source_privkey) { config['source_privkey'] }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: counterparty_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-04-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: rest-client
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
@@ -137,12 +137,14 @@ files:
|
|
137
137
|
- README.md
|
138
138
|
- Rakefile
|
139
139
|
- counterparty_ruby.gemspec
|
140
|
+
- lib/blockr_io.rb
|
140
141
|
- lib/counterparty/connection.rb
|
141
142
|
- lib/counterparty/raw_tx.rb
|
142
143
|
- lib/counterparty/resource.rb
|
143
144
|
- lib/counterparty/resources.rb
|
144
145
|
- lib/counterparty/version.rb
|
145
146
|
- lib/counterparty_ruby.rb
|
147
|
+
- spec/blockr_io_spec.rb
|
146
148
|
- spec/config.yml
|
147
149
|
- spec/connection_spec.rb
|
148
150
|
- spec/exceptions_spec.rb
|
@@ -176,6 +178,7 @@ signing_key:
|
|
176
178
|
specification_version: 3
|
177
179
|
summary: An ActiveRecord-esque abstraction of the Counterparty API
|
178
180
|
test_files:
|
181
|
+
- spec/blockr_io_spec.rb
|
179
182
|
- spec/config.yml
|
180
183
|
- spec/connection_spec.rb
|
181
184
|
- spec/exceptions_spec.rb
|