# Copyright (c) 2016-2022 The Ruby-Eth Contributors## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# -*- encoding : ascii-8bit -*-# Provides the {Eth} module.moduleEth# Provides an recursive-length prefix (RLP) encoder and decoder.moduleRlp# Provides an RLP-decoder.moduleDecoderextendself# Decodes an RLP-encoded object.## @param rlp [String] an RLP-encoded object.# @return [Object] the decoded and maybe deserialized object.# @raise [Eth::Rlp::DecodingError] if the input string does not end after# the root item.defperform(rlp)rlp=Util.hex_to_binrlpifUtil.is_hex?rlprlp=Util.str_to_bytesrlpbeginitem,next_start=consume_itemrlp,0rescueException=>eraiseDecodingError,"Cannot decode rlp string: #{e}"endraiseDecodingError,"RLP string ends with #{rlp.size-next_start} superfluous bytes"ifnext_start!=rlp.sizereturnitemendprivate# Consume an RLP-encoded item from the given start.defconsume_item(rlp,start)t,l,s=consume_length_prefixrlp,startconsume_payloadrlp,s,t,lend# Consume an RLP length prefix at the given position.defconsume_length_prefix(rlp,start)b0=rlp[start].ordifb0<Constant::PRIMITIVE_PREFIX_OFFSET# single byte[:str,1,start]elsifb0<Constant::PRIMITIVE_PREFIX_OFFSET+Constant::SHORT_LENGTH_LIMITraiseDecodingError,"Encoded as short string although single byte was possible"if(b0-Constant::PRIMITIVE_PREFIX_OFFSET==1)&&rlp[start+1].ord<Constant::PRIMITIVE_PREFIX_OFFSET# short string[:str,b0-Constant::PRIMITIVE_PREFIX_OFFSET,start+1]elsifb0<Constant::LIST_PREFIX_OFFSETenforce_no_zero_bytesrlp,start# long stringll=b0-Constant::PRIMITIVE_PREFIX_OFFSET-Constant::SHORT_LENGTH_LIMIT+1l=Util.big_endian_to_intrlp[(start+1)...(start+1+ll)]raiseDecodingError,"Long string prefix used for short string"ifl<Constant::SHORT_LENGTH_LIMIT[:str,l,start+1+ll]elsifb0<Constant::LIST_PREFIX_OFFSET+Constant::SHORT_LENGTH_LIMIT# short list[:list,b0-Constant::LIST_PREFIX_OFFSET,start+1]elseenforce_no_zero_bytesrlp,start# long listll=b0-Constant::LIST_PREFIX_OFFSET-Constant::SHORT_LENGTH_LIMIT+1l=Util.big_endian_to_intrlp[(start+1)...(start+1+ll)]raiseDecodingError,"Long list prefix used for short list"ifl<Constant::SHORT_LENGTH_LIMIT[:list,l,start+1+ll]endend# Enforce RLP slices to not start with empty bytes.defenforce_no_zero_bytes(rlp,start)raiseDecodingError,"Length starts with zero bytes"ifrlp.slice(start+1)==Constant::BYTE_ZEROend# Consume an RLP payload at the given position of given type and size.defconsume_payload(rlp,start,type,length)casetypewhen:str[rlp[start...(start+length)],start+length]when:listitems=[]next_item_start=startpayload_end=next_item_start+lengthwhilenext_item_start<payload_enditem,next_item_start=consume_itemrlp,next_item_startitems.pushitemendraiseDecodingError,"List length prefix announced a too small length"ifnext_item_start>payload_end[items,next_item_start]elseraiseTypeError,"Type must be either :str or :list"endendendendend