Class: Steem::TransactionBuilder
- Inherits:
-
Object
- Object
- Steem::TransactionBuilder
- Includes:
- ChainConfig, Retriable, Utils
- Defined in:
- lib/steem/transaction_builder.rb
Overview
TransactionBuilder can be used to create a transaction that the NetworkBroadcastApi can broadcast to the rest of the platform. The main feature of this class is the ability to cryptographically sign the transaction so that it conforms to the consensus rules that are required by the blockchain.
wif = '5JrvPrQeBBvCRdjv29iDvkwn3EQYZ9jqfAHzrCyUvfbEbRkrYFC'
builder = Steem::TransactionBuilder.new(wif: wif)
builder.put(vote: {
voter: 'alice',
author: 'bob',
permlink: 'my-burgers',
weight: 10000
})
trx = builder.transaction
network_broadcast_api = Steem::NetworkBroadcastApi.new
network_broadcast_api.broadcast_transaction_synchronous(trx: trx)
Constant Summary
Constants included from ChainConfig
ChainConfig::EXPIRE_IN_SECS, ChainConfig::EXPIRE_IN_SECS_PROPOSAL, ChainConfig::NETWORKS_STEEM_ADDRESS_PREFIX, ChainConfig::NETWORKS_STEEM_CHAIN_ID, ChainConfig::NETWORKS_STEEM_CORE_ASSET, ChainConfig::NETWORKS_STEEM_DEBT_ASSET, ChainConfig::NETWORKS_STEEM_DEFAULT_NODE, ChainConfig::NETWORKS_STEEM_VEST_ASSET, ChainConfig::NETWORKS_TEST_ADDRESS_PREFIX, ChainConfig::NETWORKS_TEST_CHAIN_ID, ChainConfig::NETWORKS_TEST_CORE_ASSET, ChainConfig::NETWORKS_TEST_DEBT_ASSET, ChainConfig::NETWORKS_TEST_DEFAULT_NODE, ChainConfig::NETWORKS_TEST_VEST_ASSET, ChainConfig::NETWORK_CHAIN_IDS
Constants included from Retriable
Retriable::MAX_BACKOFF, Retriable::MAX_RETRY_COUNT, Retriable::MAX_RETRY_ELAPSE, Retriable::RETRYABLE_EXCEPTIONS
Instance Attribute Summary collapse
-
#block_api ⇒ Object
Returns the value of attribute block_api.
-
#database_api ⇒ Object
Returns the value of attribute database_api.
-
#expiration ⇒ Object
Returns the value of attribute expiration.
-
#operations ⇒ Object
Returns the value of attribute operations.
-
#wif ⇒ Object
Returns the value of attribute wif.
Instance Method Summary collapse
- #expired? ⇒ Boolean
-
#initialize(options = {}) ⇒ TransactionBuilder
constructor
A new instance of TransactionBuilder.
- #inspect ⇒ Object
-
#potential_signatures ⇒ Array
All public keys that could possibly sign for a given transaction.
-
#prepare ⇒ TransactionBuilder
If the transaction can be prepared, this method will do so and set the expiration.
-
#put(type, op = nil) ⇒ TransactionBuilder
A quick and flexible way to append a new operation to the transaction.
-
#required_signatures ⇒ Array
This API will take a partially signed transaction and a set of public keys that the owner has the ability to sign for and return the minimal subset of public keys that should add signatures to the transaction.
- #reset ⇒ Object
-
#sign ⇒ Hash | TransactionBuilder
Appends to the
signaturesarray of the transaction, built from a serialized digest. -
#transaction ⇒ Object
If all of the required values are set, this returns a fully formed transaction that is ready to broadcast.
-
#valid? ⇒ Boolean
True if the transaction has all of the required signatures.
Methods included from Utils
Methods included from Retriable
Constructor Details
#initialize(options = {}) ⇒ TransactionBuilder
Returns a new instance of TransactionBuilder.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/steem/transaction_builder.rb', line 28 def initialize( = {}) @database_api = [:database_api] || Steem::DatabaseApi.new() @block_api = [:block_api] || Steem::BlockApi.new() @wif = [:wif] @ref_block_num = [:ref_block_num] @ref_block_prefix = [:ref_block_prefix] @expiration = nil @operations = [:operations] || [] @extensions = [] @signatures = [] @chain = [:chain] || :steem @error_pipe = [:error_pipe] || STDERR @chain_id = case @chain when :steem then NETWORKS_STEEM_CHAIN_ID when :test then NETWORKS_TEST_CHAIN_ID else; raise UnsupportedChainError, "Unsupported chain: #{@chain}" end end |
Instance Attribute Details
#block_api ⇒ Object
Returns the value of attribute block_api.
26 27 28 |
# File 'lib/steem/transaction_builder.rb', line 26 def block_api @block_api end |
#database_api ⇒ Object
Returns the value of attribute database_api.
26 27 28 |
# File 'lib/steem/transaction_builder.rb', line 26 def database_api @database_api end |
#expiration ⇒ Object
Returns the value of attribute expiration.
26 27 28 |
# File 'lib/steem/transaction_builder.rb', line 26 def expiration @expiration end |
#operations ⇒ Object
Returns the value of attribute operations.
26 27 28 |
# File 'lib/steem/transaction_builder.rb', line 26 def operations @operations end |
#wif ⇒ Object
Returns the value of attribute wif.
26 27 28 |
# File 'lib/steem/transaction_builder.rb', line 26 def wif @wif end |
Instance Method Details
#expired? ⇒ Boolean
71 72 73 |
# File 'lib/steem/transaction_builder.rb', line 71 def expired? @expiration.nil? || @expiration < Time.now end |
#inspect ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/steem/transaction_builder.rb', line 47 def inspect properties = %w( ref_block_num ref_block_prefix expiration operations extensions signatures ).map do |prop| if !!(v = instance_variable_get("@#{prop}")) "@#{prop}=#{v}" end end.compact.join(', ') "#<#{self.class.name} [#{properties}]>" end |
#potential_signatures ⇒ Array
Returns All public keys that could possibly sign for a given transaction.
246 247 248 249 250 |
# File 'lib/steem/transaction_builder.rb', line 246 def potential_signatures @database_api.get_potential_signatures(trx: transaction) do |result| result[:keys] end end |
#prepare ⇒ TransactionBuilder
If the transaction can be prepared, this method will do so and set the expiration. Once the expiration is set, it will not re-prepare. If you call #put, the expiration is set Nil so that it can be re-prepared.
Usually, this method is called automatically by #put and/or #transaction.
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 |
# File 'lib/steem/transaction_builder.rb', line 82 def prepare if expired? catch :prepare_header do; begin @database_api.get_dynamic_global_properties do |properties| block_number = properties.last_irreversible_block_num @block_api.get_block_header(block_num: block_number) do |result| header = result.header @ref_block_num = (block_number - 1) & 0xFFFF @ref_block_prefix = unhexlify(header.previous[8..-1]).unpack('V*')[0] @expiration = (Time.parse(properties.time + 'Z') + EXPIRE_IN_SECS).utc end end rescue => e if can_retry? e @error_pipe.puts "#{e} ... retrying." throw :prepare_header else raise e end end; end end self end |
#put(type, op = nil) ⇒ TransactionBuilder
A quick and flexible way to append a new operation to the transaction. This method uses ducktyping to figure out how to form the operation.
There are three main ways you can call this method. These assume that op_type is a Symbol (or String) representing the type of operation and op is the operation Hash.
put(op_type, op)
… or …
put(op_type => op)
… or …
put([op_type, op])
You can also chain multiple operations:
builder = Steem::TransactionBuilder.new
builder.put(vote: vote1).put(vote: vote2)
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/steem/transaction_builder.rb', line 138 def put(type, op = nil) @expiration = nil ## Saving this for later. This block, or something like it, might replace ## API broadcast operation structure. # case type # when Symbol, String # type_value = "#{type}_operation" # @operations << {type: type_value, value: op} # when Hash # type_value = "#{type.keys.first}_operation" # @operations << {type: type_value, value: type.values.first} # when Array # type_value = "#{type[0]}_operation" # @operations << {type: type_value, value: type[1]} # else # # don't know what to do with it, skipped # end case type when Symbol then @operations << [type, op] when String then @operations << [type.to_sym, op] when Hash then @operations << [type.keys.first.to_sym, type.values.first] when Array then @operations << type else # don't know what to do with it, skipped end prepare self end |
#required_signatures ⇒ Array
This API will take a partially signed transaction and a set of public keys that the owner has the ability to sign for and return the minimal subset of public keys that should add signatures to the transaction.
257 258 259 260 261 |
# File 'lib/steem/transaction_builder.rb', line 257 def required_signatures @database_api.get_required_signatures(trx: transaction) do |result| result[:keys] end end |
#reset ⇒ Object
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/steem/transaction_builder.rb', line 60 def reset @ref_block_num = nil @ref_block_prefix = nil @expiration = nil @operations = [] @extensions = [] @signatures = [] self end |
#sign ⇒ Hash | TransactionBuilder
Appends to the signatures array of the transaction, built from a serialized digest.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/steem/transaction_builder.rb', line 198 def sign return self unless !!@wif return self if expired? trx = { ref_block_num: @ref_block_num, ref_block_prefix: @ref_block_prefix, expiration: @expiration.strftime('%Y-%m-%dT%H:%M:%S'), operations: @operations, extensions: @extensions, signatures: @signatures } catch :serialize do; begin @database_api.get_transaction_hex(trx: trx) do |result| hex = @chain_id + result.hex[0..-4] # Why do we have to chop the last two bytes? digest = unhexlify(hex) digest_hex = Digest::SHA256.digest(digest) private_key = Bitcoin::Key.from_base58 @wif public_key_hex = private_key.pub ec = Bitcoin::OpenSSL_EC count = 0 sig = nil loop do count += 1 @error_pipe.puts "#{count} attempts to find canonical signature" if count % 40 == 0 sig = ec.sign_compact(digest_hex, private_key.priv, public_key_hex, false) next if public_key_hex != ec.recover_compact(digest_hex, sig) break if canonical? sig end trx[:signatures] = @signatures = [hexlify(sig)] end rescue => e if can_retry? e @error_pipe.puts "#{e} ... retrying." throw :serialize else raise e end end; end trx end |
#transaction ⇒ Object
If all of the required values are set, this returns a fully formed transaction that is ready to broadcast.
189 190 191 192 |
# File 'lib/steem/transaction_builder.rb', line 189 def transaction prepare sign end |
#valid? ⇒ Boolean
Returns True if the transaction has all of the required signatures.
264 265 266 267 268 |
# File 'lib/steem/transaction_builder.rb', line 264 def valid? @database_api.(trx: transaction) do |result| result.valid end end |