counterparty_ruby 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +46 -0
- data/README.md +197 -0
- data/Rakefile +28 -0
- data/counterparty_ruby.gemspec +26 -0
- data/lib/counterparty/connection.rb +200 -0
- data/lib/counterparty/raw_tx.rb +130 -0
- data/lib/counterparty/resource.rb +179 -0
- data/lib/counterparty/resources.rb +713 -0
- data/lib/counterparty/version.rb +4 -0
- data/lib/counterparty_ruby.rb +70 -0
- data/spec/config.yml +3 -0
- data/spec/connection_spec.rb +14 -0
- data/spec/exceptions_spec.rb +38 -0
- data/spec/rawtx_decode_spec.rb +122 -0
- data/spec/resource_create_spec.rb +69 -0
- data/spec/resource_read_spec.rb +91 -0
- data/spec/spec_helper.rb +29 -0
- metadata +185 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
module Counterparty
|
2
|
+
# A base class for the purpose of extending by api result hashes
|
3
|
+
class CounterResource
|
4
|
+
# This is mostly used by the eq operation and indicates the
|
5
|
+
# attributes that this resource has
|
6
|
+
attr_accessor :result_attributes # :nodoc:
|
7
|
+
|
8
|
+
# encoding (string): The encoding method to use
|
9
|
+
attr_accessor :encoding
|
10
|
+
|
11
|
+
# pubkey (string): The pubkey hex string. Required if multisig transaction
|
12
|
+
# encoding is specified for a key external to counterpartyd's local wallet.
|
13
|
+
attr_accessor :pubkey
|
14
|
+
|
15
|
+
# allow_unconfirmed_inputs (boolean): Set to true to allow this transaction
|
16
|
+
# to utilize unconfirmed UTXOs as inputs.
|
17
|
+
attr_accessor :allow_unconfirmed_inputs
|
18
|
+
|
19
|
+
# fee (integer): If you'd like to specify a custom miners' fee, specify it
|
20
|
+
# here (in satoshi). Leave as default for counterpartyd to automatically
|
21
|
+
# choose.
|
22
|
+
attr_accessor :fee
|
23
|
+
|
24
|
+
# fee_per_kb (integer): The fee per kilobyte of transaction data constant
|
25
|
+
# that counterpartyd uses when deciding on the dynamic fee to use
|
26
|
+
# (in satoshi). Leave as default unless you know what you're doing.
|
27
|
+
attr_accessor :fee_per_kb
|
28
|
+
|
29
|
+
def initialize(attrs={})
|
30
|
+
@result_attributes = attrs.keys.sort.collect(&:to_sym)
|
31
|
+
attrs.each{|k,v| instance_variable_set '@%s' % k, v}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Just a simple compare. No need to get crazy
|
35
|
+
def ==(b) # :nodoc:
|
36
|
+
( b.respond_to?(:result_attributes) &&
|
37
|
+
result_attributes == b.result_attributes &&
|
38
|
+
@result_attributes.all?{ |k| send(k) == b.send(k) } )
|
39
|
+
end
|
40
|
+
|
41
|
+
# This method returns the unsigned raw create transaction string. hex
|
42
|
+
# encoded (i.e. the same format that bitcoind returns with its raw
|
43
|
+
# transaction API calls).
|
44
|
+
def to_raw_tx
|
45
|
+
connection.request self.class.to_create_request, to_params
|
46
|
+
end
|
47
|
+
|
48
|
+
# Given the provided private key, this method returns a signed transaction
|
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
|
+
def to_signed_tx(private_key)
|
54
|
+
sign_tx to_raw_tx, private_key
|
55
|
+
end
|
56
|
+
|
57
|
+
# Commit this object to the blockchain. If a private key is passed, the
|
58
|
+
# transaction is signed using this key via a create_ call and a subsequent
|
59
|
+
# sign_tx call.
|
60
|
+
# NOTE: This method communicates your private key to the counterpartyd
|
61
|
+
# server, which might not be what you want!
|
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)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Currently this is communicating the request to the backend. This method
|
71
|
+
# is a stub for when we decide in the future to Use the bitcoin-client gem
|
72
|
+
# to perform signatures
|
73
|
+
def sign_tx(raw_tx, pkey_wif)
|
74
|
+
key = ::Bitcoin.open_key pkey_wif
|
75
|
+
raw_tx_hash = RawTx.new(raw_tx).to_hash
|
76
|
+
|
77
|
+
prev_hash = raw_tx_hash['vin'][0]['txid']
|
78
|
+
prior_tx_json = open("http://test.webbtc.com/tx/#{prev_hash}.json").read
|
79
|
+
puts prior_tx_json.inspect
|
80
|
+
prev_tx = Bitcoin::P::Tx.from_json(prior_tx_json.to_s)
|
81
|
+
|
82
|
+
|
83
|
+
puts "HERE"
|
84
|
+
signed_tx = Bitcoin::Protocol::Tx.new
|
85
|
+
signed_tx.ver = raw_tx_hash['ver']
|
86
|
+
signed_tx.lock = raw_tx_hash['lock_time']
|
87
|
+
|
88
|
+
tx_in= TxInBuilder.new
|
89
|
+
tx_in.prev_out prev_tx
|
90
|
+
tx_in.prev_out_index 0
|
91
|
+
tx_in.signature_key key
|
92
|
+
|
93
|
+
signed_tx.add_in tx_in.tx
|
94
|
+
|
95
|
+
# Here's how we put them in the raw
|
96
|
+
# @block.tx << tx
|
97
|
+
|
98
|
+
|
99
|
+
# We need to compare against
|
100
|
+
# Primarily: http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html
|
101
|
+
# With Some of this: https://bitcoin.org/en/developer-reference#signrawtransaction
|
102
|
+
=begin
|
103
|
+
def sign(tx, i, priv, hashcode=SIGHASH_ALL):
|
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
|
127
|
+
end
|
128
|
+
|
129
|
+
def connection
|
130
|
+
self.class.connection
|
131
|
+
end
|
132
|
+
|
133
|
+
# This serializes self into a hash suitable for transmission via json
|
134
|
+
def to_params
|
135
|
+
Hash[* @result_attributes.collect{|k|
|
136
|
+
v = self.send(k)
|
137
|
+
(v) ? [k,self.send(k)] : nil
|
138
|
+
}.compact.flatten]
|
139
|
+
end
|
140
|
+
|
141
|
+
class << self
|
142
|
+
# The base connection object for this class
|
143
|
+
attr_writer :connection
|
144
|
+
|
145
|
+
# Returns the counterparty-api version of this objects class name
|
146
|
+
def api_name
|
147
|
+
to_s.split('::').last.gsub(/[^\A]([A-Z])/, '_\\1').downcase
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns the currently assigned connection object, or if one hasn't
|
151
|
+
# been set, the default specified in the Counterparty module
|
152
|
+
def connection
|
153
|
+
@connection || Counterparty.connection
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns the method name of a do_* request for this resource
|
157
|
+
def to_do_request
|
158
|
+
'do_%s' % api_name
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns the method name of a create_* request for this resource
|
162
|
+
def to_create_request
|
163
|
+
'create_%s' % api_name
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns the method name of a get_* request for this resource
|
167
|
+
def to_get_request
|
168
|
+
'get_%ss' % api_name
|
169
|
+
end
|
170
|
+
|
171
|
+
# Queries the counterpartyd connection to find matching instances of this
|
172
|
+
# resource, given the filters provided in the params
|
173
|
+
def find(params)
|
174
|
+
connection.request(to_get_request, params).collect{|r| new r}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|