-
Notifications
You must be signed in to change notification settings - Fork 144
Open
Labels
Description
Contract:
public static ByteString abiencode(ByteString data, bool reverse)
{
int len = data.Length;
ExecutionEngine.Assert(len <= 32, "Too long data");
ByteString prefix = "";
for (int i = 0; i < 32 - len; ++i)
prefix += "\x00";
if (reverse)
{
for (int i = 1; i <= len; ++i)
prefix += data[len - i];
return prefix;
}else
return prefix + data;
}Sample assembly:
# Code abi.cs line 19: "prefix += data[len - i];"
0109 LDLOC1
0110 LDARG0
0111 LDLOC0
0112 LDLOC2
0113 SUB
0114 DUP
0115 PUSHINT32 00-00-00-80 # -2147483648
0120 JMPGE 04 # pos: 124 (offset: 4)
0122 JMP 0A # pos: 132 (offset: 10)
0124 DUP
0125 PUSHINT32 FF-FF-FF-7F # 2147483647
0130 JMPLE 1E # pos: 160 (offset: 30)
0132 PUSHINT64 FF-FF-FF-FF-00-00-00-00 # 4294967295
0141 AND
0142 DUP
0143 PUSHINT32 FF-FF-FF-7F # 2147483647
0148 JMPLE 0C # pos: 160 (offset: 12)
0150 PUSHINT64 00-00-00-00-01-00-00-00 # 4294967296
0159 SUB
0160 PICKITEM
0161 CAT
0162 CONVERT 28 # ByteString type
0164 DUP
0165 STLOC1
0166 DROP
Invocation:
invokefunction('abiencode', [Hash160Str('0x06b9931e101f2e9885e76503874f2cebf69bf790'), True])'{"jsonrpc":"2.0","method":"invokefunction","params":["0x65b9e0f9196acc8a58dad1a42fa459ecaf2b93c0","abiencode",[{"type":"Hash160","value":"0x06b9931e101f2e9885e76503874f2cebf69bf790"},{"type":"Boolean","value":true}],[{"account":"0x7651a826505c19ca20326381e96b0d5439519a0c","scopes":"CalledByEntry","allowedcontracts":[],"allowedgroups":[],"rules":[]}]],"id":1}'
Expected (64 hex chars, 32 bytes):
00000000000000000000000006b9931e101f2e9885e76503874f2cebf69bf790
Actual returned (many additional bytes 00):
00000000000000000000000006b90093001e101f2e98008500e700650387004f2ceb00f6009b00f7009000
!! !! !! !! !! !! !! !! !! !! !!
Explanation:
At the instruction 0160 PICKITEM before 0161 CAT, NeoVM obtains an Integer from ByteString. This is guided by
neo-vm/src/Neo.VM/ExecutionEngine.cs
Lines 1242 to 1250 in ea03316
| case PrimitiveType primitive: | |
| { | |
| ReadOnlySpan<byte> byteArray = primitive.GetSpan(); | |
| int index = (int)key.GetInteger(); | |
| if (index < 0 || index >= byteArray.Length) | |
| throw new CatchableException($"The value {index} is out of range."); | |
| Push((BigInteger)byteArray[index]); | |
| break; | |
| } |
which casts a single byte to an Integer. Then in the following instruction
CAT, we try to GetSpan() of the Integer, which is generated byneo-vm/src/Neo.VM/Types/Integer.cs
Line 35 in ea03316
| public override ReadOnlyMemory<byte> Memory => value.IsZero ? ReadOnlyMemory<byte>.Empty : value.ToByteArray(); |
Here
value.ToByteArray() may generate additional byte 00 when it's actually unnecessary. This is described by https://learn.microsoft.com/en-us/dotnet/api/system.numerics.biginteger.tobytearray?view=net-7.0#system-numerics-biginteger-tobytearray(system-boolean-system-boolean)
(And I cannot even come up with a workaround for the problem...)
Reactions are currently unavailable