arkecosystem-crypto 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/lib/arkecosystem/crypto.rb +49 -0
  3. data/lib/arkecosystem/crypto/builder/delegate_registration.rb +33 -0
  4. data/lib/arkecosystem/crypto/builder/multi_signature_registration.rb +45 -0
  5. data/lib/arkecosystem/crypto/builder/second_signature_registration.rb +27 -0
  6. data/lib/arkecosystem/crypto/builder/transaction.rb +109 -0
  7. data/lib/arkecosystem/crypto/builder/transfer.rb +32 -0
  8. data/lib/arkecosystem/crypto/builder/vote.rb +28 -0
  9. data/lib/arkecosystem/crypto/configuration/fee.rb +30 -0
  10. data/lib/arkecosystem/crypto/configuration/network.rb +18 -0
  11. data/lib/arkecosystem/crypto/crypto.rb +144 -0
  12. data/lib/arkecosystem/crypto/deserialiser.rb +94 -0
  13. data/lib/arkecosystem/crypto/deserialisers/base.rb +15 -0
  14. data/lib/arkecosystem/crypto/deserialisers/delegate_registration.rb +22 -0
  15. data/lib/arkecosystem/crypto/deserialisers/delegate_resignation.rb +12 -0
  16. data/lib/arkecosystem/crypto/deserialisers/ipfs.rb +18 -0
  17. data/lib/arkecosystem/crypto/deserialisers/multi_payment.rb +38 -0
  18. data/lib/arkecosystem/crypto/deserialisers/multi_signature_registration.rb +32 -0
  19. data/lib/arkecosystem/crypto/deserialisers/second_signature_registration.rb +18 -0
  20. data/lib/arkecosystem/crypto/deserialisers/timelock_transfer.rb +19 -0
  21. data/lib/arkecosystem/crypto/deserialisers/transfer.rb +18 -0
  22. data/lib/arkecosystem/crypto/deserialisers/vote.rb +31 -0
  23. data/lib/arkecosystem/crypto/enums/fees.rb +18 -0
  24. data/lib/arkecosystem/crypto/enums/types.rb +18 -0
  25. data/lib/arkecosystem/crypto/identity/address.rb +35 -0
  26. data/lib/arkecosystem/crypto/identity/private_key.rb +14 -0
  27. data/lib/arkecosystem/crypto/identity/public_key.rb +18 -0
  28. data/lib/arkecosystem/crypto/identity/wif.rb +22 -0
  29. data/lib/arkecosystem/crypto/message.rb +42 -0
  30. data/lib/arkecosystem/crypto/models/transaction.rb +16 -0
  31. data/lib/arkecosystem/crypto/networks/devnet.rb +24 -0
  32. data/lib/arkecosystem/crypto/networks/mainnet.rb +24 -0
  33. data/lib/arkecosystem/crypto/networks/testnet.rb +24 -0
  34. data/lib/arkecosystem/crypto/serialiser.rb +84 -0
  35. data/lib/arkecosystem/crypto/serialisers/base.rb +13 -0
  36. data/lib/arkecosystem/crypto/serialisers/delegate_registration.rb +17 -0
  37. data/lib/arkecosystem/crypto/serialisers/delegate_resignation.rb +12 -0
  38. data/lib/arkecosystem/crypto/serialisers/ipfs.rb +17 -0
  39. data/lib/arkecosystem/crypto/serialisers/multi_payment.rb +23 -0
  40. data/lib/arkecosystem/crypto/serialisers/multi_signature_registration.rb +31 -0
  41. data/lib/arkecosystem/crypto/serialisers/second_signature_registration.rb +14 -0
  42. data/lib/arkecosystem/crypto/serialisers/timelock_transfer.rb +21 -0
  43. data/lib/arkecosystem/crypto/serialisers/transfer.rb +20 -0
  44. data/lib/arkecosystem/crypto/serialisers/vote.rb +23 -0
  45. data/lib/arkecosystem/crypto/version.rb +15 -0
  46. metadata +159 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b64a593bf5f7bd992cafb8351fbfbe6f4bb29e1d25f2630bbc95439f7f31a638
4
+ data.tar.gz: a455e2df8f107f3fe9e8ff800c98f1c56cc09c204fa79d47104577868cf1c918
5
+ SHA512:
6
+ metadata.gz: ed8656bf7f41e751ec93db37c5f80baee5a1d9d80d65b58d142f07138bae869174974a601495da8dd27d0f78e6c9e0d2e3f3e59bdb621f37044dbf8abf3324b1
7
+ data.tar.gz: 0033c2c7b4a951ebb3282a1924ba8abcb8f1816008a51e14baa2f1de35ee7a526a268dc23859acaf1f72be14c42bd5566a4e52500f1def70d2ee85cac894e747
@@ -0,0 +1,49 @@
1
+ require 'arkecosystem/crypto/crypto'
2
+ require 'arkecosystem/crypto/message'
3
+
4
+ require 'arkecosystem/crypto/builder/delegate_registration'
5
+ require 'arkecosystem/crypto/builder/multi_signature_registration'
6
+ require 'arkecosystem/crypto/builder/second_signature_registration'
7
+ require 'arkecosystem/crypto/builder/transfer'
8
+ require 'arkecosystem/crypto/builder/vote'
9
+
10
+ require 'arkecosystem/crypto/configuration/fee'
11
+ require 'arkecosystem/crypto/configuration/network'
12
+
13
+ require 'arkecosystem/crypto/enums/fees'
14
+ require 'arkecosystem/crypto/enums/types'
15
+
16
+ require 'arkecosystem/crypto/identity/address'
17
+ require 'arkecosystem/crypto/identity/private_key'
18
+ require 'arkecosystem/crypto/identity/public_key'
19
+ require 'arkecosystem/crypto/identity/wif'
20
+
21
+ require 'arkecosystem/crypto/models/transaction'
22
+
23
+ require 'arkecosystem/crypto/networks/devnet'
24
+ require 'arkecosystem/crypto/networks/mainnet'
25
+ require 'arkecosystem/crypto/networks/testnet'
26
+
27
+ require 'arkecosystem/crypto/serialiser'
28
+ require 'arkecosystem/crypto/serialisers/base'
29
+ require 'arkecosystem/crypto/serialisers/delegate_registration'
30
+ require 'arkecosystem/crypto/serialisers/delegate_resignation'
31
+ require 'arkecosystem/crypto/serialisers/ipfs'
32
+ require 'arkecosystem/crypto/serialisers/multi_payment'
33
+ require 'arkecosystem/crypto/serialisers/multi_signature_registration'
34
+ require 'arkecosystem/crypto/serialisers/second_signature_registration'
35
+ require 'arkecosystem/crypto/serialisers/timelock_transfer'
36
+ require 'arkecosystem/crypto/serialisers/transfer'
37
+ require 'arkecosystem/crypto/serialisers/vote'
38
+
39
+ require 'arkecosystem/crypto/deserialiser'
40
+ require 'arkecosystem/crypto/deserialisers/base'
41
+ require 'arkecosystem/crypto/deserialisers/delegate_registration'
42
+ require 'arkecosystem/crypto/deserialisers/delegate_resignation'
43
+ require 'arkecosystem/crypto/deserialisers/ipfs'
44
+ require 'arkecosystem/crypto/deserialisers/multi_payment'
45
+ require 'arkecosystem/crypto/deserialisers/multi_signature_registration'
46
+ require 'arkecosystem/crypto/deserialisers/second_signature_registration'
47
+ require 'arkecosystem/crypto/deserialisers/timelock_transfer'
48
+ require 'arkecosystem/crypto/deserialisers/transfer'
49
+ require 'arkecosystem/crypto/deserialisers/vote'
@@ -0,0 +1,33 @@
1
+ require 'arkecosystem/crypto/crypto'
2
+ require 'arkecosystem/crypto/enums/fees'
3
+ require 'arkecosystem/crypto/enums/types'
4
+ require 'arkecosystem/crypto/identity/public_key'
5
+ require 'arkecosystem/crypto/builder/transaction'
6
+
7
+ module ArkEcosystem
8
+ module Crypto
9
+ module Builder
10
+ # The builder for delegate registration transactions.
11
+ class DelegateRegistration < Transaction
12
+ def set_username(username)
13
+ @username = username
14
+ self
15
+ end
16
+
17
+ def sign(secret)
18
+ @asset = {
19
+ delegate: {
20
+ username: @username,
21
+ public_key: ArkEcosystem::Crypto::Identity::PublicKey.from_secret_as_hex(secret)
22
+ }
23
+ }
24
+ sign_and_create_id(secret)
25
+ end
26
+
27
+ def type
28
+ ArkEcosystem::Crypto::Enums::Types::DELEGATE_REGISTRATION
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ require 'arkecosystem/crypto/crypto'
2
+ require 'arkecosystem/crypto/enums/fees'
3
+ require 'arkecosystem/crypto/enums/types'
4
+ require 'arkecosystem/crypto/builder/transaction'
5
+
6
+ module ArkEcosystem
7
+ module Crypto
8
+ module Builder
9
+ # The builder for multi signature registration transactions.
10
+ class MultiSignatureRegistration < Transaction
11
+ def initialize
12
+ super
13
+
14
+ @asset = {
15
+ multisignature: {}
16
+ }
17
+ end
18
+
19
+ def set_keysgroup(keysgroup)
20
+ @asset[:multisignature][:keysgroup] = keysgroup
21
+ self
22
+ end
23
+
24
+ def set_lifetime(lifetime)
25
+ @asset[:multisignature][:lifetime] = lifetime
26
+ self
27
+ end
28
+
29
+ def set_min(min)
30
+ @asset[:multisignature][:min] = min
31
+ self
32
+ end
33
+
34
+ def sign(secret)
35
+ @fee = (@asset[:multisignature][:keysgroup].size + 1) * ArkEcosystem::Crypto::Configuration::Fee.get(@type)
36
+ sign_and_create_id(secret)
37
+ end
38
+
39
+ def type
40
+ ArkEcosystem::Crypto::Enums::Types::MULTI_SIGNATURE_REGISTRATION
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ require 'arkecosystem/crypto/crypto'
2
+ require 'arkecosystem/crypto/enums/fees'
3
+ require 'arkecosystem/crypto/enums/types'
4
+ require 'arkecosystem/crypto/identity/public_key'
5
+ require 'arkecosystem/crypto/builder/transaction'
6
+
7
+ module ArkEcosystem
8
+ module Crypto
9
+ module Builder
10
+ # The builder for second signature registration transactions.
11
+ class SecondSignatureRegistration < Transaction
12
+ def set_second_secret(second_secret)
13
+ @asset = {
14
+ signature: {
15
+ public_key: ArkEcosystem::Crypto::Identity::PublicKey.from_secret_as_hex(second_secret)
16
+ }
17
+ }
18
+ self
19
+ end
20
+
21
+ def type
22
+ ArkEcosystem::Crypto::Enums::Types::SECOND_SIGNATURE_REGISTRATION
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,109 @@
1
+ require 'time'
2
+ require 'arkecosystem/crypto/crypto'
3
+ require 'arkecosystem/crypto/identity/private_key'
4
+ require 'arkecosystem/crypto/configuration/fee'
5
+ require 'arkecosystem/crypto/configuration/network'
6
+ require 'btcruby/base58'
7
+ require 'deep_hash_transform'
8
+
9
+ module ArkEcosystem
10
+ module Crypto
11
+ module Builder
12
+ # The base builder for transactions.
13
+ class Transaction
14
+ attr_accessor :amount, :asset, :fee, :id, :recipient_id, :sender_public_key, :sign_signature, :signature, :timestamp, :type, :vendor_field
15
+
16
+ def initialize
17
+ @type = type
18
+ @fee = ArkEcosystem::Crypto::Configuration::Fee.get(@type)
19
+ @sender_public_key = nil
20
+ @recipient_id = nil
21
+ @amount = 0
22
+ @vendor_field = nil
23
+ @timestamp = seconds_after_epoch
24
+ @asset = {}
25
+ end
26
+
27
+ def sign(secret)
28
+ sign_and_create_id(secret)
29
+ end
30
+
31
+ def sign_and_create_id(secret)
32
+ private_key = ArkEcosystem::Crypto::Identity::PrivateKey.from_secret(secret)
33
+ @sender_public_key = private_key.public_key.unpack('H*').first
34
+
35
+ transaction_bytes = ArkEcosystem::Crypto::Crypto.get_bytes(to_hash)
36
+ @signature = private_key.ecdsa_signature(Digest::SHA256.digest(transaction_bytes)).unpack('H*').first
37
+
38
+ transaction_bytes = ArkEcosystem::Crypto::Crypto.get_bytes(to_hash, false, false)
39
+ @id = Digest::SHA256.digest(transaction_bytes).unpack('H*').first
40
+ self
41
+ end
42
+
43
+ def second_sign(second_secret)
44
+ second_key = ArkEcosystem::Crypto::Identity::PrivateKey.from_secret(second_secret)
45
+
46
+ bytes = ArkEcosystem::Crypto::Crypto.get_bytes(to_hash, false)
47
+
48
+ @sign_signature = second_key.ecdsa_signature(Digest::SHA256.digest(bytes)).unpack('H*').first
49
+ self
50
+ end
51
+
52
+ def verify
53
+ ArkEcosystem::Crypto::Crypto.verify(self)
54
+ end
55
+
56
+ def second_verify(second_public_key_hex)
57
+ ArkEcosystem::Crypto::Crypto.second_verify(self, second_public_key_hex)
58
+ end
59
+
60
+ def to_params
61
+ {
62
+ type: type,
63
+ amount: amount,
64
+ fee: fee,
65
+ vendorField: vendor_field,
66
+ timestamp: timestamp,
67
+ recipientId: recipient_id,
68
+ senderPublicKey: sender_public_key,
69
+ signature: signature,
70
+ id: id
71
+ }.tap do |h|
72
+ h[:asset] = asset.deep_transform_keys { |key| snake_case_to_camel_case(key) } if asset.any?
73
+ h[:signSignature] = sign_signature if sign_signature
74
+ end
75
+ end
76
+
77
+ def to_hash
78
+ {
79
+ amount: amount,
80
+ asset: asset,
81
+ fee: fee,
82
+ id: id,
83
+ recipient_id: recipient_id,
84
+ sender_public_key: sender_public_key,
85
+ sign_signature: sign_signature,
86
+ signature: signature,
87
+ timestamp: timestamp,
88
+ type: type,
89
+ vendor_field: vendor_field
90
+ }
91
+ end
92
+
93
+ private
94
+
95
+ def seconds_after_epoch
96
+ network = ArkEcosystem::Crypto::Configuration::Network.get
97
+
98
+ (Time.now.utc - Time.parse(network.epoch).to_time.to_i).to_i
99
+ end
100
+
101
+ def snake_case_to_camel_case(string)
102
+ string.to_s.split('_').enum_for(:each_with_index).collect do |s, index|
103
+ index.zero? ? s : s.capitalize
104
+ end.join
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,32 @@
1
+ require 'arkecosystem/crypto/crypto'
2
+ require 'arkecosystem/crypto/enums/fees'
3
+ require 'arkecosystem/crypto/enums/types'
4
+ require 'arkecosystem/crypto/builder/transaction'
5
+
6
+ module ArkEcosystem
7
+ module Crypto
8
+ module Builder
9
+ # The builder for transfer transactions.
10
+ class Transfer < Transaction
11
+ def set_recipient_id(recipient_id)
12
+ @recipient_id = recipient_id
13
+ self
14
+ end
15
+
16
+ def set_amount(amount)
17
+ @amount = amount
18
+ self
19
+ end
20
+
21
+ def set_vendor_field(vendor_field)
22
+ @vendor_field = vendor_field
23
+ self
24
+ end
25
+
26
+ def type
27
+ ArkEcosystem::Crypto::Enums::Types::TRANSFER
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ require 'arkecosystem/crypto/crypto'
2
+ require 'arkecosystem/crypto/enums/fees'
3
+ require 'arkecosystem/crypto/enums/types'
4
+ require 'arkecosystem/crypto/identity/address'
5
+ require 'arkecosystem/crypto/builder/transaction'
6
+
7
+ module ArkEcosystem
8
+ module Crypto
9
+ module Builder
10
+ # The builder for vote transactions.
11
+ class Vote < Transaction
12
+ def set_votes(votes)
13
+ @asset[:votes] = votes
14
+ self
15
+ end
16
+
17
+ def sign(secret)
18
+ @recipient_id = ArkEcosystem::Crypto::Identity::Address.from_secret(secret)
19
+ sign_and_create_id(secret)
20
+ end
21
+
22
+ def type
23
+ ArkEcosystem::Crypto::Enums::Types::VOTE
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ require 'arkecosystem/crypto/enums/fees'
2
+
3
+ module ArkEcosystem
4
+ module Crypto
5
+ module Configuration
6
+ # The holder of fee configuration.
7
+ class Fee
8
+ @fees = [
9
+ ArkEcosystem::Crypto::Enums::Fees::TRANSFER,
10
+ ArkEcosystem::Crypto::Enums::Fees::SECOND_SIGNATURE_REGISTRATION,
11
+ ArkEcosystem::Crypto::Enums::Fees::DELEGATE_REGISTRATION,
12
+ ArkEcosystem::Crypto::Enums::Fees::VOTE,
13
+ ArkEcosystem::Crypto::Enums::Fees::MULTI_SIGNATURE_REGISTRATION,
14
+ ArkEcosystem::Crypto::Enums::Fees::IPFS,
15
+ ArkEcosystem::Crypto::Enums::Fees::TIMELOCK_TRANSFER,
16
+ ArkEcosystem::Crypto::Enums::Fees::MULTI_PAYMENT,
17
+ ArkEcosystem::Crypto::Enums::Fees::DELEGATE_RESIGNATION
18
+ ]
19
+
20
+ def self.get(type)
21
+ @fees[type]
22
+ end
23
+
24
+ def self.set(type, fee)
25
+ @fees[type] = fee
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require 'arkecosystem/crypto/networks/mainnet'
2
+
3
+ module ArkEcosystem
4
+ module Crypto
5
+ module Configuration
6
+ # The holder of network configuration.
7
+ class Network
8
+ def self.get
9
+ @network || ArkEcosystem::Crypto::Networks::Mainnet
10
+ end
11
+
12
+ def self.set(network)
13
+ @network = network
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,144 @@
1
+ require 'btcruby'
2
+ require 'arkecosystem/crypto/enums/types'
3
+
4
+ module ArkEcosystem
5
+ module Crypto
6
+ # The shared cryptography methods.
7
+ class Crypto
8
+ def self.get_id(transaction)
9
+ Digest::SHA256.digest(get_bytes(transaction, false, false)).unpack('H*').first
10
+ end
11
+
12
+ def self.get_bytes(transaction, skip_signature = true, skip_second_signature = true)
13
+ bytes = ''
14
+ bytes << [transaction[:type]].pack('c')
15
+ bytes << [transaction[:timestamp]].pack('V')
16
+ bytes << [transaction[:sender_public_key]].pack('H*')
17
+
18
+ bytes << if transaction[:recipient_id]
19
+ BTC::Base58.data_from_base58check(transaction[:recipient_id])
20
+ else
21
+ [].pack('x21')
22
+ end
23
+
24
+ if transaction[:vendor_field]
25
+ bytes << transaction[:vendor_field]
26
+
27
+ if transaction[:vendor_field].size < 64
28
+ bytes << [].pack("x#{64 - transaction[:vendor_field].size}")
29
+ end
30
+ else
31
+ bytes << [].pack('x64')
32
+ end
33
+
34
+ bytes << [transaction[:amount]].pack('Q<')
35
+ bytes << [transaction[:fee]].pack('Q<')
36
+
37
+ case transaction[:type]
38
+ when ArkEcosystem::Crypto::Enums::Types::SECOND_SIGNATURE_REGISTRATION
39
+ asset_signature_public_key = transaction[:asset][:signature][:public_key]
40
+
41
+ bytes << [asset_signature_public_key].pack('H*')
42
+ when ArkEcosystem::Crypto::Enums::Types::DELEGATE_REGISTRATION
43
+ bytes << transaction[:asset][:delegate][:username]
44
+ when ArkEcosystem::Crypto::Enums::Types::VOTE
45
+ bytes << transaction[:asset][:votes].join('')
46
+ when ArkEcosystem::Crypto::Enums::Types::MULTI_SIGNATURE_REGISTRATION
47
+ ms_asset = transaction[:asset][:multisignature]
48
+
49
+ bytes << [ms_asset[:min]].pack('C')
50
+ bytes << [ms_asset[:lifetime]].pack('C')
51
+ bytes << ms_asset[:keysgroup].join('')
52
+ end
53
+
54
+ if !skip_signature && transaction[:signature]
55
+ bytes << [transaction[:signature]].pack('H*')
56
+ end
57
+
58
+ if !skip_second_signature && transaction[:sign_signature]
59
+ bytes << [transaction[:sign_signature]].pack('H*')
60
+ end
61
+
62
+ bytes
63
+ end
64
+
65
+ def self.verify(transaction)
66
+ public_only_key = BTC::Key.new(public_key: [transaction.sender_public_key].pack('H*'))
67
+
68
+ bytes = ArkEcosystem::Crypto::Crypto.get_bytes(transaction.to_hash)
69
+
70
+ public_only_key.verify_ecdsa_signature([transaction.signature].pack('H*'), Digest::SHA256.digest(bytes))
71
+ end
72
+
73
+ def self.second_verify(transaction, second_public_key_hex)
74
+ public_only_key = BTC::Key.new(public_key: [second_public_key_hex].pack('H*'))
75
+
76
+ bytes = ArkEcosystem::Crypto::Crypto.get_bytes(transaction.to_hash, false)
77
+
78
+ public_only_key.verify_ecdsa_signature([transaction.sign_signature].pack('H*'), Digest::SHA256.digest(bytes))
79
+ end
80
+
81
+ def self.parse_signatures(serialized, transaction, start_offset)
82
+ signature = serialized[start_offset..-1]
83
+
84
+ multi_signature_offset = 0
85
+
86
+ if !signature.length
87
+ transaction.delete(:signature)
88
+ else
89
+ # First Signature
90
+ signature_length = signature[2, 2].to_i(16) + 2
91
+ transaction[:signature] = serialized[start_offset, signature_length * 2]
92
+
93
+ # Multi Signature
94
+ multi_signature_offset += signature_length * 2
95
+
96
+ # Second Signature
97
+ transaction[:second_signature] = serialized[(start_offset + signature_length * 2)..-1]
98
+
99
+ if transaction[:second_signature].empty?
100
+ transaction.delete(:second_signature)
101
+ elsif transaction[:second_signature][0, 2] == 'ff'
102
+ transaction.delete(:second_signature)
103
+ else
104
+ # Second Signature
105
+ second_signature_length = signature[2, 2].to_i(16) + 2
106
+ transaction[:second_signature] = transaction[:second_signature][0, second_signature_length * 2]
107
+
108
+ # Multi Signature
109
+ multi_signature_offset += second_signature_length * 2
110
+ end
111
+
112
+ # All Signatures
113
+ signatures = serialized[(start_offset + multi_signature_offset)..-1]
114
+
115
+ return transaction if signatures.empty?
116
+
117
+ return transaction if signatures[0, 2] != 'ff'
118
+
119
+ # Parse Multi Signatures
120
+ signatures = signatures[2..-1]
121
+ transaction[:signatures] = []
122
+
123
+ more_signatures = true
124
+
125
+ while more_signatures
126
+ break if signatures.empty?
127
+
128
+ multi_signature_length = signatures[2, 2].to_i(16) + 2
129
+
130
+ if multi_signature_length > 0
131
+ transaction[:signatures].push(signatures[0, multi_signature_length * 2])
132
+ else
133
+ more_signatures = false
134
+ end
135
+
136
+ signatures = signatures[(multi_signature_length * 2)..-1]
137
+ end
138
+
139
+ transaction
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end