Class: Mastercoin::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/mastercoin-ruby/transaction.rb

Defined Under Namespace

Classes: NoMastercoinTransactionException

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tx_hash) ⇒ Transaction

Returns a new instance of Transaction.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/mastercoin-ruby/transaction.rb', line 12

def initialize(tx_hash)
  @store = Mastercoin.storage
  self.data_addresses = []
  self.rejected_outputs = [] 
  self.target_address = nil
  self.btc_tx = @store.get_tx(tx_hash)
  self.sending_address = btc_tx.inputs.first.get_prev_out.get_address
  self.source_address = Mastercoin::ExodusPayment.highest_output_for_tx(self.btc_tx)

  exodus_value = nil

  raise TransactionNotFoundException.new("Transaction #{tx_hash} could not be found. Is your blockchain up to date?") if self.btc_tx.nil?

  unless self.has_genesis_as_output?
    raise NoMastercoinTransactionException.new("This transaction does not contain a txout to the genesis address, invalid.")
  end

  unless self.has_three_outputs?
    raise NoMastercoinTransactionException.new("This transaction does not contain three outputs, invalid.")
  end
 
  if self.btc_tx.outputs.collect{|x| x.script.is_multisig?}.include?(true)
    self.multisig = true
  else
    self.multisig = false
  end

  # This should probably be next up for refactor, smelling code creep!

  self.btc_tx.outputs.each do |output|
    if output.get_address == Mastercoin::EXODUS_ADDRESS
      exodus_value = output.value
    end
  end

  if multisig
    self.btc_tx.outputs.each do |output|
      if output.script.is_multisig?
        keys = output.script.get_multisig_pubkeys.collect{|x| x.unpack("H*")[0]}
        self.data = Mastercoin::Message.probe_and_read(keys, self.sending_address)
      elsif output.get_address == Mastercoin::EXODUS_ADDRESS
        # Do nothing for now
      else
        self.target_address = output.get_address if output.value == exodus_value
      end
    end
  else
    exodus_size_outputs = self.btc_tx.outputs.find_all{|x| x.value == exodus_value}

    self.btc_tx.outputs.each do |output|
      if output.get_address == Mastercoin::EXODUS_ADDRESS
        # Do nothing yet; this is simply the exodus address
      elsif Mastercoin::SimpleSend.decode_from_address(output.get_address).looks_like_mastercoin? # This looks like a data packet
        Mastercoin.log.debug "Found data for address #{output.get_address}"
        raise NoMastercoinTransactionException.new("More then one data-addresses found, invalidating.") if self.data.present?
        self.data = Mastercoin::SimpleSend.decode_from_address(output.get_address)
      end
    end

    Mastercoin.log.debug "Looking for data sequence #{self.data.sequence} +1 == #{self.data.sequence.to_i + 1}"

    exodus_size_outputs.reject{|x| x.get_address == Mastercoin::EXODUS_ADDRESS}.each do |output| # Exclude Exodus output
      address = output.get_address
      sequence = Mastercoin::Util.get_sequence(address)
      Mastercoin.log.debug "Sequence: #{sequence} for #{address}"

      if (self.data.sequence.to_i + 1).to_s  == sequence.to_s
        self.target_address = address
        Mastercoin.log.debug "Target address found #{self.target_address}"
      end
    end

    unless self.target_address
      Mastercoin.log.debug "Target address not found attempting 'peek & decode' Level 1, checking exodus-sized outputs."
      # Find data outputs and brute force receiver
      found = exodus_size_outputs.reject do |output|
        output.get_address == Mastercoin::EXODUS_ADDRESS || Mastercoin::SimpleSend.decode_from_address(output.get_address).looks_like_mastercoin? # This looks like a data packet
      end

      if found.length == 1
        Mastercoin.log.debug "Only one possible target left; found target address."
        self.target_address = found.first.get_address
      else
        Mastercoin.log.debug "Target address not found attempting 'peek & decode' Level 2, checking all-non exodus-sized outputs."

        self.btc_tx.outputs.each do |output|
          address = output.get_address
          sequence = Mastercoin::Util.get_sequence(address)
          Mastercoin.log.debug "Sequence: #{sequence} for #{address}"

          if (self.data.sequence.to_i + 1).to_s  == sequence.to_s
            self.target_address = address
            Mastercoin.log.debug "Target address found #{self.target_address}"
          end
        end
      end
    end
  end
  raise NoMastercoinTransactionException.new("Could not find valid looking target address, invalid") if self.target_address.nil? && !self.data.class == SellingOffer
  raise NoMastercoinTransactionException.new("Could not find a valid looking data-address, invalid.") unless self.data
end

Instance Attribute Details

#amountObject

Returns the value of attribute amount.



6
7
8
# File 'lib/mastercoin-ruby/transaction.rb', line 6

def amount
  @amount
end

#btc_txObject

Returns the value of attribute btc_tx.



5
6
7
# File 'lib/mastercoin-ruby/transaction.rb', line 5

def btc_tx
  @btc_tx
end

#currency_idObject

Returns the value of attribute currency_id.



6
7
8
# File 'lib/mastercoin-ruby/transaction.rb', line 6

def currency_id
  @currency_id
end

#dataObject

Returns the value of attribute data.



9
10
11
# File 'lib/mastercoin-ruby/transaction.rb', line 9

def data
  @data
end

#data_addressesObject

Returns the value of attribute data_addresses.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def data_addresses
  @data_addresses
end

#multisigObject

Returns the value of attribute multisig.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def multisig
  @multisig
end

#rejected_outputsObject

Returns the value of attribute rejected_outputs.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def rejected_outputs
  @rejected_outputs
end

#sending_addressObject

Returns the value of attribute sending_address.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def sending_address
  @sending_address
end

#source_addressObject

Returns the value of attribute source_address.



10
11
12
# File 'lib/mastercoin-ruby/transaction.rb', line 10

def source_address
  @source_address
end

#target_addressObject

Returns the value of attribute target_address.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def target_address
  @target_address
end

#transaction_typeObject

Returns the value of attribute transaction_type.



6
7
8
# File 'lib/mastercoin-ruby/transaction.rb', line 6

def transaction_type
  @transaction_type
end

Instance Method Details

#has_genesis_as_output?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/mastercoin-ruby/transaction.rb', line 118

def has_genesis_as_output?
  self.btc_tx.outputs.collect{|x| x.get_address == Mastercoin::EXODUS_ADDRESS}.any?
end

#has_three_outputs?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'lib/mastercoin-ruby/transaction.rb', line 114

def has_three_outputs?
  self.btc_tx.outputs.size >= 3
end

#to_sObject



122
123
124
# File 'lib/mastercoin-ruby/transaction.rb', line 122

def to_s
  self.data.explain(self.sending_address, self.target_address)
end