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 CHANGED
@@ -9,6 +9,7 @@ source "http://rubygems.org"
9
9
  gem 'bitcoin-ruby', '~> 0.0.1', git: "https://github.com/maran/bitcoin-ruby.git"
10
10
  gem 'sequel', '~> 4.1.1'
11
11
  gem 'thor'
12
+ gem 'activesupport', '>= 3.0.0'
12
13
 
13
14
  group :development do
14
15
  gem "rdoc", "~> 3.12"
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.4
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
@@ -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
- "20" => "Currency trade offer bitcoins",
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.decode_from_compressed_public_key(public_key)
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[12..19].to_i(16)
23
- simple_send.amount = public_key[20..35].to_i(16)
24
- simple_send.sequence = public_key[2..3].to_i(16)
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 encode_to_compressed_public_key
29
- raw = "02" + (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(66,"0")
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 to_s
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
- # Do nothing yet; this is simply the exodus address
38
- elsif output.script.is_multisig?
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
- keys.each do |key|
41
- self.data_addresses << Mastercoin::SimpleSend.decode_from_compressed_public_key(key) if Mastercoin::SimpleSend.decode_from_compressed_public_key(key).looks_like_mastercoin?
42
- end
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
- #TODO Change this not really too trust worthy
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.data_addresses << Mastercoin::SimpleSend.decode_from_address(output.get_address)
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.data_addresses[0].sequence.to_s == sequence.to_s
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
- if self.transaction_type.to_s == "0"
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
@@ -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 > 255
18
- seq -= 255
46
+ if seq < 0
47
+ seq += 256
19
48
  end
20
49
 
21
50
  return seq
@@ -5,17 +5,18 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "mastercoin-ruby"
8
- s.version = "0.0.4"
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-11"
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/simple_send.rb"
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("020100000000000000020000000000000032000000000000000000000000000000")
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("02000000000000000002000000000000003200000000000000000000000000000")
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.transaction_type.should eq(Mastercoin::TRANSACTION_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
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-11 00:00:00.000000000 Z
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/simple_send.rb
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: 1425563484101339016
195
+ hash: -1833207625332828551
173
196
  required_rubygems_version: !ruby/object:Gem::Requirement
174
197
  none: false
175
198
  requirements: