Fix getting values for arrays. (#73)

Co-authored-by: nikugogoi <95nikass@gmail.com>
This commit is contained in:
Ashwin Phatak 2021-06-17 11:59:53 +05:30 committed by GitHub
parent 7d609f9a2b
commit e3ef17d547
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 24 deletions

View File

@ -26,24 +26,24 @@ $ yarn test
* [x] Fixed-size byte arrays * [x] Fixed-size byte arrays
* [x] Enums * [x] Enums
* [ ] Function Types * [ ] Function Types
* [ ] Reference Types * [x] Reference Types
* [ ] Arrays * [x] Arrays
* [x] Get all elements in array * [x] Get all elements in array
* [x] Get element in array by index * [x] Get element in array by index
* [ ] Fixed size arrays * [x] Fixed size arrays
* [x] Integer Type * [x] Integer Type
* [x] Boolean Type * [x] Boolean Type
* [x] Address Type * [x] Address Type
* [ ] Fixed-size byte arrays * [x] Fixed-size byte arrays
* [x] Enum type * [x] Enum type
* [x] Dynamically-sized byte array * [x] Dynamically-sized byte array
* [x] Struct Type * [x] Struct Type
* [x] Mapping Type * [x] Mapping Type
* [ ] Dynamically-sized arrays * [x] Dynamically-sized arrays
* [x] Integer Type * [x] Integer Type
* [x] Boolean Type * [x] Boolean Type
* [x] Address Type * [x] Address Type
* [ ] Fixed-size byte arrays * [x] Fixed-size byte arrays
* [x] Enum Type * [x] Enum Type
* [x] Dynamically-sized byte array * [x] Dynamically-sized byte array
* [x] Struct Type * [x] Struct Type
@ -54,14 +54,14 @@ $ yarn test
* [x] Dynamically-sized byte array * [x] Dynamically-sized byte array
* [x] Bytes * [x] Bytes
* [x] String * [x] String
* [ ] Structs * [x] Structs
* [x] Get struct value with all members * [x] Get struct value with all members
* [x] Value Types * [x] Value Types
* [x] Get value of a single member in struct * [x] Get value of a single member in struct
* [ ] Reference Types * [x] Reference Types
* [x] Struct type members (nested) * [x] Struct type members (nested)
* [x] Fixed size Array members * [x] Fixed size Array members
* [ ] Dynamically sized Array members * [x] Dynamically sized Array members
* [x] Bytes and string type members * [x] Bytes and string type members
* [x] Mapping type members * [x] Mapping type members
* [ ] Mapping Types * [ ] Mapping Types

View File

@ -367,7 +367,7 @@ describe('Get value from storage', () => {
expect(proofData.length).to.equal(addressArray.length); expect(proofData.length).to.equal(addressArray.length);
}); });
it.skip('get value for fixed size arrays of fixed size bytes type', async () => { it('get value for fixed size arrays of fixed size bytes type', async () => {
const expectedValue = Array.from({ length: 5 }, () => ethers.utils.hexlify(ethers.utils.randomBytes(10))); const expectedValue = Array.from({ length: 5 }, () => ethers.utils.hexlify(ethers.utils.randomBytes(10)));
await testFixedArrays.setFixedBytesArray(expectedValue); await testFixedArrays.setFixedBytesArray(expectedValue);
@ -592,7 +592,7 @@ describe('Get value from storage', () => {
expect(value).to.equal(addressArray[arrayIndex]); expect(value).to.equal(addressArray[arrayIndex]);
}); });
it.skip('get value for dynamic sized array of fixed size byte array', async () => { it('get value for dynamic sized array of fixed size byte array', async () => {
const fixedBytesArray = Array.from({ length: 4 }, () => ethers.utils.hexlify(ethers.utils.randomBytes(10))); const fixedBytesArray = Array.from({ length: 4 }, () => ethers.utils.hexlify(ethers.utils.randomBytes(10)));
await testDynamicArrays.setFixedBytesArray(fixedBytesArray); await testDynamicArrays.setFixedBytesArray(fixedBytesArray);
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
@ -1072,7 +1072,7 @@ describe('Get value from storage', () => {
}; };
dynamicArrayStruct = { dynamicArrayStruct = {
address1: signers[4].address, address1: signers[4].address.toLowerCase(),
uintArray: [1, 2, 3, 4, 5].map(BigInt) uintArray: [1, 2, 3, 4, 5].map(BigInt)
}; };
}); });
@ -1099,7 +1099,7 @@ describe('Get value from storage', () => {
expect(value).to.eql(stringStruct); expect(value).to.eql(stringStruct);
}); });
it.skip('get value for struct with dynamic array members', async () => { it('get value for struct with dynamic array members', async () => {
await testReferenceStructs.setDynamicArrayStruct(dynamicArrayStruct); await testReferenceStructs.setDynamicArrayStruct(dynamicArrayStruct);
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'dynamicArrayStruct'); const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'dynamicArrayStruct');
@ -1138,7 +1138,7 @@ describe('Get value from storage', () => {
expect(value).to.eql(stringStruct[member]); expect(value).to.eql(stringStruct[member]);
}); });
it.skip('get value of dynamic array member in a struct', async () => { it('get value of dynamic array member in a struct', async () => {
const member = 'uintArray'; const member = 'uintArray';
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'dynamicArrayStruct', member); const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'dynamicArrayStruct', member);
@ -1247,6 +1247,9 @@ describe('Get value from storage', () => {
expect(value).to.equal(expectedValue); expect(value).to.equal(expectedValue);
}); });
// TODO: Fix getting value for mapping with keys as fixed-size byte array
// Zero value is returned if using fixed-sized byte array keys of length less than 32 bytes
// Type Bytes32 works whereas types like bytes16, bytes24 do not work.
it.skip('get value for mapping with fixed-size byte array keys', async () => { it.skip('get value for mapping with fixed-size byte array keys', async () => {
const mapKey = ethers.utils.hexlify(ethers.utils.randomBytes(8)); const mapKey = ethers.utils.hexlify(ethers.utils.randomBytes(8));
const [, signer1] = await ethers.getSigners(); const [, signer1] = await ethers.getSigners();

View File

@ -219,17 +219,22 @@ const getDynamicArrayInfo = async (getStorageAt: GetStorageAt, blockHash: string
const getArrayValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, types: Types, mappingKeys: MappingKey[], slot: string, base: string, arraySize: number) => { const getArrayValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, types: Types, mappingKeys: MappingKey[], slot: string, base: string, arraySize: number) => {
const resultArray = []; const resultArray = [];
const proofs = []; const proofs = [];
let { numberOfBytes: baseNumberOfBytes, label: baseTypeLabel } = types[base]; const { numberOfBytes: baseNumberOfBytes } = types[base];
// Address type elements use an entire single slot i.e. 32 bytes.
if (baseTypeLabel === 'address' || baseTypeLabel.includes('contract')) {
baseNumberOfBytes = '32';
}
const getArrayElement = async (mappingKeys: MappingKey[], index: number) => { const getArrayElement = async (mappingKeys: MappingKey[], index: number) => {
const arrayOffset = index * Number(baseNumberOfBytes); let arraySlotOffset = 0;
const arraySlot = BigNumber.from(slot).add(Math.floor(arrayOffset / 32)).toHexString(); let slotIndex;
const arraySlotOffset = arrayOffset % 32;
if (Number(baseNumberOfBytes) <= 32) {
const elementsInSlot = Math.floor(32 / Number(baseNumberOfBytes));
slotIndex = Math.floor(index / elementsInSlot);
arraySlotOffset = (index % elementsInSlot) * Number(baseNumberOfBytes);
} else {
const slotsUsedByElement = Math.ceil(Number(baseNumberOfBytes) / 32);
slotIndex = slotsUsedByElement * index;
}
const arraySlot = BigNumber.from(slot).add(slotIndex).toHexString();
return getDecodedValue(getStorageAt, blockHash, address, types, { slot: arraySlot, offset: arraySlotOffset, type: base }, mappingKeys); return getDecodedValue(getStorageAt, blockHash, address, types, { slot: arraySlot, offset: arraySlotOffset, type: base }, mappingKeys);
}; };
@ -240,7 +245,6 @@ const getArrayValue = async (getStorageAt: GetStorageAt, blockHash: string, addr
return getArrayElement(remainingKeys, arrayIndex); return getArrayElement(remainingKeys, arrayIndex);
} }
// TODO: Get values in single call and parse according to type.
// Loop over elements of array and get value. // Loop over elements of array and get value.
for (let i = 0; i < arraySize; i++) { for (let i = 0; i < arraySize; i++) {
const { value, proof } = await getArrayElement(mappingKeys, i); const { value, proof } = await getArrayElement(mappingKeys, i);
@ -309,6 +313,7 @@ const getStructureValue = async (getStorageAt: GetStorageAt, blockHash: string,
* @param getStorageAt * @param getStorageAt
*/ */
const getInplaceValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, slot: string, offset: number, numberOfBytes: string) => { const getInplaceValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, slot: string, offset: number, numberOfBytes: string) => {
// TODO: Memoize getStorageAt function for duplicate multiple calls.
const { value, proof } = await getStorageAt({ blockHash, contract: address, slot }); const { value, proof } = await getStorageAt({ blockHash, contract: address, slot });
const valueLength = utils.hexDataLength(value); const valueLength = utils.hexDataLength(value);