Bugfix: objects with array fields were not deserialized properly

fixes #10

The commit also introduces a common base type for all RLP exceptions
This commit is contained in:
Zahary Karadjov 2018-05-17 16:07:37 +03:00
parent 63af57837d
commit 8d7dd1acb1
2 changed files with 44 additions and 18 deletions

44
rlp.nim
View File

@ -26,9 +26,10 @@ type
of rlpList: of rlpList:
elems*: seq[RlpNode] elems*: seq[RlpNode]
MalformedRlpError* = object of Exception RlpError* = object of Exception
UnsupportedRlpError* = object of Exception MalformedRlpError* = object of RlpError
BadCastError* = object of Exception UnsupportedRlpError* = object of RlpError
RlpTypeMismatch* = object of RlpError
proc rlpFromBytes*(data: BytesRange): Rlp = proc rlpFromBytes*(data: BytesRange): Rlp =
result.bytes = data result.bytes = data
@ -38,9 +39,8 @@ let
zeroBytesRlp* = Rlp() zeroBytesRlp* = Rlp()
proc rlpFromHex*(input: string): Rlp = proc rlpFromHex*(input: string): Rlp =
if input.len mod 2 != 0: doAssert input.len mod 2 == 0,
raise newException(BadCastError, "rlpFromHex expects a string with even number of characters (assuming two characters per byte)"
"The input string len should be even (assuming two characters per byte)")
let totalBytes = input.len div 2 let totalBytes = input.len div 2
var backingStore = newSeq[byte](totalBytes) var backingStore = newSeq[byte](totalBytes)
@ -50,8 +50,7 @@ proc rlpFromHex*(input: string): Rlp =
if parseHex(input, nextByte, i*2, 2) == 2: if parseHex(input, nextByte, i*2, 2) == 2:
backingStore[i] = byte(nextByte) backingStore[i] = byte(nextByte)
else: else:
raise newException(BadCastError, doAssert false, "rlpFromHex expects a hexademical string, but the input contains non hexademical characters"
"The input string contains invalid characters")
result.bytes = backingStore.toRange() result.bytes = backingStore.toRange()
@ -172,24 +171,29 @@ proc isInt*(self: Rlp): bool =
template maxBytes*(o: typedesc[Ordinal | uint64 | uint]): int = sizeof(o) template maxBytes*(o: typedesc[Ordinal | uint64 | uint]): int = sizeof(o)
proc toInt*(self: Rlp, IntType: typedesc): IntType = proc toInt*(self: Rlp, IntType: typedesc): IntType =
mixin maxBytes
# XXX: self insertions are not working in generic procs # XXX: self insertions are not working in generic procs
# https://github.com/nim-lang/Nim/issues/5053 # https://github.com/nim-lang/Nim/issues/5053
if self.isList() or not self.hasData(): if not self.hasData():
raise newException(BadCastError, "") raise newException(RlpTypeMismatch, "Attempt to read an Int value past the RLP end")
if self.isList():
raise newException(RlpTypeMismatch, "Int expected, but found a List")
let let
payloadStart = self.payloadOffset() payloadStart = self.payloadOffset()
payloadSize = self.payloadBytesCount() payloadSize = self.payloadBytesCount()
if payloadSize > maxBytes(IntType): if payloadSize > maxBytes(IntType):
raise newException(BadCastError, "") raise newException(RlpTypeMismatch, "The RLP contains a larger than expected Int value")
for i in payloadStart ..< (payloadStart + payloadSize): for i in payloadStart ..< (payloadStart + payloadSize):
result = cast[IntType](result shl 8) or cast[IntType](self.bytes[self.position + i]) result = cast[IntType](result shl 8) or cast[IntType](self.bytes[self.position + i])
proc toString*(self: Rlp): string = proc toString*(self: Rlp): string =
if not isBlob(): if not isBlob():
raise newException(BadCastError, "") raise newException(RlpTypeMismatch, "String expected, but the source RLP is not a blob")
let let
payloadOffset = payloadOffset() payloadOffset = payloadOffset()
@ -206,7 +210,7 @@ proc toString*(self: Rlp): string =
proc toBytes*(self: Rlp): BytesRange = proc toBytes*(self: Rlp): BytesRange =
if not isBlob(): if not isBlob():
raise newException(BadCastError, "") raise newException(RlpTypeMismatch, "Bytes expected, but the source RLP in not a blob")
let let
payloadOffset = payloadOffset() payloadOffset = payloadOffset()
@ -280,19 +284,22 @@ proc read*[R, E](rlp: var Rlp, T: type array[R, E]): T =
when E is (byte or char): when E is (byte or char):
if not rlp.isBlob: if not rlp.isBlob:
raise newException(BadCastError, "The source RLP is not a blob.") raise newException(RlpTypeMismatch, "Bytes array expected, but the source RLP is not a blob.")
var bytes = rlp.toBytes var bytes = rlp.toBytes
if result.len != bytes.len: if result.len != bytes.len:
raise newException(BadCastError, "The source RLP has incorrect size") raise newException(RlpTypeMismatch, "Fixed-size array expected, but the source RLP contains a blob of different lenght")
copyMem(addr result[0], bytes.baseAddr, bytes.len) copyMem(addr result[0], bytes.baseAddr, bytes.len)
rlp.skipElem
else: else:
if not rlp.isList: if not rlp.isList:
raise newException(BadCastError, "The source RLP is not a list.") raise newException(RlpTypeMismatch, "List expected, but the source RLP is not a list.")
if result.len != rlp.listLen: if result.len != rlp.listLen:
raise newException(BadCastError, "The source RLP has incorrect size") raise newException(RlpTypeMismatch, "Fixed-size array expected, but the source RLP contains a list of different length")
var i = 0 var i = 0
for elem in rlp: for elem in rlp:
@ -306,9 +313,10 @@ proc read*[E](rlp: var Rlp, T: type seq[E]): T =
var bytes = rlp.toBytes var bytes = rlp.toBytes
result = newSeq[byte](bytes.len) result = newSeq[byte](bytes.len)
copyMem(addr result[0], bytes.baseAddr, bytes.len) copyMem(addr result[0], bytes.baseAddr, bytes.len)
rlp.skipElem
else: else:
if not rlp.isList: if not rlp.isList:
raise newException(BadCastError, "The source RLP is not a list.") raise newException(RlpTypeMismatch, "Sequence expected, but the source RLP is not a list.")
result = newSeqOfCap[E](rlp.listLen) result = newSeqOfCap[E](rlp.listLen)

View File

@ -32,6 +32,24 @@ test "you cannot finish a list without appending enough elements":
proc withNewLines(x: string): string = x & "\n" proc withNewLines(x: string): string = x & "\n"
test "encode/decode object":
type MyObj = object
a: array[3, char]
b: int
var input: MyObj
input.a = ['e', 't', 'h']
input.b = 63
var writer = initRlpWriter()
writer.append(input)
let bytes = writer.finish()
var rlp = rlpFromBytes(bytes)
var output = rlp.read(MyObj)
check:
input == output
test "encode and decode lists": test "encode and decode lists":
var writer = initRlpList(3) var writer = initRlpList(3)
writer.append "foo" writer.append "foo"