Class: Runar::SDK::RPCProvider
- Defined in:
- lib/runar/sdk/rpc_provider.rb
Class Method Summary collapse
-
.regtest(host: 'localhost', port: 18_332, username: 'bitcoin', password: 'bitcoin') ⇒ RPCProvider
Factory method that returns an RPCProvider with default regtest settings.
Instance Method Summary collapse
-
#broadcast(raw_tx) ⇒ String
Broadcast a signed raw transaction to the node.
-
#get_contract_utxo(script_hash) ⇒ Hash?
Script-hash UTXO lookup via scantxoutset (best-effort).
-
#get_fee_rate ⇒ Integer
Return fee rate in satoshis per kilobyte.
-
#get_network ⇒ String
Return the network name this provider is connected to.
-
#get_raw_transaction(txid) ⇒ String
Fetch the raw transaction hex by txid.
-
#get_transaction(txid) ⇒ Transaction
Fetch a Transaction by txid using getrawtransaction (verbose).
-
#get_utxos(address) ⇒ Array<Utxo>
Return all UTXOs for the given address using listunspent.
-
#initialize(host: 'localhost', port: 18_332, username: 'bitcoin', password: 'bitcoin', network: 'regtest') ⇒ RPCProvider
constructor
A new instance of RPCProvider.
-
#mine(n_blocks = 1) ⇒ Array<String>
Generate
n_blocksblocks (regtest only). -
#rpc_call(method, *params) ⇒ Object
Low-level JSON-RPC call.
Constructor Details
#initialize(host: 'localhost', port: 18_332, username: 'bitcoin', password: 'bitcoin', network: 'regtest') ⇒ RPCProvider
Returns a new instance of RPCProvider.
31 32 33 34 35 36 |
# File 'lib/runar/sdk/rpc_provider.rb', line 31 def initialize(host: 'localhost', port: 18_332, username: 'bitcoin', password: 'bitcoin', network: 'regtest') @host = host @port = port @auth = ["#{username}:#{password}"].pack('m0') @network = network end |
Class Method Details
.regtest(host: 'localhost', port: 18_332, username: 'bitcoin', password: 'bitcoin') ⇒ RPCProvider
Factory method that returns an RPCProvider with default regtest settings.
45 46 47 |
# File 'lib/runar/sdk/rpc_provider.rb', line 45 def self.regtest(host: 'localhost', port: 18_332, username: 'bitcoin', password: 'bitcoin') new(host: host, port: port, username: username, password: password, network: 'regtest') end |
Instance Method Details
#broadcast(raw_tx) ⇒ String
Broadcast a signed raw transaction to the node.
80 81 82 |
# File 'lib/runar/sdk/rpc_provider.rb', line 80 def broadcast(raw_tx) rpc_call('sendrawtransaction', raw_tx).to_s end |
#get_contract_utxo(script_hash) ⇒ Hash?
Script-hash UTXO lookup via scantxoutset (best-effort).
Uses the scantxoutset RPC to scan the UTXO set for outputs matching the given script hash. This is functional for regtest/testnet but slow on mainnet. For production use, consider an electrum-style indexer or track the UTXO manually with RunarContract#from_txid after deployment.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/runar/sdk/rpc_provider.rb', line 109 def get_contract_utxo(script_hash) result = rpc_call('scantxoutset', 'start', ["raw(#{script_hash})"]) unspents = Array(result['unspents']) return nil if unspents.empty? u = unspents.first { txid: u['txid'], output_index: u['vout'].to_i, satoshis: (u['amount'].to_f * 1e8).round, script: u['scriptPubKey'] } rescue StandardError raise NotImplementedError, 'RPCProvider#get_contract_utxo: scantxoutset RPC failed. ' \ 'Your node may not support this command. Alternatives: ' \ 'use an electrum-style indexer, or track the UTXO manually with ' \ 'RunarContract#from_txid after deployment.' end |
#get_fee_rate ⇒ Integer
Return fee rate in satoshis per kilobyte.
Returns 1 sat/KB unconditionally — appropriate for regtest. For production networks, consider wrapping estimatesmartfee.
142 143 144 |
# File 'lib/runar/sdk/rpc_provider.rb', line 142 def get_fee_rate 1 end |
#get_network ⇒ String
Return the network name this provider is connected to.
132 133 134 |
# File 'lib/runar/sdk/rpc_provider.rb', line 132 def get_network @network end |
#get_raw_transaction(txid) ⇒ String
Fetch the raw transaction hex by txid.
71 72 73 74 |
# File 'lib/runar/sdk/rpc_provider.rb', line 71 def get_raw_transaction(txid) result = rpc_call('getrawtransaction', txid, false) result.to_s end |
#get_transaction(txid) ⇒ Transaction
Fetch a Transaction by txid using getrawtransaction (verbose).
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/runar/sdk/rpc_provider.rb', line 53 def get_transaction(txid) raw = rpc_call('getrawtransaction', txid, true) raw_hex = raw.fetch('hex', '') outputs = Array(raw['vout']).map do |o| val_btc = o.fetch('value', 0.0) sats = (val_btc * 1e8).round sp = o.fetch('scriptPubKey', {}) TxOutput.new(script: sp.fetch('hex', ''), satoshis: sats) end Transaction.new(txid: txid, version: 1, outputs: outputs, raw: raw_hex) end |
#get_utxos(address) ⇒ Array<Utxo>
Return all UTXOs for the given address using listunspent.
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/runar/sdk/rpc_provider.rb', line 88 def get_utxos(address) result = Array(rpc_call('listunspent', 0, 9_999_999, [address])) result.map do |u| Utxo.new( txid: u['txid'], output_index: u['vout'].to_i, satoshis: (u['amount'].to_f * 1e8).round, script: u.fetch('scriptPubKey', '') ) end end |
#mine(n_blocks = 1) ⇒ Array<String>
Generate n_blocks blocks (regtest only).
Uses generatetoaddress with a dummy bech32 address. This will advance the chain and confirm any transactions in the mempool.
153 154 155 156 157 |
# File 'lib/runar/sdk/rpc_provider.rb', line 153 def mine(n_blocks = 1) # A standard regtest coinbase address for the dummy recipient. dummy_address = 'bcrt1qjrdns4f5zwkv29ln86plqzs092yd5fg6nsz8re' rpc_call('generatetoaddress', n_blocks, dummy_address) end |
#rpc_call(method, *params) ⇒ Object
Low-level JSON-RPC call.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/runar/sdk/rpc_provider.rb', line 165 def rpc_call(method, *params) body = JSON.generate( jsonrpc: '1.0', id: 'runar', method: method, params: params ) uri = URI::HTTP.build(host: @host, port: @port, path: '/') request = Net::HTTP::Post.new(uri) request['Content-Type'] = 'application/json' request['Authorization'] = "Basic #{@auth}" request.body = body response = Net::HTTP.start(@host, @port, read_timeout: 600) do |http| http.request(request) end parse_rpc_response(response, method) end |