mastercoin-ruby 0.0.4 → 0.0.5
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 +1 -0
- data/Gemfile.lock +13 -0
- data/TODO +4 -0
- data/VERSION +1 -1
- data/bin/console +14 -0
- data/lib/mastercoin-ruby.rb +5 -1
- data/lib/mastercoin-ruby/message.rb +83 -0
- data/lib/mastercoin-ruby/selling_offer.rb +46 -0
- data/lib/mastercoin-ruby/simple_send.rb +11 -12
- data/lib/mastercoin-ruby/transaction.rb +21 -30
- data/lib/mastercoin-ruby/util.rb +31 -2
- data/mastercoin-ruby.gemspec +14 -5
- data/spec/selling_offer_spec.rb +54 -0
- data/spec/{simple_send.rb → simple_send_spec.rb} +7 -7
- metadata +27 -4
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -7,7 +7,14 @@ GIT
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
+
activesupport (4.0.0)
|
11
|
+
i18n (~> 0.6, >= 0.6.4)
|
12
|
+
minitest (~> 4.2)
|
13
|
+
multi_json (~> 1.3)
|
14
|
+
thread_safe (~> 0.1)
|
15
|
+
tzinfo (~> 0.3.37)
|
10
16
|
addressable (2.3.5)
|
17
|
+
atomic (1.1.14)
|
11
18
|
builder (3.2.2)
|
12
19
|
diff-lcs (1.2.4)
|
13
20
|
faraday (0.8.8)
|
@@ -23,6 +30,7 @@ GEM
|
|
23
30
|
hashie (2.0.5)
|
24
31
|
highline (1.6.19)
|
25
32
|
httpauth (0.2.0)
|
33
|
+
i18n (0.6.5)
|
26
34
|
jeweler (1.8.7)
|
27
35
|
builder
|
28
36
|
bundler (~> 1.0)
|
@@ -35,6 +43,7 @@ GEM
|
|
35
43
|
json (1.8.0)
|
36
44
|
jwt (0.1.8)
|
37
45
|
multi_json (>= 1.5)
|
46
|
+
minitest (4.7.5)
|
38
47
|
multi_json (1.8.0)
|
39
48
|
multi_xml (0.5.5)
|
40
49
|
multipart-post (1.2.0)
|
@@ -60,11 +69,15 @@ GEM
|
|
60
69
|
rspec-mocks (2.14.3)
|
61
70
|
sequel (4.1.1)
|
62
71
|
thor (0.18.1)
|
72
|
+
thread_safe (0.1.3)
|
73
|
+
atomic
|
74
|
+
tzinfo (0.3.37)
|
63
75
|
|
64
76
|
PLATFORMS
|
65
77
|
ruby
|
66
78
|
|
67
79
|
DEPENDENCIES
|
80
|
+
activesupport (>= 3.0.0)
|
68
81
|
bitcoin-ruby (~> 0.0.1)!
|
69
82
|
bundler (~> 1.0)
|
70
83
|
jeweler (~> 1.8.7)
|
data/TODO
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
If all outputs are the same, then look at sequence numbers:
|
2
|
+
If there is a broken sequence (i.e. 3,4,8), then the odd-man-out is the change address (8 in this example)
|
3
|
+
If there is an ambiguous sequence (i.e. 3,4,4), then the transaction is invalid!
|
4
|
+
If there is a perfect sequence (i.e. 3,4,5), then the transaction is invalid!
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.5
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
3
|
+
require 'mastercoin-ruby'
|
4
|
+
include Mastercoin
|
5
|
+
|
6
|
+
require 'irb'
|
7
|
+
require 'irb/completion'
|
8
|
+
def reload(require_regex)
|
9
|
+
$".grep(/^#{require_regex}/).each {|e| $".delete(e) && require(e) }
|
10
|
+
end
|
11
|
+
def reload!
|
12
|
+
$".grep(/mastercoin/).each {|e| $".delete(e) && require(e) }
|
13
|
+
end
|
14
|
+
IRB.start
|
data/lib/mastercoin-ruby.rb
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
require 'bitcoin'
|
2
2
|
require 'logger'
|
3
|
+
require 'active_support/core_ext'
|
3
4
|
|
4
5
|
module Mastercoin
|
5
6
|
class TransactionNotFoundException < StandardError;end
|
6
7
|
autoload :SimpleSend, 'mastercoin-ruby/simple_send'
|
8
|
+
autoload :SellingOffer, 'mastercoin-ruby/selling_offer'
|
7
9
|
autoload :ExodusPayment, 'mastercoin-ruby/exodus_payment'
|
8
10
|
autoload :Transaction, 'mastercoin-ruby/transaction'
|
11
|
+
autoload :Message, 'mastercoin-ruby/message'
|
9
12
|
autoload :Util, 'mastercoin-ruby/util'
|
10
13
|
autoload :BitcoinWrapper, 'mastercoin-ruby/bitcoin_wrapper'
|
11
14
|
|
12
15
|
TRANSACTION_SIMPLE_SEND = "0"
|
16
|
+
TRANSACTION_SELL_FOR_BITCOIN = 20
|
13
17
|
|
14
18
|
TRANSACTION_TYPES = {
|
15
19
|
TRANSACTION_SIMPLE_SEND => "Simple transfer",
|
16
20
|
"10" => "Mark saving",
|
17
21
|
"11" => "Mark compromised",
|
18
|
-
|
22
|
+
TRANSACTION_SELL_FOR_BITCOIN => "Currency trade offer bitcoins",
|
19
23
|
"21" => "Currency trade offer master-coin derived",
|
20
24
|
"22" => "Currency trade offer accept",
|
21
25
|
"30" => "Register data-stream",
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Mastercoin
|
2
|
+
class Message
|
3
|
+
# Try to decode the keys and grab the keys that have a logical sequence number
|
4
|
+
|
5
|
+
# TODO Build it so that we bruteforce a couple of hashing to see if we can get the right amount
|
6
|
+
# Dont assume the first key is the compressed key
|
7
|
+
def self.probe(keys, xor_target)
|
8
|
+
decoded_keys = Message.decode(keys[1..-1], xor_target, return_as: :array)
|
9
|
+
decoded_keys.find_all do |key|
|
10
|
+
(1..keys.count).collect{|x| x.to_s.rjust(2,"0")}.to_a.include?(key[1][0..1])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.probe_and_read(keys, xor_target)
|
15
|
+
result = Message.probe(keys, xor_target)
|
16
|
+
transaction_type = (result[0][1] + result[1][1])[2..9].to_i(16)
|
17
|
+
|
18
|
+
if transaction_type == Mastercoin::TRANSACTION_SELL_FOR_BITCOIN
|
19
|
+
Mastercoin::SellingOffer.decode_from_compressed_public_key([result[0][0], result[1][0]], xor_target)
|
20
|
+
elsif transaction_type.to_s == Mastercoin::TRANSACTION_SIMPLE_SEND.to_s
|
21
|
+
Mastercoin::SimpleSend.decode_from_compressed_public_key(keys, xor_target)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.decode(keys, xor_target, options = {})
|
26
|
+
if keys.is_a?(Array)
|
27
|
+
i = 1
|
28
|
+
key_data = keys.collect do |key|
|
29
|
+
impose = Mastercoin::Util.multiple_hash(xor_target, i)[0..-3]
|
30
|
+
i += 1
|
31
|
+
mangled_key = key.each_char.to_a.drop(2).reverse.drop(2).reverse.join
|
32
|
+
if options[:return_as] == :hash
|
33
|
+
{key => Mastercoin::Util.xor_pack_unpack_strings(impose, mangled_key)}
|
34
|
+
elsif options[:return_as] == :array
|
35
|
+
[key, Mastercoin::Util.xor_pack_unpack_strings(impose, mangled_key)]
|
36
|
+
else
|
37
|
+
Mastercoin::Util.xor_pack_unpack_strings(impose, mangled_key)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
impose = Mastercoin::Util.multiple_hash(xor_target, 1)[0..-3]
|
42
|
+
key = keys.each_char.to_a.drop(2).reverse.drop(2).reverse.join
|
43
|
+
key_data = Mastercoin::Util.xor_pack_unpack_strings(impose, key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.decode_from_compressed_public_key(keys, xor_target)
|
48
|
+
key_data = self.decode(keys, xor_target)
|
49
|
+
self.decode_key_to_data(key_data)
|
50
|
+
end
|
51
|
+
|
52
|
+
def encode_to_compressed_public_key(xor_target)
|
53
|
+
keys = self.encode_data_to_key
|
54
|
+
|
55
|
+
if keys.is_a?(String)
|
56
|
+
impose = Mastercoin::Util.multiple_hash(xor_target, 1)[0..-3]
|
57
|
+
new_key = "02" + Mastercoin::Util.xor_pack_unpack_strings(impose, keys)+ "00"
|
58
|
+
result = mangle_key_until_valid(new_key)
|
59
|
+
elsif keys.is_a?(Array)
|
60
|
+
i = 1
|
61
|
+
result = keys.collect do |key|
|
62
|
+
impose = Mastercoin::Util.multiple_hash(xor_target, i)[0..-3]
|
63
|
+
i += 1
|
64
|
+
new_key = "02" + Mastercoin::Util.xor_pack_unpack_strings(impose, key)+ "00"
|
65
|
+
|
66
|
+
mangle_key_until_valid(new_key)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def mangle_key_until_valid(key)
|
74
|
+
key[64..66] = Random.rand(256).to_s(16).rjust(2,"0")
|
75
|
+
|
76
|
+
if Mastercoin::Util.valid_ecdsa_point?(key)
|
77
|
+
return key
|
78
|
+
else
|
79
|
+
mangle_key_until_valid(key)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Mastercoin
|
2
|
+
class SellingOffer < Mastercoin::Message
|
3
|
+
class CannotDecodeSellingOfferException < StandardError;end
|
4
|
+
|
5
|
+
# 14015bd586c0c7a28979ca294b114441f23bfc97be17cd6077b9e12e2709fec3
|
6
|
+
attr_accessor :transaction_type, :currency_id, :amount, :bitcoin_amount, :time_limit, :transaction_fee
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
options.reverse_merge!({time_limit: 10, transaction_fee: 20000})
|
10
|
+
self.transaction_type = Mastercoin::TRANSACTION_SELL_FOR_BITCOIN
|
11
|
+
self.currency_id = options[:currency_id]
|
12
|
+
self.amount = options[:amount]
|
13
|
+
self.bitcoin_amount = options[:bitcoin_amount]
|
14
|
+
self.time_limit = options[:time_limit]
|
15
|
+
self.transaction_fee = options[:transaction_fee]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.decode_key_to_data(keys)
|
19
|
+
raise CannotDecodeSellingOfferException.new("Need an array of two public keys in order to decode Selling Offer") unless keys.is_a?(Array) || keys.count != 2
|
20
|
+
|
21
|
+
key = Mastercoin::Util.sort_and_strip_keys(keys).join
|
22
|
+
|
23
|
+
offer = SellingOffer.new
|
24
|
+
offer.transaction_type = key[0..7].to_i(16)
|
25
|
+
offer.currency_id = key[8..15].to_i(16)
|
26
|
+
offer.amount = key[16..31].to_i(16)
|
27
|
+
offer.bitcoin_amount = key[32..47].to_i(16)
|
28
|
+
offer.time_limit = key[48..49].to_i(16)
|
29
|
+
offer.transaction_fee = key[50..65].to_i(16)
|
30
|
+
offer
|
31
|
+
end
|
32
|
+
|
33
|
+
def encode_data_to_key
|
34
|
+
raw = self.transaction_type.to_i.to_s(16).rjust(8,"0") + self.currency_id.to_i.to_s(16).rjust(8, "0") + self.amount.to_i.to_s(16).rjust(16, "0") + self.bitcoin_amount.to_i.to_s(16).rjust(16, "0") + self.time_limit.to_i.to_s(16).rjust(2,"0") + self.transaction_fee.to_i.to_s(16).rjust(16,"0")
|
35
|
+
raw = raw.ljust(120,"0")
|
36
|
+
keys = raw.chars.each_slice(60).map(&:join)
|
37
|
+
keys.each_with_index.collect do |key, index|
|
38
|
+
"#{(index + 1).to_s(16).rjust(2,"0")}#{key}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def explain
|
43
|
+
"Selling Offer of #{(self.amount / 1e8).to_f} #{Mastercoin::CURRENCY_IDS[self.currency_id.to_s]} for #{(self.bitcoin_amount / 1e8).to_f} Bitcoins. Time limit #{self.time_limit}. BTC Fee #{self.transaction_fee}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Mastercoin
|
2
|
-
class SimpleSend
|
2
|
+
class SimpleSend < Mastercoin::Message
|
3
3
|
attr_accessor :transaction_type, :currency_id, :amount, :receiving_address, :sequence
|
4
4
|
|
5
5
|
# Supply the amount in 'dacoinminster's
|
@@ -16,19 +16,18 @@ module Mastercoin
|
|
16
16
|
01
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.
|
19
|
+
def self.decode_key_to_data(public_key)
|
20
20
|
simple_send = SimpleSend.new
|
21
|
-
simple_send.transaction_type = Mastercoin::TRANSACTION_SIMPLE_SEND
|
22
|
-
simple_send.currency_id = public_key[
|
23
|
-
simple_send.amount = public_key[
|
24
|
-
simple_send.sequence = public_key[
|
21
|
+
simple_send.transaction_type = public_key[2..9]#Mastercoin::TRANSACTION_SIMPLE_SEND
|
22
|
+
simple_send.currency_id = public_key[10..17].to_i(16)
|
23
|
+
simple_send.amount = public_key[18..33].to_i(16)
|
24
|
+
simple_send.sequence = public_key[0..1].to_i(16)
|
25
25
|
return simple_send
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
raw =
|
30
|
-
raw = raw.ljust(
|
31
|
-
|
28
|
+
def encode_data_to_key
|
29
|
+
raw = (self.public_key_sequence.to_i.to_s(16).rjust(2, "0") + self.transaction_type.to_i.to_s(16).rjust(8,"0") + self.currency_id.to_i.to_s(16).rjust(8, "0") + self.amount.to_i.to_s(16).rjust(16, "0"))
|
30
|
+
raw = raw.ljust(62,"0")
|
32
31
|
return raw
|
33
32
|
end
|
34
33
|
|
@@ -53,10 +52,10 @@ module Mastercoin
|
|
53
52
|
end
|
54
53
|
|
55
54
|
def looks_like_mastercoin?
|
56
|
-
Mastercoin::TRANSACTION_TYPES.keys.include?(self.transaction_type.to_s) && Mastercoin::CURRENCY_IDS.keys.include?(self.currency_id.to_s)
|
55
|
+
Mastercoin::TRANSACTION_TYPES.keys.include?(self.transaction_type.to_i.to_s) && Mastercoin::CURRENCY_IDS.keys.include?(self.currency_id.to_s)
|
57
56
|
end
|
58
57
|
|
59
|
-
def
|
58
|
+
def explain
|
60
59
|
"SimpleSend transaction for %.8f #{self.currency_id_text}." % (self.amount / 1e8)
|
61
60
|
end
|
62
61
|
|
@@ -4,14 +4,20 @@ module Mastercoin
|
|
4
4
|
|
5
5
|
attr_accessor :btc_tx
|
6
6
|
attr_accessor :transaction_type, :currency_id, :amount
|
7
|
+
attr_accessor :data_addresses, :rejected_outputs, :target_address, :multisig, :sending_address
|
8
|
+
|
9
|
+
attr_accessor :data
|
7
10
|
attr_accessor :source_address
|
8
|
-
attr_accessor :data_addresses, :rejected_outputs, :target_address, :multisig
|
9
11
|
|
10
12
|
def initialize(tx_hash)
|
11
13
|
@store = Mastercoin.storage
|
12
14
|
self.data_addresses = []
|
13
15
|
self.rejected_outputs = []
|
14
16
|
self.btc_tx = @store.get_tx(tx_hash)
|
17
|
+
self.sending_address = btc_tx.inputs.first.get_prev_out.get_address
|
18
|
+
self.source_address = Mastercoin::ExodusPayment.highest_output_for_tx(self.btc_tx)
|
19
|
+
|
20
|
+
exodus_value = nil
|
15
21
|
|
16
22
|
raise TransactionNotFoundException.new("Transaction #{tx_hash} could not be found. Is your blockchain up to date?") if self.btc_tx.nil?
|
17
23
|
|
@@ -29,20 +35,21 @@ module Mastercoin
|
|
29
35
|
self.multisig = false
|
30
36
|
end
|
31
37
|
|
32
|
-
self.source_address = Mastercoin::ExodusPayment.highest_output_for_tx(self.btc_tx)
|
33
|
-
|
34
38
|
if multisig
|
35
39
|
self.btc_tx.outputs.each do |output|
|
36
40
|
if output.get_address == Mastercoin::EXODUS_ADDRESS
|
37
|
-
|
38
|
-
|
41
|
+
exodus_value = output.value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
self.btc_tx.outputs.each do |output|
|
46
|
+
if output.script.is_multisig?
|
39
47
|
keys = output.script.get_multisig_pubkeys.collect{|x| x.unpack("H*")[0]}
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
self.data = Mastercoin::Message.probe_and_read(keys, self.sending_address)
|
49
|
+
elsif output.get_address == Mastercoin::EXODUS_ADDRESS
|
50
|
+
# Do nothing for now
|
43
51
|
else
|
44
|
-
|
45
|
-
self.target_address = output.get_address if output.value == 0.00006 * 1e8
|
52
|
+
self.target_address = output.get_address if output.value == exodus_value
|
46
53
|
end
|
47
54
|
end
|
48
55
|
else
|
@@ -50,7 +57,7 @@ module Mastercoin
|
|
50
57
|
if output.get_address == Mastercoin::EXODUS_ADDRESS
|
51
58
|
# Do nothing yet; this is simply the exodus address
|
52
59
|
elsif Mastercoin::SimpleSend.decode_from_address(output.get_address).looks_like_mastercoin? # This looks like a data packet
|
53
|
-
self.
|
60
|
+
self.data = Mastercoin::SimpleSend.decode_from_address(output.get_address)
|
54
61
|
end
|
55
62
|
end
|
56
63
|
|
@@ -58,24 +65,12 @@ module Mastercoin
|
|
58
65
|
address = output.get_address
|
59
66
|
sequence = Mastercoin::Util.get_sequence(address)
|
60
67
|
|
61
|
-
if self.
|
68
|
+
if self.data.sequence.to_s == sequence.to_s
|
62
69
|
self.target_address = address
|
63
70
|
end
|
64
71
|
end
|
65
72
|
end
|
66
|
-
|
67
|
-
raise NoMastercoinTransactionException.new("Could not find a valid looking data-address, invalid.") unless self.data_addresses.any?
|
68
|
-
|
69
|
-
self.data_addresses.sort!{|x, y| x.sequence.to_i <=> y.sequence.to_i }
|
70
|
-
|
71
|
-
self.analyze_addresses!
|
72
|
-
end
|
73
|
-
|
74
|
-
def analyze_addresses!
|
75
|
-
address = self.data_addresses[0]
|
76
|
-
self.transaction_type = address.transaction_type
|
77
|
-
self.currency_id = address.currency_id
|
78
|
-
self.amount = address.amount
|
73
|
+
raise NoMastercoinTransactionException.new("Could not find a valid looking data-address, invalid.") unless self.data
|
79
74
|
end
|
80
75
|
|
81
76
|
def has_three_outputs?
|
@@ -87,11 +82,7 @@ module Mastercoin
|
|
87
82
|
end
|
88
83
|
|
89
84
|
def to_s
|
90
|
-
|
91
|
-
"Simple send:: Sent #{self.amount / 1e8} '#{Mastercoin::CURRENCY_IDS[self.currency_id.to_s]}' to #{self.target_address}"
|
92
|
-
else
|
93
|
-
"Unknown transaction: #{self.transaction_type}"
|
94
|
-
end
|
85
|
+
self.data.explain
|
95
86
|
end
|
96
87
|
end
|
97
88
|
end
|
data/lib/mastercoin-ruby/util.rb
CHANGED
@@ -1,5 +1,34 @@
|
|
1
1
|
module Mastercoin
|
2
2
|
class Util
|
3
|
+
def self.sort_keys(public_keys)
|
4
|
+
public_keys.sort{|x,y| x[0..1] <=> y[0..1]}
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.strip_key(key)
|
8
|
+
return key[2..-1]
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def self.sort_and_strip_keys(keys)
|
13
|
+
Util.sort_keys(keys).collect{|key| Util.strip_key(key)}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.xor_pack_unpack_strings(s1, s2)
|
17
|
+
s1_bytes = [s1].pack("H*").unpack("C*")
|
18
|
+
s2_bytes = [s2].pack("H*").unpack("C*")
|
19
|
+
s1_bytes.zip(s2_bytes).map { |a, b| (a ^ b).to_s(16).rjust(2,"0") }.join
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.multiple_hash(target, times = 1)
|
23
|
+
times -= 1
|
24
|
+
new_target = Digest::SHA256.hexdigest(target).upcase
|
25
|
+
if times > 0
|
26
|
+
return multiple_hash(new_target, times)
|
27
|
+
end
|
28
|
+
|
29
|
+
return new_target
|
30
|
+
end
|
31
|
+
|
3
32
|
def self.valid_ecdsa_point?(pub_key)
|
4
33
|
begin
|
5
34
|
Bitcoin::Key.new(nil, pub_key).addr
|
@@ -14,8 +43,8 @@ module Mastercoin
|
|
14
43
|
decoded = Bitcoin.decode_base58(bitcoin_address)
|
15
44
|
|
16
45
|
seq = decoded[2..3].to_i(16) - 1
|
17
|
-
if seq
|
18
|
-
seq
|
46
|
+
if seq < 0
|
47
|
+
seq += 256
|
19
48
|
end
|
20
49
|
|
21
50
|
return seq
|
data/mastercoin-ruby.gemspec
CHANGED
@@ -5,17 +5,18 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "mastercoin-ruby"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Maran"]
|
12
|
-
s.date = "2013-10-
|
12
|
+
s.date = "2013-10-28"
|
13
13
|
s.description = "Basic implementation of the Mastercoin protocol."
|
14
14
|
s.email = "maran.hidskes@gmail.com"
|
15
|
-
s.executables = ["exodus_payment", "mastercoin_transaction", "simple_send", "wallet.rb"]
|
15
|
+
s.executables = ["console", "exodus_payment", "mastercoin_transaction", "simple_send", "wallet.rb"]
|
16
16
|
s.extra_rdoc_files = [
|
17
17
|
"LICENSE.txt",
|
18
|
-
"README.md"
|
18
|
+
"README.md",
|
19
|
+
"TODO"
|
19
20
|
]
|
20
21
|
s.files = [
|
21
22
|
".document",
|
@@ -24,7 +25,9 @@ Gem::Specification.new do |s|
|
|
24
25
|
"LICENSE.txt",
|
25
26
|
"README.md",
|
26
27
|
"Rakefile",
|
28
|
+
"TODO",
|
27
29
|
"VERSION",
|
30
|
+
"bin/console",
|
28
31
|
"bin/exodus_payment",
|
29
32
|
"bin/mastercoin_transaction",
|
30
33
|
"bin/simple_send",
|
@@ -32,11 +35,14 @@ Gem::Specification.new do |s|
|
|
32
35
|
"lib/mastercoin-ruby.rb",
|
33
36
|
"lib/mastercoin-ruby/bitcoin_wrapper.rb",
|
34
37
|
"lib/mastercoin-ruby/exodus_payment.rb",
|
38
|
+
"lib/mastercoin-ruby/message.rb",
|
39
|
+
"lib/mastercoin-ruby/selling_offer.rb",
|
35
40
|
"lib/mastercoin-ruby/simple_send.rb",
|
36
41
|
"lib/mastercoin-ruby/transaction.rb",
|
37
42
|
"lib/mastercoin-ruby/util.rb",
|
38
43
|
"mastercoin-ruby.gemspec",
|
39
|
-
"spec/
|
44
|
+
"spec/selling_offer_spec.rb",
|
45
|
+
"spec/simple_send_spec.rb"
|
40
46
|
]
|
41
47
|
s.homepage = "http://github.com/maran/mastercoin-ruby"
|
42
48
|
s.licenses = ["MIT"]
|
@@ -51,6 +57,7 @@ Gem::Specification.new do |s|
|
|
51
57
|
s.add_runtime_dependency(%q<bitcoin-ruby>, ["~> 0.0.1"])
|
52
58
|
s.add_runtime_dependency(%q<sequel>, ["~> 4.1.1"])
|
53
59
|
s.add_runtime_dependency(%q<thor>, [">= 0"])
|
60
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0"])
|
54
61
|
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
55
62
|
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
56
63
|
s.add_development_dependency(%q<jeweler>, ["~> 1.8.7"])
|
@@ -59,6 +66,7 @@ Gem::Specification.new do |s|
|
|
59
66
|
s.add_dependency(%q<bitcoin-ruby>, ["~> 0.0.1"])
|
60
67
|
s.add_dependency(%q<sequel>, ["~> 4.1.1"])
|
61
68
|
s.add_dependency(%q<thor>, [">= 0"])
|
69
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
62
70
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
63
71
|
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
64
72
|
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
@@ -68,6 +76,7 @@ Gem::Specification.new do |s|
|
|
68
76
|
s.add_dependency(%q<bitcoin-ruby>, ["~> 0.0.1"])
|
69
77
|
s.add_dependency(%q<sequel>, ["~> 4.1.1"])
|
70
78
|
s.add_dependency(%q<thor>, [">= 0"])
|
79
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
71
80
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
72
81
|
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
73
82
|
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'mastercoin-ruby'
|
2
|
+
|
3
|
+
describe Mastercoin::SellingOffer do
|
4
|
+
|
5
|
+
context "Encoding and decoding public keys" do
|
6
|
+
before do
|
7
|
+
@selling_offer = Mastercoin::SellingOffer.new(currency_id: 2, amount: 1e8.to_i, bitcoin_amount: 1e6.to_i, time_limit: 6, transaction_fee: 1e5.to_i)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "Should encode to exactly two public keys" do
|
11
|
+
keys = @selling_offer.encode_to_compressed_public_key("1J2svn2GxYx9LPrpCLFikmzn9kkrXBrk8B")
|
12
|
+
keys.count.should eq(2)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "Should be backward compatible with existing transactions in the blockchain" do
|
16
|
+
selling = Mastercoin::SellingOffer.decode_from_compressed_public_key(["0226cb0561011d9045f6371cb09086ba7148d9942328bcf1dd78cb6edb35ccdda9", "022eac137ab02d826df0af54e92a352945c9892df6cd77f1a7c390fc82c8b0edea"], "13NRX88EZbS5q81x6XFrTECzrciPREo821")
|
17
|
+
selling.amount.should be(1e8.to_i)
|
18
|
+
selling.currency_id.should be(2)
|
19
|
+
selling.time_limit.should be(12)
|
20
|
+
selling.bitcoin_amount.should be(1000)
|
21
|
+
selling.transaction_fee.should be(100000)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "Should encode valid public keys" do
|
25
|
+
keys = @selling_offer.encode_to_compressed_public_key("1J2svn2GxYx9LPrpCLFikmzn9kkrXBrk8B")
|
26
|
+
keys.first[0..-3].should eq("02d52c390e46f1110410078a9db14d124206924666fb10a5e8dbf9cc2e2ecde3")
|
27
|
+
keys.last[0..-3].should eq("02020f44668806819e67a030e82a6af98376dac1065d7fe533daf251d43aa836")
|
28
|
+
Mastercoin::Util.valid_ecdsa_point?(keys.first).should eq(true)
|
29
|
+
Mastercoin::Util.valid_ecdsa_point?(keys.last).should eq(true)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "Should decode public keys into a valid transaction" do
|
33
|
+
keys = @selling_offer.encode_to_compressed_public_key("1J2svn2GxYx9LPrpCLFikmzn9kkrXBrk8B")
|
34
|
+
offer_two = Mastercoin::SellingOffer.decode_from_compressed_public_key(keys, "1J2svn2GxYx9LPrpCLFikmzn9kkrXBrk8B")
|
35
|
+
offer_two.amount.should eq(@selling_offer.amount)
|
36
|
+
offer_two.bitcoin_amount.should eq(@selling_offer.bitcoin_amount)
|
37
|
+
offer_two.currency_id.should eq(@selling_offer.currency_id)
|
38
|
+
offer_two.transaction_fee.should be(1e5.to_i)
|
39
|
+
offer_two.time_limit.should eq(@selling_offer.time_limit)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "Should strip sequence and compressed key format" do
|
43
|
+
key = "0300000014000000020000000005f5e10000000000000f424060000000000018"
|
44
|
+
Mastercoin::Util.strip_key(key).should eq("00000014000000020000000005f5e10000000000000f424060000000000018")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "Should sort keys based on sequence number" do
|
48
|
+
keys = ["0300000014000000020000000005f5e10000000000000f424060000000000018","0200000014000000020000000005f5e10000000000000f424060000000000018", "0100000014000000020000000005f5e10000000000000f424060000000000018"]
|
49
|
+
Mastercoin::Util.sort_keys(keys).should eq(["0100000014000000020000000005f5e10000000000000f424060000000000018",
|
50
|
+
"0200000014000000020000000005f5e10000000000000f424060000000000018",
|
51
|
+
"0300000014000000020000000005f5e10000000000000f424060000000000018"])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -39,25 +39,25 @@ describe Mastercoin::SimpleSend do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
it "Should output a valid looking compressed public key" do
|
42
|
-
public_key = @simple_send.encode_to_compressed_public_key
|
43
|
-
public_key.should eq("
|
42
|
+
public_key = @simple_send.encode_to_compressed_public_key(@simple_send.receiving_address)
|
43
|
+
public_key[0..-3].should eq("023d1de8362fca649b99f66e1c98f409e0d17edfcffc0b9082e1bc0330dc5798")
|
44
44
|
end
|
45
45
|
|
46
46
|
it "Should be a valid ECDSA point" do
|
47
|
-
public_key = @simple_send.encode_to_compressed_public_key
|
47
|
+
public_key = @simple_send.encode_to_compressed_public_key(@simple_send.receiving_address)
|
48
48
|
Mastercoin::Util.valid_ecdsa_point?(public_key).should eq(true)
|
49
49
|
end
|
50
50
|
|
51
51
|
it "Should always start with 02 for compressed key" do
|
52
|
-
public_key = @simple_send.encode_to_compressed_public_key
|
52
|
+
public_key = @simple_send.encode_to_compressed_public_key(@simple_send.receiving_address)
|
53
53
|
public_key[0..1].should eq("02")
|
54
54
|
end
|
55
55
|
|
56
56
|
it "Should be able to parse a given public key" do
|
57
|
-
simple_send = Mastercoin::SimpleSend.decode_from_compressed_public_key("
|
58
|
-
simple_send.currency_id.should eq(2)
|
57
|
+
simple_send = Mastercoin::SimpleSend.decode_from_compressed_public_key("023d1de8362fca649b99f66e1c98f409e0d17edfcffc0b9082e1bc0330dc579811", @simple_send.receiving_address)
|
59
58
|
simple_send.amount.should eq(50)
|
60
|
-
simple_send.
|
59
|
+
simple_send.currency_id.should eq(2)
|
60
|
+
simple_send.transaction_type.to_i.to_s.should eq(Mastercoin::TRANSACTION_SIMPLE_SEND)
|
61
61
|
simple_send.public_key_sequence.should eq(1)
|
62
62
|
end
|
63
63
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mastercoin-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-10-
|
12
|
+
date: 2013-10-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bitcoin-ruby
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activesupport
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.0.0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.0.0
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
79
|
name: rdoc
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,6 +142,7 @@ dependencies:
|
|
126
142
|
description: Basic implementation of the Mastercoin protocol.
|
127
143
|
email: maran.hidskes@gmail.com
|
128
144
|
executables:
|
145
|
+
- console
|
129
146
|
- exodus_payment
|
130
147
|
- mastercoin_transaction
|
131
148
|
- simple_send
|
@@ -134,6 +151,7 @@ extensions: []
|
|
134
151
|
extra_rdoc_files:
|
135
152
|
- LICENSE.txt
|
136
153
|
- README.md
|
154
|
+
- TODO
|
137
155
|
files:
|
138
156
|
- .document
|
139
157
|
- Gemfile
|
@@ -141,7 +159,9 @@ files:
|
|
141
159
|
- LICENSE.txt
|
142
160
|
- README.md
|
143
161
|
- Rakefile
|
162
|
+
- TODO
|
144
163
|
- VERSION
|
164
|
+
- bin/console
|
145
165
|
- bin/exodus_payment
|
146
166
|
- bin/mastercoin_transaction
|
147
167
|
- bin/simple_send
|
@@ -149,11 +169,14 @@ files:
|
|
149
169
|
- lib/mastercoin-ruby.rb
|
150
170
|
- lib/mastercoin-ruby/bitcoin_wrapper.rb
|
151
171
|
- lib/mastercoin-ruby/exodus_payment.rb
|
172
|
+
- lib/mastercoin-ruby/message.rb
|
173
|
+
- lib/mastercoin-ruby/selling_offer.rb
|
152
174
|
- lib/mastercoin-ruby/simple_send.rb
|
153
175
|
- lib/mastercoin-ruby/transaction.rb
|
154
176
|
- lib/mastercoin-ruby/util.rb
|
155
177
|
- mastercoin-ruby.gemspec
|
156
|
-
- spec/
|
178
|
+
- spec/selling_offer_spec.rb
|
179
|
+
- spec/simple_send_spec.rb
|
157
180
|
homepage: http://github.com/maran/mastercoin-ruby
|
158
181
|
licenses:
|
159
182
|
- MIT
|
@@ -169,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
169
192
|
version: '0'
|
170
193
|
segments:
|
171
194
|
- 0
|
172
|
-
hash:
|
195
|
+
hash: -1833207625332828551
|
173
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
197
|
none: false
|
175
198
|
requirements:
|