diff --git a/apps/blockchain/ethereum/src/Protocol.sol b/apps/blockchain/ethereum/src/Protocol.sol index 66ebf9b6..79b2dd52 100644 --- a/apps/blockchain/ethereum/src/Protocol.sol +++ b/apps/blockchain/ethereum/src/Protocol.sol @@ -174,9 +174,9 @@ library Protocol { // Constant length part of the request is always 7 uint256 long. uint256 len = 7; - len += Cairo.shortStringSerializedLength(req.name); - len += Cairo.shortStringSerializedLength(req.symbol); - len += Cairo.shortStringSerializedLength(req.uri); + len += Cairo.cairoStringSerializedLength(req.name); + len += Cairo.cairoStringSerializedLength(req.symbol); + len += Cairo.cairoStringSerializedLength(req.uri); // Arrays always have their length first, then serialized length of each element. // For uint256, we can pre-compute it as a uint256 is 2 felts long. @@ -188,7 +188,7 @@ library Protocol { // We start by adding the length of the tokenURIs array. len += 1; for (uint256 i = 0; i < req.tokenURIs.length; i++) { - len += Cairo.shortStringSerializedLength(req.tokenURIs[i]); + len += Cairo.cairoStringSerializedLength(req.tokenURIs[i]); } return len; @@ -224,13 +224,13 @@ library Protocol { // Variable length part of the request. uint256 offset = 7; - offset += Cairo.shortStringSerialize(req.name, buf, offset); - offset += Cairo.shortStringSerialize(req.symbol, buf, offset); - offset += Cairo.shortStringSerialize(req.uri, buf, offset); + offset += Cairo.cairoStringSerialize(req.name, buf, offset); + offset += Cairo.cairoStringSerialize(req.symbol, buf, offset); + offset += Cairo.cairoStringSerialize(req.uri, buf, offset); offset += Cairo.uint256ArraySerialize(req.tokenIds, buf, offset); offset += Cairo.uint256ArraySerialize(req.tokenValues, buf, offset); - offset += Cairo.shortStringArraySerialize(req.tokenURIs, buf, offset); + offset += Cairo.cairoStringArraySerialize(req.tokenURIs, buf, offset); offset += Cairo.uint256ArraySerialize(req.newOwners, buf, offset); return buf; @@ -266,13 +266,13 @@ library Protocol { uint256 inc; - (inc, req.name) = Cairo.shortStringDeserialize(buf, offset); + (inc, req.name) = Cairo.cairoStringDeserialize(buf, offset); offset += inc; - (inc, req.symbol) = Cairo.shortStringDeserialize(buf, offset); + (inc, req.symbol) = Cairo.cairoStringDeserialize(buf, offset); offset += inc; - (inc, req.uri) = Cairo.shortStringDeserialize(buf, offset); + (inc, req.uri) = Cairo.cairoStringDeserialize(buf, offset); offset += inc; (inc, req.tokenIds) = Cairo.uint256ArrayDeserialize(buf, offset); @@ -281,7 +281,7 @@ library Protocol { (inc, req.tokenValues) = Cairo.uint256ArrayDeserialize(buf, offset); offset += inc; - (inc, req.tokenURIs) = Cairo.shortStringArrayDeserialize(buf, offset); + (inc, req.tokenURIs) = Cairo.cairoStringArrayDeserialize(buf, offset); offset += inc; (inc, req.newOwners) = Cairo.uint256ArrayDeserialize(buf, offset); diff --git a/apps/blockchain/ethereum/src/sn/Cairo.sol b/apps/blockchain/ethereum/src/sn/Cairo.sol index a048d186..0f3e0df5 100644 --- a/apps/blockchain/ethereum/src/sn/Cairo.sol +++ b/apps/blockchain/ethereum/src/sn/Cairo.sol @@ -209,69 +209,44 @@ library Cairo { return ((len * 2) + 1, uints); } - /** - @notice Computes the lengths of the packed cairo string. - - @dev Packed cairo string is represented as uint256[] and the length - is always preceding the array in serialization. - This is cheaper than packing the string to get the length. - */ - function shortStringSerializedLength( - string memory str - ) - internal - pure - returns (uint256) - { - // Even if the string is empty, we should return 1 - // as the serialization is expecting a uint256 as length of the Span. - bytes memory strBytes = bytes(str); - return ((strBytes.length + CAIRO_STR_LEN) / 32) + 1; - } - /** - @notice Serializes a string into a cairo short string. - @param str String to be serialized. - @param buf Buffer where serialized form is saved. - @param offset Offset in `buf` where serialization must start. - - @return Offset increment applied to serialize the value. - */ - function shortStringSerialize( - string memory str, - uint256[] memory buf, + /** + @notice Unpacks a cairo string (byte array) into a string. + + @param buf Buffer where the string is packed as a cairo string. + @param offset Offset where the unpack must start. + + @return Unpacked string. + */ + function cairoStringUnpack( + uint256[] memory buf, uint256 offset - ) - internal - pure - returns (uint256) + ) + internal + pure + returns (string memory) { - uint256[] memory packed = shortStringPack(str); - - if (packed.length == 0) { - buf[offset] = 0; - return 1; - } else { - buf[offset++] = packed.length; - - for (uint256 i = 0; i < packed.length; i++) { - buf[offset + i] = packed[i]; - } - - return 1 + packed.length; + string memory s; + uint256 dataLen = buf[offset]; + offset += 1; + for (uint256 i = offset; i < (offset + dataLen); ++i) { + s = string.concat(s, uint256AsciiNbcharsToString(buf[i], uint8(CAIRO_STR_LEN))); } + offset += dataLen; + + // handle pending word + uint8 nbChars = uint8(buf[offset + 1] & 0xFF); + s = string.concat(s, uint256AsciiNbcharsToString(buf[offset], nbChars)); + return s; } /** - @notice Packs a string into an array of uint256 (Cairo Short String -> felt252). - - @dev Cairo short strings are 31 chars long, so here we pack every - 31 chars into a uint256 (representing a felt). - To ensure there are no overflow of short string, the first byte is cleared - always cleared. - */ - function shortStringPack( + @notice Packs a string into an array of uint256 (Cairo byte array string) + + @dev Cairo (byte array) string are chunk by 31 bytes. + */ + function cairoStringPack( string memory str ) internal @@ -279,41 +254,58 @@ library Cairo { returns (uint256[] memory) { bytes memory strBytes = bytes(str); - uint256 packedLen = (strBytes.length + CAIRO_STR_LEN) / 32; + uint256 dataLen = strBytes.length / CAIRO_STR_LEN; + uint256 pendingLen = strBytes.length % CAIRO_STR_LEN; + uint256 packedLen = 1 + dataLen + 1 + 1; uint256[] memory packedData = new uint256[](packedLen); - - if (packedLen == 0) { - return packedData; - } - + uint256 index = 0; uint256 v; + uint256 offset = 0x20; // length is first u256 - for (uint256 i = 0; i < strBytes.length; i += CAIRO_STR_LEN) { + packedData[index] = dataLen; + index++; - // We take only CAIRO_STR_LEN to ensure it fits in a felt252. - assembly { v := mload(add(add(strBytes, i), CAIRO_STR_LEN)) } - - // The MSB must always be cleared to fit into a felt252. - v &= (type(uint256).max >> 8); + for (uint256 i = 0; i < dataLen; i ++) { + assembly { + v := mload(add(strBytes, offset)) + v := shr(8, v) + } packedData[index] = v; index++; + offset += CAIRO_STR_LEN; } + // pending word + uint256 mask = (1 << ((pendingLen * 8) + 1)) - 1; + + assembly { + v := mload(add(strBytes, offset)) + v := shr(8, v) + } + + mask = mask << ((31 - pendingLen) * 8); + v &= mask; + + packedData[index] = v; + index++; + + packedData[index] = pendingLen; + return packedData; } /** - @notice Deserializes a cairo short string from the given buffer. + @notice Deserializes a cairo (byte array) string from the given buffer - @param buf Buffer where data is serialized. - @param offset Offset in `buf` where deserialization must start. + @param buf Buffer where data is serialized. + @param offset Offset in `buf` where deserialization must start. - @return The offset increment applied to deserialize and the deserialized value. + @return The offset increment applied to deserialize and the deserialized value. */ - function shortStringDeserialize( + function cairoStringDeserialize( uint256[] memory buf, uint256 offset ) @@ -321,52 +313,92 @@ library Cairo { pure returns (uint256, string memory) { - uint256 len = buf[offset++]; - string memory s = shortStringUnpack(buf, offset, len); + string memory s = cairoStringUnpack(buf, offset); + uint256 packedLen = 1 + buf[offset] + 1 + 1; + return (packedLen, s); + } - // +1 to take in account the array length that was also deserialized. - return (len + 1, s); + /** + @notice Commputes the length of the packed cairo string. + + @dev Same schema as Cairo byteArray serialize. + */ + function cairoStringSerializedLength( + string memory str + ) + internal + pure + returns (uint256) + { + bytes memory strBytes = bytes(str); + uint256 dataLen = strBytes.length / CAIRO_STR_LEN; + uint256 packedLen = 1 + dataLen + 1 + 1; + return packedLen; } /** - @notice Serializes an array of strings as cairo array of short strings. + @notice Serializes a string into a cairo (byte array) string. - @param arr Array to be serialized. - @param buf Buffer where serialized form is saved. - @param offset Offset in `buf` where serialization must start. + @param str String to be serialized. + @param buf Buffer where serialized form is saved. + @param offset Offset in `buf` where serialization must start. - @return Offset increment applied to serialize the value. - */ - function shortStringArraySerialize( - string[] memory arr, + @return Offset increment applied to serialize the value. + */ + function cairoStringSerialize( + string memory str, uint256[] memory buf, uint256 offset ) internal pure returns (uint256) + { + uint256[] memory packed = cairoStringPack(str); + for (uint256 i = 0; i < packed.length; i++) { + buf[offset + i] = packed[i]; + } + return packed.length; + } + + /** + @notice Serializes an array for strings as cairo array of strings. + + @param arr Array to be serialized. + @param buf Buffer where serialized form is saved. + @param offset Offset in `buf` where serialization must start. + + @return Offset increment applied to serialize the value. + */ + function cairoStringArraySerialize( + string[] memory arr, + uint256[] memory buf, + uint256 offset + ) + internal + pure + returns (uint256) { uint256 _offset = offset; - // Arrays always have their length first in Cairo. buf[_offset++] = arr.length; for (uint256 i = 0; i < arr.length; i++) { - _offset += shortStringSerialize(arr[i], buf, _offset); + _offset += cairoStringSerialize(arr[i], buf, _offset); } return _offset - offset; } /** - @notice Deserializes an array of short string from the given buffer. + @notice Deserializes an array of cairo strin from the given buffer. - @param buf Buffer where data is serialized. - @param offset Offset in `buf` where deserialization must start. + @param buf Buffer where data is serialized. + @param offset Offset in `buf` where deserialization must start. - @return The offset increment applied to deserialize and the deserialized value. - */ - function shortStringArrayDeserialize( + @return The offset increment applied to deserialize and the deserialized value. + */ + function cairoStringArrayDeserialize( uint256[] memory buf, uint256 offset ) @@ -382,8 +414,8 @@ library Cairo { for (uint256 i = 0; i < len; i++) { ( uint256 inc, - string memory s - ) = shortStringDeserialize(buf, _offset); + string memory s + ) = cairoStringDeserialize(buf, _offset); _offset += inc; strs[i] = s; @@ -393,70 +425,39 @@ library Cairo { } /** - @notice Unpacks a cairo short string into a string. - - @param buf Buffer where the string is packed as cairo short string. - @param offset Offset where the unpack must start. - @param len Length of the string to unpack. + @notice Converts an uint256 containing ascii characters + as a string. - @return Unpacked string. + @param value String from ascii characters. */ - function shortStringUnpack( - uint256[] memory buf, - uint256 offset, - uint256 len + function uint256AsciiToString( + uint256 value ) internal pure returns (string memory) { - string memory s; - for (uint256 i = offset; i < offset + len; i++) { - s = string.concat(s, uint256AsciiToString(buf[i])); - } - - return s; + return uint256AsciiNbcharsToString(value, uint8(CAIRO_STR_LEN)); } - /** - @notice Converts an uint256 containing ascii characters - as a string. - - @param value String from ascii characters. - */ - function uint256AsciiToString( - uint256 value + function uint256AsciiNbcharsToString( + uint256 value, + uint8 length ) internal pure returns (string memory) { - string memory s = new string(32); + string memory s = new string(length); bytes memory byteString = bytes(s); - uint256 notNullCharCount = 0; - // Extract all ascii values inside the uint256. - for (uint256 i = 0; i < 32; i++) { - uint256 asciiValue = (value >> (8 * (31 - i))) & 0xFF; + // cairo string is 31 bytes with first character as MSB + for (uint256 i = 0; i < length; ++i) { + uint256 asciiValue = (value >> (8 * (CAIRO_STR_LEN - 1 - i))) & 0xFF; byteString[i] = bytes1(uint8(asciiValue)); - if (asciiValue > 0) { - notNullCharCount++; - } } - // Only use the not null ascii character to rebuild the string. - bytes memory finalString = new bytes(notNullCharCount); - - // Not opti, but accurate to remove all null chars in this situation. - uint256 finalIdx = 0; - for (uint256 i = 0; i < 32; i++) { - if (byteString[i] > 0) { - finalString[finalIdx] = byteString[i]; - finalIdx += 1; - } - } - - return string(finalString); + return s; } diff --git a/apps/blockchain/ethereum/test/Cairo.t.sol b/apps/blockchain/ethereum/test/Cairo.t.sol index dba816b2..3c492405 100644 --- a/apps/blockchain/ethereum/test/Cairo.t.sol +++ b/apps/blockchain/ethereum/test/Cairo.t.sol @@ -109,120 +109,127 @@ contract CairoTest is Test { } // - function test_shortStringSerializedLength() public { - assertEq(Cairo.shortStringSerializedLength(""), 1); - assertEq(Cairo.shortStringSerializedLength("ABCD"), 2); + function test_cairoStringSerializedLength() public { + assertEq(Cairo.cairoStringSerializedLength(""), 3); + assertEq(Cairo.cairoStringSerializedLength("ABCD"), 3); assertEq( - Cairo.shortStringSerializedLength( + Cairo.cairoStringSerializedLength( "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg"), - 3); + 4); } // - function test_shortStringPack() public { - uint256[] memory bufEmpty = Cairo.shortStringPack(""); - assertEq(bufEmpty.length, 0); + function test_cairoStringPack() public { + uint256[] memory bufEmpty = Cairo.cairoStringPack(""); + assertEq(bufEmpty[0], 0); + assertEq(bufEmpty[1], 0); + assertEq(bufEmpty[2], 0); + assertEq(bufEmpty.length, 3); + uint256[] memory bufShort = Cairo.cairoStringPack("ABC"); + assertEq(bufShort[0], 0); + assertEq(bufShort[1], 0x0041424300000000000000000000000000000000000000000000000000000000); + assertEq(bufShort[2], 3); + assertEq(bufShort.length, 3); - uint256[] memory buf = Cairo.shortStringPack("ABCD"); - assertEq(buf.length, 1); - assertEq( - buf[0], - 0x0041424344000000000000000000000000000000000000000000000000000000); + uint256[] memory bufLong = Cairo.cairoStringPack("ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg"); + assertEq(bufLong[0], 1); + assertEq(bufLong[1], 0x004142434445464748494a4b4c4d4e4f505152535455565758595a3031323334); + assertEq(bufLong[2], 0x0035363738393061626364656667000000000000000000000000000000000000); + assertEq(bufLong[3], 13); + assertEq(bufLong.length, 4); - uint256[] memory buf2 = Cairo.shortStringPack("ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg"); - assertEq(buf2.length, 2); - assertEq( - buf2[0], - 0x004142434445464748494a4b4c4d4e4f505152535455565758595a3031323334); - assertEq( - buf2[1], - 0x0035363738393061626364656667000000000000000000000000000000000000); } // - function test_shortStringUnpack() public { - uint256 v1 = 0x00414243000000000000000000000000000000000000000000000000000000; - string memory a = Cairo.uint256AsciiToString(v1); + function test_cairoStringUnpack() public { + uint256[] memory buf = new uint256[](3); + buf[0] = 0; + buf[1] = 0x0041424300000000000000000000000000000000000000000000000000000000; + buf[2] = 3; + string memory a = Cairo.cairoStringUnpack(buf, 0); assertEq(a, "ABC"); - assertTrue(Strings.equal(a, "ABC")); - uint256[] memory buf = new uint256[](1); - buf[0] = 0x00414243000000000000000000000000000000000000000000000000000000; - string memory s = Cairo.shortStringUnpack(buf, 0, buf.length); - assertEq(s, "ABC"); - - uint256[] memory buf2 = new uint256[](2); - buf2[0] = 0x004142434445464748494a4b4c4d4e4f505152535455565758595a3031323334; - buf2[1] = 0x0035363738393061626364656667000000000000000000000000000000000000; - string memory s2 = Cairo.shortStringUnpack(buf2, 0, buf2.length); - assertTrue(Strings.equal(s2, "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg")); + uint256[] memory buf2 = new uint256[](4); + buf2[0] = 1; + buf2[1] = 0x00546869732070616c696e64726f6d65206973206e6f7420617320676f6f642c; + buf2[2] = 0x0020627574206174206c656173742069742773206c6f6e6720656e6f75676800; + buf2[3] = 30; + string memory b = Cairo.cairoStringUnpack(buf2, 0); + assertEq(b, "This palindrome is not as good, but at least it's long enough"); } // - function test_shortStringSerialize() public { - uint256[] memory buf = new uint256[](2); + function test_cairoStringSerialize() public { + uint256[] memory buf = new uint256[](3); uint256 offset = 0; - offset += Cairo.shortStringSerialize("ABCD", buf, offset); - assertEq(offset, 2); - assertEq(buf[0], 1); + offset += Cairo.cairoStringSerialize("ABCD", buf, offset); + assertEq(offset, 3); + assertEq(buf[0], 0); assertEq( buf[1], 0x0041424344000000000000000000000000000000000000000000000000000000); + assertEq(buf[2], 4); } // - function test_shortStringDeserialize() public { - uint256[] memory buf = new uint256[](3); - buf[0] = 2; + function test_cairoStringDeserialize() public { + uint256[] memory buf = new uint256[](4); + buf[0] = 1; buf[1] = 0x004142434445464748494a4b4c4d4e4f505152535455565758595a3031323334; buf[2] = 0x0035363738393061626364656667000000000000000000000000000000000000; + buf[3] = 13; - (uint256 inc, string memory s) = Cairo.shortStringDeserialize(buf, 0); - assertEq(inc, 3); + (uint256 inc, string memory s) = Cairo.cairoStringDeserialize(buf, 0); + assertEq(inc, 4); assertTrue(Strings.equal(s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg")); } // - function test_shortStringArraySerialize() public { + function test_cairoStringArraySerialize() public { string[] memory data = new string[](2); data[0] = "ABCD"; data[1] = "abcd"; - uint256[] memory buf = new uint256[](5); + uint256[] memory buf = new uint256[](7); - uint256 inc = Cairo.shortStringArraySerialize(data, buf, 0); - assertEq(inc, 5); + uint256 inc = Cairo.cairoStringArraySerialize(data, buf, 0); + assertEq(inc, 7); assertEq(buf[0], 2); - assertEq(buf[1], 1); + assertEq(buf[1], 0); assertEq(buf[2], 0x0041424344000000000000000000000000000000000000000000000000000000); - assertEq(buf[3], 1); - assertEq(buf[4], 0x0061626364000000000000000000000000000000000000000000000000000000); + assertEq(buf[3], 4); + assertEq(buf[4], 0); + assertEq(buf[5], 0x0061626364000000000000000000000000000000000000000000000000000000); + assertEq(buf[6], 4); } // - function test_shortStringArrayDeserialize() public { - uint256[] memory buf = new uint256[](5); + function test_cairoStringArrayDeserialize() public { + uint256[] memory buf = new uint256[](7); buf[0] = 2; - buf[1] = 1; + buf[1] = 0; buf[2] = 0x0041424344000000000000000000000000000000000000000000000000000000; - buf[3] = 1; - buf[4] = 0x0061626364000000000000000000000000000000000000000000000000000000; + buf[3] = 4; + buf[4] = 0; + buf[5] = 0x0061626364000000000000000000000000000000000000000000000000000000; + buf[6] = 4; - (uint256 inc, string[] memory strs) = Cairo.shortStringArrayDeserialize(buf, 0); - assertEq(inc, 5); + (uint256 inc, string[] memory strs) = Cairo.cairoStringArrayDeserialize(buf, 0); + assertEq(inc, 7); assertEq(strs[0], "ABCD"); assertEq(strs[1], "abcd"); } // - function test_shortStringArrayDeserializeEmpty() public { + function test_cairoStringArrayDeserializeEmpty() public { uint256[] memory buf = new uint256[](1); buf[0] = 0; - (uint256 inc, string[] memory strs) = Cairo.shortStringArrayDeserialize(buf, 0); + (uint256 inc, string[] memory strs) = Cairo.cairoStringArrayDeserialize(buf, 0); assertEq(inc, 1); assertEq(strs.length, 0); } } + diff --git a/apps/blockchain/ethereum/test/Protocol.t.sol b/apps/blockchain/ethereum/test/Protocol.t.sol index bd5d8f36..7aa57073 100644 --- a/apps/blockchain/ethereum/test/Protocol.t.sol +++ b/apps/blockchain/ethereum/test/Protocol.t.sol @@ -132,11 +132,11 @@ contract ProtocolTest is Test { function test_requestSerializedLength() public { Request memory req = buildRequestDummy(); uint256 len = Protocol.requestSerializedLength(req); - assertEq(len, 17); + assertEq(len, 22); Request memory reqFull = buildRequestDummyFull(); uint256 lenFull = Protocol.requestSerializedLength(reqFull); - assertEq(lenFull, 26); + assertEq(lenFull, 30); } // @@ -152,38 +152,48 @@ contract ProtocolTest is Test { assertEq(buf[4], 0x123); assertEq(buf[5], 0x0); assertEq(buf[6], 0x789); - assertEq(buf[7], 0); - assertEq(buf[8], 0); - assertEq(buf[9], 1); - assertEq(buf[10], 0x0041424344000000000000000000000000000000000000000000000000000000); - assertEq(buf[11], 1); - assertEq(buf[12], 1); - assertEq(buf[13], 0); - assertEq(buf[14], 0); - assertEq(buf[15], 0); - assertEq(buf[16], 0); + assertEq(buf[7], 0); // name data len + assertEq(buf[8], 0); // name pending word + assertEq(buf[9], 0); // name pending word len + assertEq(buf[10], 0); // symbol data len + assertEq(buf[11], 0); // symbol pending word + assertEq(buf[12], 0); // symbol pending word len + assertEq(buf[13], 0); // base_uri data len + assertEq(buf[14], 0x0041424344000000000000000000000000000000000000000000000000000000); // base_uri pending word + assertEq(buf[15], 4); // base_uri pending word len + assertEq(buf[16], 1); // ids len + assertEq(buf[17], 1); // ids[0] low + assertEq(buf[18], 0); // ids[0] high + assertEq(buf[19], 0); // values len + assertEq(buf[20], 0); // uris len + assertEq(buf[21], 0); // new owners len } // function test_requestDeserialize() public { - uint256[] memory data = new uint256[](17); - data[0] = 0x1; - data[1] = 0x1; - data[2] = 0x0; - data[3] = 0x0; - data[4] = 0x123; - data[5] = 0x0; - data[6] = 0x789; - data[7] = 0; - data[8] = 0; - data[9] = 1; - data[10] = 0x0041424344000000000000000000000000000000000000000000000000000000; - data[11] = 1; - data[12] = 1; - data[13] = 0; - data[14] = 0; - data[15] = 0; - data[16] = 0; + uint256[] memory data = new uint256[](22); + data[0] = 0x1; // header + data[1] = 0x1; // hash low + data[2] = 0x0; // hash high + data[3] = 0x0; // collectionL1 + data[4] = 0x123; // collectionL2 + data[5] = 0x0; // ownerL1 + data[6] = 0x789; // ownerL2 + data[7] = 0; // name data len + data[8] = 0; // name pending word + data[9] = 0; // name pending word len + data[10] = 0; // symbol data len + data[11] = 0; // symbol pending word + data[12] = 0; // symbol pending word len + data[13] = 0; // base_uri data len + data[14] = 0x0041424344000000000000000000000000000000000000000000000000000000; // base_uri pending word + data[15] = 4; // base_uri pending word len + data[16] = 1; // ids len + data[17] = 1; // ids[0] low + data[18] = 0; // ids[0] high + data[19] = 0; // values len + data[20] = 0; // uris len + data[21] = 0; // new owners len Request memory req = Protocol.requestDeserialize(data, 0);