According to EVM opcode description:
Note: the below is written in kind of python array slicing convention: array starts at index 0 and array[0:2] means the array[0] and array[1] elements (not including the array[2] element):
CALLDATACOPY Stack (from top to bottom): destOffset | offset | length
=> memory[destOffset:destOffset+length] = msg.data[offset:offset+length]
When you reach CALLDATACOPY
, your stack is 0 => 31 => 4
which means:
destOffset
= 0
offset
= 31
length
= 4
This one copies 4 bytes from the calldata from the 31th byte to write to memory at location 0.
Because your calldata only has 32 bytes 0xff.....ff so it copies only the last 0xff and fill the rest with 0x00,0x00,0x00 and write to memory at location 0.
So the final memory at location 0: 0xff000000... the rest is the memory before the call to CALLDATACOPY
I guess.
Test contract with the same sequence of opcode, call it with your calldata:
contract Test{
fallback() external {
assembly {
calldatacopy(0, 31, 4)
}
}
}
--- Update: another example : When you reach CALLDATACOPY, your stack is 0 => 29 => 3 which means:
- It's equivalent to
calldatacopy(0, 29, 3)
in the above test contract
destOffset = 0
; offset = 29
; length = 3
- Call data is : 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAABBCCDDEE
- 0th byte 0xff | 1st byte 0xff | .... | 28th byte 0xBB | 29th byte : 0xCC | ...
- memory[0,3] = 0xcc 0xdd 0xee....