module BTC class AssetMarker include Opcodes MAGIC = "\x4f\x41".freeze VERSION1 = "\x01\x00".freeze PREFIX_V1 = MAGIC + VERSION1 attr_accessor :quantities attr_accessor :metadata attr_reader :data attr_reader :output attr_reader :script def initialize(output: nil, script: nil, data: nil, quantities: nil, metadata: nil) if output || script script ||= output.script raise ArgumentError, "Script is not an OP_RETURN script" if !script.op_return_script? data = script.op_return_data raise ArgumentError, "No pushdata found in script" if !data || data.bytesize == 0 @output = output @script = script end if data data = BTC::Data.ensure_binary_encoding(data) raise ArgumentError, "Data must be at least 6 bytes long (4 bytes prefix and 2 bytes for varints)" if data.bytesize < 6 raise ArgumentError, "Prefix is invalid. Expected #{BTC.to_hex(PREFIX_V1)}" if data[0, PREFIX_V1.bytesize] != PREFIX_V1 offset = PREFIX_V1.bytesize count, bytesread = WireFormat.read_varint(data: data, offset: offset) raise ArgumentError, "Cannot read Asset Quantity Count varint" if !count offset = bytesread @quantities = [] count.times do qty, bytesread = WireFormat.read_uleb128(data: data, offset: offset) raise ArgumentError, "Cannot read Asset Quantity LEB128 unsigned integer" if !qty raise ArgumentError, "Open Assets limit LEB128 encoding for quantities to 9 bytes" if (bytesread - offset) > 9 @quantities << qty offset = bytesread end metadata, bytesread = WireFormat.read_string(data: data, offset: offset) raise ArgumentError, "Cannot read Asset Metadata" if !metadata @metadata = metadata @data = data[0, bytesread] else # Initialize with optional attributes @quantities = quantities @metadata = metadata end end def quantities @quantities ||= [] end def quantities=(qs) @quantities = qs @data = nil @script = nil @output = nil end def metadata @metadata ||= "".b end def metadata=(md) @metadata = md @data = nil @script = nil @output = nil end def data @data ||= begin PREFIX_V1 + WireFormat.encode_varint(self.quantities.size) + self.quantities.inject("".b){|buf, qty| buf << WireFormat.encode_uleb128(qty) } + WireFormat.encode_string(self.metadata) end end def script @script ||= begin Script.new << OP_RETURN << self.data end end def output @output ||= TransactionOutput.new(value: 0, script: self.script) end end end