Class: BSV::Network::ARC

Inherits:
Object
  • Object
show all
Defined in:
lib/bsv/network/arc.rb

Overview

ARC broadcaster for submitting transactions to the BSV network.

Any object responding to #broadcast(tx) can serve as a broadcaster; this class implements that contract using the ARC API.

The HTTP client is injectable for testability. It must respond to #request(uri, request) and return an object with #code and #body.

Constant Summary collapse

REJECTED_STATUSES =

ARC response statuses that indicate the transaction was NOT accepted. Matches the TypeScript SDK’s ARC broadcaster failure set (issue #305, finding F5.13). Prior to this fix, Ruby only recognised REJECTED and DOUBLE_SPEND_ATTEMPTED, silently treating INVALID / MALFORMED / MINED_IN_STALE_BLOCK responses as successful broadcasts.

%w[
  REJECTED
  DOUBLE_SPEND_ATTEMPTED
  INVALID
  MALFORMED
  MINED_IN_STALE_BLOCK
].freeze
ORPHAN_MARKER =

Substring match for orphan detection in txStatus or extraInfo fields.

'ORPHAN'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url, api_key: nil, deployment_id: nil, callback_url: nil, callback_token: nil, http_client: nil) ⇒ ARC

Returns a new instance of ARC.

Parameters:

  • url (String)

    ARC base URL (without trailing slash)

  • api_key (String, nil) (defaults to: nil)

    optional bearer token for Authorization

  • deployment_id (String, nil) (defaults to: nil)

    optional deployment identifier for the XDeployment-ID header; defaults to a per-instance random value

  • callback_url (String, nil) (defaults to: nil)

    optional X-CallbackUrl for ARC status callbacks

  • callback_token (String, nil) (defaults to: nil)

    optional X-CallbackToken for ARC status callback authentication

  • http_client (#request, nil) (defaults to: nil)

    injectable HTTP client for testing



53
54
55
56
57
58
59
60
61
# File 'lib/bsv/network/arc.rb', line 53

def initialize(url, api_key: nil, deployment_id: nil, callback_url: nil,
               callback_token: nil, http_client: nil)
  @url = url.chomp('/')
  @api_key = api_key
  @deployment_id = deployment_id || "bsv-ruby-sdk-#{SecureRandom.hex(8)}"
  @callback_url = callback_url
  @callback_token = callback_token
  @http_client = http_client
end

Class Method Details

.default(testnet: false, **opts) ⇒ ARC

Returns an ARC instance pointed at the GorillaPool public ARC endpoint.

Parameters:

  • testnet (Boolean) (defaults to: false)

    when true, uses the GorillaPool testnet endpoint

  • opts (Hash)

    forwarded to #initialize (e.g. api_key:, callback_url:)

Returns:



23
24
25
26
# File 'lib/bsv/network/arc.rb', line 23

def self.default(testnet: false, **opts)
  url = testnet ? TESTNET_URL : MAINNET_URL
  new(url, **opts)
end

Instance Method Details

#broadcast(tx, wait_for: nil, skip_fee_validation: nil, skip_script_validation: nil) ⇒ BroadcastResponse

Submit a transaction to ARC.

The transaction is encoded as Extended Format (BRC-30) hex when every input has source_satoshis and source_locking_script populated, which lets ARC validate sighashes without fetching parents. Falls back to plain raw-tx hex when EF is unavailable.

Parameters:

  • tx (Transaction)

    the transaction to broadcast

  • wait_for (String, nil) (defaults to: nil)

    ARC wait condition — one of ‘RECEIVED’, ‘STORED’, ‘ANNOUNCED_TO_NETWORK’, ‘SEEN_ON_NETWORK’, or ‘MINED’. When set, ARC holds the connection open until the transaction reaches the requested state (or times out). Defaults to nil (no wait).

  • skip_fee_validation (Boolean, nil) (defaults to: nil)

    when truthy, sends the X-SkipFeeValidation: true header, asking ARC to bypass its minimum-fee check. Useful for zero-fee data transactions or during local testing. Defaults to nil (fee validation applies).

  • skip_script_validation (Boolean, nil) (defaults to: nil)

    when truthy, sends the X-SkipScriptValidation: true header, asking ARC to bypass script correctness checks. Defaults to nil (script validation applies).

Returns:

Raises:

  • (BroadcastError)

    when ARC returns a non-2xx HTTP status or a rejected/orphan txStatus



87
88
89
90
91
92
93
94
95
96
# File 'lib/bsv/network/arc.rb', line 87

def broadcast(tx, wait_for: nil, skip_fee_validation: nil, skip_script_validation: nil)
  uri = URI("#{@url}/v1/tx")
  request = build_post_request(uri, wait_for: wait_for,
                                    skip_fee_validation: skip_fee_validation,
                                    skip_script_validation: skip_script_validation)
  request.body = JSON.generate(rawTx: raw_tx_hex(tx))

  response = execute(uri, request)
  handle_broadcast_response(response)
end

#broadcast_many(txs, wait_for: nil, skip_fee_validation: nil, skip_script_validation: nil) ⇒ Array<BroadcastResponse, BroadcastError>

Submit multiple transactions to ARC in a single batch request.

Each transaction is encoded as Extended Format (BRC-30) hex where possible, falling back to plain raw-tx hex per transaction independently.

Returns a mixed array of BroadcastResponse and BroadcastError objects — one element per submitted transaction in the same order. Per-transaction rejections are returned as BroadcastError values rather than raised, so callers can inspect the full result set even when some transactions fail. Only HTTP-level errors (non-2xx) raise a BroadcastError for the whole batch.

Parameters:

  • txs (Array<Transaction>)

    transactions to broadcast

  • wait_for (String, nil) (defaults to: nil)

    ARC wait condition (see #broadcast)

  • skip_fee_validation (Boolean, nil) (defaults to: nil)

    when truthy, sends X-SkipFeeValidation: true for the batch request

  • skip_script_validation (Boolean, nil) (defaults to: nil)

    when truthy, sends X-SkipScriptValidation: true for the batch request

Returns:

Raises:

  • (BroadcastError)

    when ARC returns a non-2xx HTTP status or a malformed (non-array) response body



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/bsv/network/arc.rb', line 119

def broadcast_many(txs, wait_for: nil, skip_fee_validation: nil, skip_script_validation: nil)
  return [] if txs.empty?

  uri = URI("#{@url}/v1/txs")
  request = build_post_request(uri, wait_for: wait_for,
                                    skip_fee_validation: skip_fee_validation,
                                    skip_script_validation: skip_script_validation)
  request.body = JSON.generate(txs.map { |tx| { rawTx: raw_tx_hex(tx) } })

  response = execute(uri, request)
  handle_batch_response(response)
end

#status(txid) ⇒ Object

Query the status of a previously submitted transaction. Returns BroadcastResponse on success, raises BroadcastError on failure.



134
135
136
137
138
139
140
141
142
# File 'lib/bsv/network/arc.rb', line 134

def status(txid)
  uri = URI("#{@url}/v1/tx/#{txid}")
  request = Net::HTTP::Get.new(uri)
  request['XDeployment-ID'] = @deployment_id
  apply_auth_header(request)

  response = execute(uri, request)
  handle_broadcast_response(response)
end