mastercoin-ruby 0.0.2
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/.document +5 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +74 -0
- data/LICENSE.txt +61 -0
- data/README.md +85 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/exodus_payment +27 -0
- data/bin/mastercoin_transaction +21 -0
- data/bin/simple_send +52 -0
- data/bin/wallet.rb +169 -0
- data/lib/mastercoin-ruby/bitcoin_wrapper.rb +44 -0
- data/lib/mastercoin-ruby/exodus_payment.rb +146 -0
- data/lib/mastercoin-ruby/simple_send.rb +67 -0
- data/lib/mastercoin-ruby/transaction.rb +94 -0
- data/lib/mastercoin-ruby/util.rb +24 -0
- data/lib/mastercoin-ruby.rb +54 -0
- data/mastercoin-ruby.gemspec +77 -0
- data/spec/simple_send.rb +64 -0
- metadata +185 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
module Mastercoin
|
2
|
+
class ExodusPayment
|
3
|
+
|
4
|
+
attr_accessor :coins_bought, :bonus_bought, :address, :tx, :time_included
|
5
|
+
|
6
|
+
def to_s
|
7
|
+
"Bought #{self.coins_bought} Mastercoins and got a #{self.bonus_bought} Mastercoins extra."
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_json
|
11
|
+
{coins_bought: self.coins_bought, bonus_bought: self.bonus_bought}.to_json
|
12
|
+
end
|
13
|
+
|
14
|
+
def total_amount
|
15
|
+
self.coins_bought + self.bonus_bought
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_transaction(hash)
|
19
|
+
buying = ExodusPayment.new
|
20
|
+
buying.coins_bought = 0
|
21
|
+
buying.bonus_bought = 0
|
22
|
+
|
23
|
+
store = Mastercoin.storage
|
24
|
+
tx = store.get_tx(hash)
|
25
|
+
raise TransactionNotFoundException.new("Could not find the given transaction with #{hash}. Perhaps your blockchain is not up-to-date?") unless tx
|
26
|
+
buying.tx = tx
|
27
|
+
block_time = store.get_block_by_tx(tx.hash).time
|
28
|
+
buying.time_included = block_time
|
29
|
+
highest = ExodusPayment.highest_output_for_tx(tx)
|
30
|
+
buying.address = highest
|
31
|
+
|
32
|
+
exodus_output = tx.outputs.find{|x| x.to_hash(with_address:true)["address"] == Mastercoin::EXODUS_ADDRESS}
|
33
|
+
|
34
|
+
if tx.get_block.depth <= Mastercoin::END_BLOCK
|
35
|
+
btc_amount = (exodus_output.value / 1e8)
|
36
|
+
bought = btc_amount * 100
|
37
|
+
buying.coins_bought += bought
|
38
|
+
date_difference = (Mastercoin::END_TIME.to_i - block_time.to_i) / 60.0 / 60 / 24 / 7
|
39
|
+
if date_difference > 0
|
40
|
+
bonus = (btc_amount * 100 * (date_difference * 0.1))
|
41
|
+
|
42
|
+
buying.bonus_bought += sprintf("%0.08f", bonus).to_f
|
43
|
+
end
|
44
|
+
end
|
45
|
+
return buying
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.highest_output_for_tx(tx)
|
49
|
+
result = {}
|
50
|
+
output_hash = tx.in.collect{|x| x.get_prev_out.to_hash(with_address: true) }
|
51
|
+
|
52
|
+
output_hash.each do |output|
|
53
|
+
address = output['address']
|
54
|
+
result[address] ||= 0
|
55
|
+
result[address] += output['value'].to_f
|
56
|
+
end
|
57
|
+
|
58
|
+
highest_input = result.sort{|x,y| y[1] <=> x[1]}
|
59
|
+
highest_input = highest_input[0][0]
|
60
|
+
end
|
61
|
+
|
62
|
+
# This is a very slow and probably very inefficient way to calculate the coins bought
|
63
|
+
# TODO: Please rewrite
|
64
|
+
def self.from_address(address)
|
65
|
+
buying = ExodusPayment.new
|
66
|
+
@used = {}
|
67
|
+
@rejected_tx = []
|
68
|
+
|
69
|
+
buying.address = address
|
70
|
+
|
71
|
+
buying.coins_bought = 0
|
72
|
+
buying.bonus_bought = 0
|
73
|
+
|
74
|
+
store = Mastercoin.storage
|
75
|
+
txouts = store.get_txouts_for_address(address)
|
76
|
+
|
77
|
+
# 1. Get all outputs for an address
|
78
|
+
# 2. Check to see if this ouput has a next input for the Exodus address
|
79
|
+
# A. Get the tx for the next input if any exist
|
80
|
+
# B. Check if the tx has any outputs with the Exodus address
|
81
|
+
# 3. If so find which input did the total best payments to Exodus
|
82
|
+
# 4. Check the inputs for Exodus output and award the one with the highest total
|
83
|
+
|
84
|
+
txouts.each do |txout|
|
85
|
+
Mastercoin.log.debug("Checking txout: #{txout.to_hash(with_address: true)}")
|
86
|
+
input = txout.get_next_in
|
87
|
+
|
88
|
+
if input
|
89
|
+
tx = input.get_tx
|
90
|
+
next if @rejected_tx.include?(tx.hash)
|
91
|
+
|
92
|
+
block_time = store.get_block_by_tx(tx.hash).time
|
93
|
+
|
94
|
+
if tx.get_block.depth > Mastercoin::END_BLOCK
|
95
|
+
Mastercoin.log.debug("Transaction after end date: Rejecting")
|
96
|
+
@rejected_tx << tx.hash
|
97
|
+
next
|
98
|
+
end
|
99
|
+
|
100
|
+
addresses = tx.outputs.collect{|x| x.to_hash(with_address: true)["address"] }
|
101
|
+
|
102
|
+
unless addresses.include?(Mastercoin::EXODUS_ADDRESS)
|
103
|
+
Mastercoin.log.debug("TX #{tx.hash} does not include transaction to Exodus")
|
104
|
+
@rejected_tx << tx.hash
|
105
|
+
next
|
106
|
+
else
|
107
|
+
Mastercoin.log.debug("TX #{tx.hash} is a transaction to Exodus")
|
108
|
+
end
|
109
|
+
|
110
|
+
highest_input = ExodusPayment.highest_output_for_tx(tx)
|
111
|
+
|
112
|
+
Mastercoin.log.debug("Highest input for #{tx.hash} is #{highest_input}")
|
113
|
+
|
114
|
+
# Get all the inputs from this transaction and see which has the higest one. the Funds belong to the input with the highest value
|
115
|
+
tx.out.each do |output|
|
116
|
+
if output.get_addresses.flatten.include?(Mastercoin::EXODUS_ADDRESS) && !@used.keys.include?(tx.hash)
|
117
|
+
Mastercoin.log.debug("TX #{tx.hash} is not inside our used tx hash: #{@used.keys}")
|
118
|
+
|
119
|
+
unless txout.get_address == highest_input
|
120
|
+
Mastercoin.log.debug("This is not the highest input; can't give the coins. #{txout.get_address} we needed #{highest_input}")
|
121
|
+
next
|
122
|
+
else
|
123
|
+
end
|
124
|
+
|
125
|
+
@used[tx.hash] = highest_input
|
126
|
+
|
127
|
+
btc_amount = (output.value / 1e8)
|
128
|
+
bought = btc_amount * 100
|
129
|
+
buying.coins_bought += bought
|
130
|
+
date_difference = (Mastercoin::END_TIME.to_i - block_time.to_i) / 60.0 / 60 / 24 / 7
|
131
|
+
if date_difference > 0
|
132
|
+
bonus = (btc_amount * 100 * (date_difference * 0.1))
|
133
|
+
|
134
|
+
buying.bonus_bought += sprintf("%0.08f", bonus).to_f
|
135
|
+
end
|
136
|
+
else
|
137
|
+
Mastercoin.log.debug("This is not the Exodus output; probably change address")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
return buying
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Mastercoin
|
2
|
+
class SimpleSend
|
3
|
+
attr_accessor :transaction_type, :currency_id, :amount, :receiving_address, :sequence
|
4
|
+
|
5
|
+
# Supply the amount in 'dacoinminster's
|
6
|
+
def initialize(options= {})
|
7
|
+
self.transaction_type = Mastercoin::TRANSACTION_SIMPLE_SEND
|
8
|
+
self.currency_id = options[:currency_id]
|
9
|
+
self.amount = options[:amount]
|
10
|
+
self.receiving_address = options[:receiving_address]
|
11
|
+
end
|
12
|
+
|
13
|
+
# hardcode the sequence for a public key simple send since it's always fits inside a public key
|
14
|
+
# Please note that we start at 01 - 00 will generate unvalid ECDSA points somehow
|
15
|
+
def public_key_sequence
|
16
|
+
01
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.decode_from_compressed_public_key(public_key)
|
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)
|
25
|
+
return simple_send
|
26
|
+
end
|
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
|
+
|
32
|
+
return raw
|
33
|
+
end
|
34
|
+
|
35
|
+
def encode_to_address
|
36
|
+
raw = (self.get_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") + "000000")
|
37
|
+
Bitcoin.hash160_to_address(raw)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.decode_from_address(raw_address)
|
41
|
+
simple_send = Mastercoin::SimpleSend.new
|
42
|
+
decoded = Bitcoin.decode_base58(raw_address)
|
43
|
+
simple_send.sequence = decoded[2..3].to_i(16)
|
44
|
+
simple_send.transaction_type = decoded[4..11].to_i(16)
|
45
|
+
simple_send.currency_id = decoded[12..19].to_i(16)
|
46
|
+
simple_send.amount = decoded[20..35].to_i(16)
|
47
|
+
return simple_send
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_sequence(bitcoin_address = nil)
|
51
|
+
bitcoin_address ||= self.receiving_address
|
52
|
+
Mastercoin::Util.get_sequence(bitcoin_address)
|
53
|
+
end
|
54
|
+
|
55
|
+
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)
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
"SimpleSend transaction for %.8f #{self.currency_id_text}." % (self.amount / 1e8)
|
61
|
+
end
|
62
|
+
|
63
|
+
def currency_id_text
|
64
|
+
Mastercoin::CURRENCY_IDS[self.currency_id.to_s]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Mastercoin
|
2
|
+
class Transaction
|
3
|
+
class NoMastercoinTransactionException < StandardError;end;
|
4
|
+
|
5
|
+
attr_accessor :btc_tx
|
6
|
+
attr_accessor :transaction_type, :currency_id, :amount
|
7
|
+
attr_accessor :source_address
|
8
|
+
attr_accessor :data_addresses, :rejected_outputs, :target_address, :multisig
|
9
|
+
|
10
|
+
def initialize(tx_hash)
|
11
|
+
@store = Mastercoin.storage
|
12
|
+
self.data_addresses = []
|
13
|
+
self.rejected_outputs = []
|
14
|
+
self.btc_tx = @store.get_tx(tx_hash)
|
15
|
+
|
16
|
+
raise TransactionNotFoundException.new("Transaction #{tx_hash} could not be found. Is your blockchain up to date?") if self.btc_tx.nil?
|
17
|
+
|
18
|
+
unless self.has_genesis_as_output?
|
19
|
+
raise NoMastercoinTransaction.new("This transaction does not contain a txout to the genesis address, invalid.")
|
20
|
+
end
|
21
|
+
|
22
|
+
unless self.has_three_outputs?
|
23
|
+
raise NoMastercoinTransaction.new("This transaction does not contain three outputs, invalid.")
|
24
|
+
end
|
25
|
+
|
26
|
+
if self.btc_tx.outputs.collect{|x| x.script.is_multisig?}.include?(true)
|
27
|
+
self.multisig = true
|
28
|
+
else
|
29
|
+
self.multisig = false
|
30
|
+
end
|
31
|
+
|
32
|
+
self.source_address = Mastercoin::ExodusPayment.highest_output_for_tx(self.btc_tx)
|
33
|
+
|
34
|
+
if multisig
|
35
|
+
self.btc_tx.outputs.each do |output|
|
36
|
+
if output.get_address == Mastercoin::EXODUS_ADDRESS
|
37
|
+
# Do nothing yet; this is simply the exodus address
|
38
|
+
elsif output.script.is_multisig?
|
39
|
+
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
|
43
|
+
else
|
44
|
+
#TODO Change this not really too trust worthy
|
45
|
+
self.target_address = output.get_address if output.value == 0.00006 * 1e8
|
46
|
+
end
|
47
|
+
end
|
48
|
+
else
|
49
|
+
self.btc_tx.outputs.each do |output|
|
50
|
+
if output.get_address == Mastercoin::EXODUS_ADDRESS
|
51
|
+
# Do nothing yet; this is simply the exodus address
|
52
|
+
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)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
self.btc_tx.outputs.each do |output|
|
58
|
+
address = output.get_address
|
59
|
+
sequence = Mastercoin::Util.get_sequence(address)
|
60
|
+
if self.data_addresses[0].sequence.to_s == sequence.to_s
|
61
|
+
self.target_address = address
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
self.data_addresses.sort!{|x, y| x.sequence.to_i <=> y.sequence.to_i }
|
67
|
+
|
68
|
+
self.analyze_addresses!
|
69
|
+
end
|
70
|
+
|
71
|
+
def analyze_addresses!
|
72
|
+
address = self.data_addresses[0]
|
73
|
+
self.transaction_type = address.transaction_type
|
74
|
+
self.currency_id = address.currency_id
|
75
|
+
self.amount = address.amount
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_three_outputs?
|
79
|
+
self.btc_tx.outputs.size >= 3
|
80
|
+
end
|
81
|
+
|
82
|
+
def has_genesis_as_output?
|
83
|
+
self.btc_tx.outputs.collect{|x| x.get_address == Mastercoin::EXODUS_ADDRESS}.any?
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_s
|
87
|
+
if self.transaction_type.to_s == "0"
|
88
|
+
"Simple send:: Sent #{self.amount / 1e8} '#{Mastercoin::CURRENCY_IDS[self.currency_id.to_s]}' to #{self.target_address}"
|
89
|
+
else
|
90
|
+
"Unknown transaction: #{self.transaction_type}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mastercoin
|
2
|
+
class Util
|
3
|
+
def self.valid_ecdsa_point?(pub_key)
|
4
|
+
begin
|
5
|
+
Bitcoin::Key.new(nil, pub_key).addr
|
6
|
+
rescue OpenSSL::PKey::EC::Point::Error
|
7
|
+
return false
|
8
|
+
end
|
9
|
+
|
10
|
+
return true
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_sequence(bitcoin_address)
|
14
|
+
decoded = Bitcoin.decode_base58(bitcoin_address)
|
15
|
+
|
16
|
+
seq = decoded[2..3].to_i(16) - 1
|
17
|
+
if seq > 255
|
18
|
+
seq -= 255
|
19
|
+
end
|
20
|
+
|
21
|
+
return seq
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'bitcoin'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Mastercoin
|
5
|
+
class TransactionNotFoundException < StandardError;end
|
6
|
+
autoload :SimpleSend, 'mastercoin-ruby/simple_send'
|
7
|
+
autoload :ExodusPayment, 'mastercoin-ruby/exodus_payment'
|
8
|
+
autoload :Transaction, 'mastercoin-ruby/transaction'
|
9
|
+
autoload :Util, 'mastercoin-ruby/util'
|
10
|
+
autoload :BitcoinWrapper, 'mastercoin-ruby/bitcoin_wrapper'
|
11
|
+
|
12
|
+
TRANSACTION_SIMPLE_SEND = "0"
|
13
|
+
|
14
|
+
TRANSACTION_TYPES = {
|
15
|
+
TRANSACTION_SIMPLE_SEND => "Simple transfer",
|
16
|
+
"10" => "Mark saving",
|
17
|
+
"11" => "Mark compromised",
|
18
|
+
"20" => "Currency trade offer bitcoins",
|
19
|
+
"21" => "Currency trade offer master-coin derived",
|
20
|
+
"22" => "Currency trade offer accept",
|
21
|
+
"30" => "Register data-stream",
|
22
|
+
"40" => "Bet offer",
|
23
|
+
"100" => "Create child currency"
|
24
|
+
}
|
25
|
+
|
26
|
+
CURRENCY_IDS = {
|
27
|
+
"1" => "Mastercoin",
|
28
|
+
"2" => "Test Mastercoin"
|
29
|
+
}
|
30
|
+
|
31
|
+
EXODUS_ADDRESS = "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P"
|
32
|
+
END_TIME = Time.new(2013,9,01,00,00,00, "+00:00")
|
33
|
+
END_BLOCK = 255365
|
34
|
+
|
35
|
+
def self.set_storage(storage_string)
|
36
|
+
@storage_string = storage_string
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.storage
|
40
|
+
Bitcoin.network ||= :bitcoin
|
41
|
+
@@storage ||= Bitcoin::Storage.sequel(:db => @storage_string)
|
42
|
+
return @@storage
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.init_logger(level = Logger::INFO)
|
46
|
+
@@log ||= Logger.new(STDOUT)
|
47
|
+
@@log.level = level
|
48
|
+
@@log
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.log
|
52
|
+
@@log ||= Mastercoin.init_logger
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "mastercoin-ruby"
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Maran"]
|
12
|
+
s.date = "2013-09-25"
|
13
|
+
s.description = "Basic implementation of the Mastercoin protocol."
|
14
|
+
s.email = "maran.hidskes@gmail.com"
|
15
|
+
s.executables = ["exodus_payment", "mastercoin_transaction", "simple_send", "wallet.rb"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt",
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"bin/exodus_payment",
|
29
|
+
"bin/mastercoin_transaction",
|
30
|
+
"bin/simple_send",
|
31
|
+
"bin/wallet.rb",
|
32
|
+
"lib/mastercoin-ruby.rb",
|
33
|
+
"lib/mastercoin-ruby/bitcoin_wrapper.rb",
|
34
|
+
"lib/mastercoin-ruby/exodus_payment.rb",
|
35
|
+
"lib/mastercoin-ruby/simple_send.rb",
|
36
|
+
"lib/mastercoin-ruby/transaction.rb",
|
37
|
+
"lib/mastercoin-ruby/util.rb",
|
38
|
+
"mastercoin-ruby.gemspec",
|
39
|
+
"spec/simple_send.rb"
|
40
|
+
]
|
41
|
+
s.homepage = "http://github.com/maran/mastercoin-ruby"
|
42
|
+
s.licenses = ["MIT"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.rubygems_version = "1.8.24"
|
45
|
+
s.summary = "Ruby library for the Mastercoin protocol"
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_runtime_dependency(%q<bitcoin-ruby>, ["~> 0.0.1"])
|
52
|
+
s.add_runtime_dependency(%q<sequel>, ["~> 4.1.1"])
|
53
|
+
s.add_runtime_dependency(%q<thor>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
55
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
56
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.7"])
|
57
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<bitcoin-ruby>, ["~> 0.0.1"])
|
60
|
+
s.add_dependency(%q<sequel>, ["~> 4.1.1"])
|
61
|
+
s.add_dependency(%q<thor>, [">= 0"])
|
62
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
63
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
65
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<bitcoin-ruby>, ["~> 0.0.1"])
|
69
|
+
s.add_dependency(%q<sequel>, ["~> 4.1.1"])
|
70
|
+
s.add_dependency(%q<thor>, [">= 0"])
|
71
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
72
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
73
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
74
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
data/spec/simple_send.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'mastercoin-ruby'
|
2
|
+
|
3
|
+
describe Mastercoin::SimpleSend do
|
4
|
+
before do
|
5
|
+
@simple_send = Mastercoin::SimpleSend.new(currency_id: 2, amount: 50, receiving_address: "184mQaxRYwiU2jqUE852FZGQvbZyRhcDSu")
|
6
|
+
end
|
7
|
+
|
8
|
+
context "Encoding and decoding addresses" do
|
9
|
+
it "Should output a valid looking bitcoin address" do
|
10
|
+
address = @simple_send.encode_to_address
|
11
|
+
address.should eq("17vrMab8gQx72eCEaUxJzL4fg5VwEUumJQ")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "Should decode a valid looking bitcoin address" do
|
15
|
+
simple_send = Mastercoin::SimpleSend.decode_from_address("17vrMab8gQx72eCEaUxJzL4fg5VwEUumJQ")
|
16
|
+
simple_send.currency_id.should eq(2)
|
17
|
+
simple_send.amount.should eq(50)
|
18
|
+
simple_send.transaction_type.to_s.should eq(Mastercoin::TRANSACTION_SIMPLE_SEND)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "Should backwards compatible with existing transactions" do
|
22
|
+
simple_send = Mastercoin::SimpleSend.decode_from_address("1CVE9Au1XEm3MkYxeAhUDVqWvaHrP98iUt")
|
23
|
+
simple_send.amount.should eq(100 * 1e8)
|
24
|
+
simple_send.sequence.should eq(126)
|
25
|
+
simple_send.transaction_type.should eq(0)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "Should be backwards compatible with sequences" do
|
29
|
+
Mastercoin::SimpleSend.new.get_sequence("1CcJFxoEW5PUwesMVxGrq6kAPJ1TJsSVqq").should eq(126)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "Encoding and decoding public keys" do
|
34
|
+
it "Should accept all options for a SimpleSend transaction" do
|
35
|
+
@simple_send.currency_id.should eq(2)
|
36
|
+
@simple_send.amount.should eq(50)
|
37
|
+
@simple_send.receiving_address.should eq("184mQaxRYwiU2jqUE852FZGQvbZyRhcDSu")
|
38
|
+
@simple_send.transaction_type.should eq(Mastercoin::TRANSACTION_SIMPLE_SEND)
|
39
|
+
end
|
40
|
+
|
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")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "Should be a valid ECDSA point" do
|
47
|
+
public_key = @simple_send.encode_to_compressed_public_key
|
48
|
+
Mastercoin::Util.valid_ecdsa_point?(public_key).should eq(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "Should always start with 02 for compressed key" do
|
52
|
+
public_key = @simple_send.encode_to_compressed_public_key
|
53
|
+
public_key[0..1].should eq("02")
|
54
|
+
end
|
55
|
+
|
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)
|
59
|
+
simple_send.amount.should eq(50)
|
60
|
+
simple_send.transaction_type.should eq(Mastercoin::TRANSACTION_SIMPLE_SEND)
|
61
|
+
simple_send.public_key_sequence.should eq(1)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|