Open Hecate2 opened 11 months ago
And neo-go does pick a single byte
a solution to solve your problem: use public static byte[] Reverse(this Array source);
a solution to solve your problem: use
public static byte[] Reverse(this Array source);
Many thanks! The following contract works:
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)
return prefix + (ByteString)((byte[])data).Reverse();
else
return prefix + data;
}
And, also because of BigInteger adding an additional byte 0x00, (ByteString)BigInteger.Parse("63076024560530113402979550242307453568063438748328787417531900361828837441551")
exceeds the 32-byte limit, if written in contracts. The purpose is to inject a constant hash 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f
Just a question from my side:
Then in the following instruction CAT, we try to GetSpan() of the Integer, which is generated by https://github.com/neo-project/neo-vm/blob/ea03316ae34ca4bc7a30fd4b65dad74f1fed7e17/src/Neo.VM/Types/Integer.cs#L35 Here value.ToByteArray() may generate additional byte 00 when it's actually unnecessary.
Are you sure that exactly this part of code is responsible for extra zeroes? Because, as MSDN doc postulates, If the value is zero, returns an array of one byte whose element is 0x00.
. And for the case of the zero integer, the Neo's conversion code provided above has value.IsZero ? ReadOnlyMemory<byte>.Empty
check that will return an empty byte array if the source integer is zero.
Zero integers are not related to the problem. The additional zeroes come from bytes of large values (probably those larger than 0x80?). If those large bytes are encoded with only 1 byte, they will be considered negative (minus) values, because the most significant bit is 1. So .NET encodes them with an additional byte 0x00 for them to be positive. For example, byte 0xb9 is encoded as 0xb900 (little-endian), because 0xb9 >= 0x80. (And then the little-endian bytes are inserted into a big-endian ByteString...)
We can change
Push((BigInteger)byteArray[index]);
To create a ByteString instead of BigInteger
Contract:
Sample assembly:
Invocation:
Expected (64 hex chars, 32 bytes):
Actual returned (many additional bytes
00
):Explanation: At the instruction
0160 PICKITEM
before0161 CAT
, NeoVM obtains an Integer from ByteString. This is guided by https://github.com/neo-project/neo-vm/blob/ea03316ae34ca4bc7a30fd4b65dad74f1fed7e17/src/Neo.VM/ExecutionEngine.cs#L1242-L1250 which casts a single byte to an Integer. Then in the following instructionCAT
, we try to GetSpan() of the Integer, which is generated by https://github.com/neo-project/neo-vm/blob/ea03316ae34ca4bc7a30fd4b65dad74f1fed7e17/src/Neo.VM/Types/Integer.cs#L35 Herevalue.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...)