counterparty_ruby 0.9.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Gem Version](https://badge.fury.io/rb/counterparty_ruby.svg)](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
|
![Party On Wayne](http://data.whicdn.com/images/24796384/tumblr_m0ng6rBeWT1qhd0xso1_500_large.jpg)
|
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
|