Module: BSV::Wallet::Validators

Defined in:
lib/bsv/wallet_interface/validators.rb

Constant Summary collapse

RESERVED_PROTOCOL_PREFIXES =

Reserved protocol name prefixes (BRC-44 and BRC-98). Names beginning with any of these strings are disallowed.

['admin', 'p '].freeze
RESERVED_PROTOCOL_SUFFIX =

Suffix that is disallowed on protocol names.

' protocol'
RESERVED_BASKET_PREFIXES =

Reserved basket name prefixes. Basket names beginning with any of these strings are disallowed.

['admin', 'p '].freeze
RESERVED_BASKET_SUFFIX =

Suffix that is disallowed on basket names.

' basket'
RESERVED_BASKET_NAME =

Basket name that is globally reserved and cannot be used.

'default'

Class Method Summary collapse

Class Method Details

.validate_basket!(basket) ⇒ Object

Basket name rules (BRC-99):

  • 5-300 chars

  • lowercase letters, numbers, and spaces only

  • no consecutive spaces

  • must not end with ‘ basket’

  • must not start with ‘admin’

  • must not be ‘default’

  • must not start with ‘p ’ (BRC-99 reserved)



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/bsv/wallet_interface/validators.rb', line 92

def validate_basket!(basket)
  raise InvalidParameterError.new('basket', 'a String') unless basket.is_a?(String)
  raise InvalidParameterError.new('basket', 'between 5 and 300 characters') if basket.length < 5 || basket.length > 300
  raise InvalidParameterError.new('basket', 'lowercase letters, numbers, and spaces only') unless basket.match?(/\A[a-z0-9 ]+\z/)
  raise InvalidParameterError.new('basket', 'free of consecutive spaces') if basket.include?('  ')
  raise InvalidParameterError.new('basket', "not ending with \"#{RESERVED_BASKET_SUFFIX}\"") if basket.end_with?(RESERVED_BASKET_SUFFIX)

  RESERVED_BASKET_PREFIXES.each do |prefix|
    raise InvalidParameterError.new('basket', "not starting with \"#{prefix}\"") if basket.start_with?(prefix)
  end
  raise InvalidParameterError.new('basket', "not equal to \"#{RESERVED_BASKET_NAME}\"") if basket == RESERVED_BASKET_NAME
end

.validate_counterparty!(counterparty) ⇒ Object

Counterparty: ‘self’, ‘anyone’, or 66-char hex (compressed pubkey)



72
73
74
75
76
# File 'lib/bsv/wallet_interface/validators.rb', line 72

def validate_counterparty!(counterparty)
  return if %w[self anyone].include?(counterparty)

  validate_pub_key_hex!(counterparty, 'counterparty')
end

.validate_description!(description, name = 'description') ⇒ Object

Description: 5-50 characters



79
80
81
82
# File 'lib/bsv/wallet_interface/validators.rb', line 79

def validate_description!(description, name = 'description')
  raise InvalidParameterError.new(name, 'a String') unless description.is_a?(String)
  raise InvalidParameterError.new(name, 'between 5 and 50 characters') if description.length < 5 || description.length > 50
end

.validate_hex_string!(value, name = 'hex_string') ⇒ Object

Hex string: even-length hex characters only



142
143
144
145
# File 'lib/bsv/wallet_interface/validators.rb', line 142

def validate_hex_string!(value, name = 'hex_string')
  raise InvalidParameterError.new(name, 'a String') unless value.is_a?(String)
  raise InvalidParameterError.new(name, 'a valid hex string') unless value.match?(/\A[0-9a-f]*\z/) && value.length.even?
end

.validate_integer!(value, name, min: nil, max: nil) ⇒ Object

Integer within bounds



148
149
150
151
152
# File 'lib/bsv/wallet_interface/validators.rb', line 148

def validate_integer!(value, name, min: nil, max: nil)
  raise InvalidParameterError.new(name, 'an Integer') unless value.is_a?(Integer)
  raise InvalidParameterError.new(name, "at least #{min}") if min && value < min
  raise InvalidParameterError.new(name, "at most #{max}") if max && value > max
end

.validate_key_id!(key_id) ⇒ Object

Key ID: 1-800 bytes



64
65
66
67
68
69
# File 'lib/bsv/wallet_interface/validators.rb', line 64

def validate_key_id!(key_id)
  raise InvalidParameterError.new('key_id', 'a String') unless key_id.is_a?(String)

  byte_length = key_id.bytesize
  raise InvalidParameterError.new('key_id', 'between 1 and 800 bytes') if byte_length < 1 || byte_length > 800
end

.validate_label!(label) ⇒ Object

Label: 1-300 characters



106
107
108
109
# File 'lib/bsv/wallet_interface/validators.rb', line 106

def validate_label!(label)
  raise InvalidParameterError.new('label', 'a String') unless label.is_a?(String)
  raise InvalidParameterError.new('label', 'between 1 and 300 characters') if label.empty? || label.length > 300
end

.validate_outpoint!(outpoint) ⇒ Object

Outpoint: “<64-hex-txid>.<non-negative-integer>”



118
119
120
121
122
123
124
125
126
127
# File 'lib/bsv/wallet_interface/validators.rb', line 118

def validate_outpoint!(outpoint)
  raise InvalidParameterError.new('outpoint', 'a String') unless outpoint.is_a?(String)

  parts = outpoint.split('.')
  raise InvalidParameterError.new('outpoint', 'in format "<txid>.<index>"') unless parts.length == 2

  txid, index = parts
  raise InvalidParameterError.new('outpoint txid', 'a 64-character hex string') unless txid.match?(/\A[0-9a-f]{64}\z/)
  raise InvalidParameterError.new('outpoint index', 'a non-negative integer') unless index.match?(/\A\d+\z/)
end

.validate_protocol_id!(protocol_id) ⇒ Object

BRC-100 protocol ID rules:

  • Array of [security_level, protocol_name]

  • security_level: Integer 0, 1, or 2

  • protocol_name: 5-400 chars (up to 430 for ‘specific linkage revelation’ protocol)

  • lowercase letters, numbers, and spaces only

  • no consecutive spaces

  • must not end with ‘ protocol’

  • must not start with ‘admin’ (BRC-44)

  • must not start with ‘p ’ (BRC-98 reserved)

The name is normalised (stripped and downcased) before validation so that ‘ MyProtocol ’ and ‘myprotocol’ are treated identically and do not silently fork to different key-derivation paths (F8.7).



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/bsv/wallet_interface/validators.rb', line 38

def validate_protocol_id!(protocol_id)
  unless protocol_id.is_a?(Array) && protocol_id.length == 2
    raise InvalidParameterError.new('protocol_id',
                                    'an Array of [security_level, protocol_name]')
  end

  level, name = protocol_id
  raise InvalidParameterError.new('protocol_id security level', '0, 1, or 2') unless [0, 1, 2].include?(level)
  raise InvalidParameterError.new('protocol_id name', 'a String') unless name.is_a?(String)

  name = name.strip.downcase

  max_length = name.start_with?('specific linkage revelation') ? 430 : 400
  raise InvalidParameterError.new('protocol_id name', "between 5 and #{max_length} characters") if name.length < 5 || name.length > max_length
  raise InvalidParameterError.new('protocol_id name', 'lowercase letters, numbers, and spaces only') unless name.match?(/\A[a-z0-9 ]+\z/)
  raise InvalidParameterError.new('protocol_id name', 'free of consecutive spaces') if name.include?('  ')
  if name.end_with?(RESERVED_PROTOCOL_SUFFIX)
    raise InvalidParameterError.new('protocol_id name', "not ending with \"#{RESERVED_PROTOCOL_SUFFIX}\"")
  end

  RESERVED_PROTOCOL_PREFIXES.each do |prefix|
    raise InvalidParameterError.new('protocol_id name', "not starting with \"#{prefix}\"") if name.start_with?(prefix)
  end
end

.validate_pub_key_hex!(value, name = 'public_key') ⇒ Object

Compressed public key hex: exactly 66 hex characters



136
137
138
139
# File 'lib/bsv/wallet_interface/validators.rb', line 136

def validate_pub_key_hex!(value, name = 'public_key')
  raise InvalidParameterError.new(name, 'a String') unless value.is_a?(String)
  raise InvalidParameterError.new(name, 'a 66-character hex string (compressed public key)') unless value.match?(/\A[0-9a-f]{66}\z/)
end

.validate_satoshis!(value, name = 'satoshis') ⇒ Object

Satoshis: 1 to 2_100_000_000_000_000



130
131
132
133
# File 'lib/bsv/wallet_interface/validators.rb', line 130

def validate_satoshis!(value, name = 'satoshis')
  raise InvalidParameterError.new(name, 'an Integer') unless value.is_a?(Integer)
  raise InvalidParameterError.new(name, 'between 1 and 2100000000000000') if value < 1 || value > 2_100_000_000_000_000
end

.validate_tag!(tag) ⇒ Object

Tag: 1-300 characters



112
113
114
115
# File 'lib/bsv/wallet_interface/validators.rb', line 112

def validate_tag!(tag)
  raise InvalidParameterError.new('tag', 'a String') unless tag.is_a?(String)
  raise InvalidParameterError.new('tag', 'between 1 and 300 characters') if tag.empty? || tag.length > 300
end