mastercoin-ruby 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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: