¿Cómo derivar la clave de almacenamiento de la asignación a una cuenta?

Estoy tratando de derivar los cambios de almacenamiento de cuentas asignando cuentas (aquí, las cuentas representan la dirección clave en el mapeo de asignación de saldos ERC20 (dirección => uint256) ) a sus claves de almacenamiento correspondientes a partir de un seguimiento de transacción de paridad.

Un seguimiento de ejemplo (obtenido a través de web3.py) de la transacción https://etherscan.io/tx/0x210cfe3d3b62f415ed0327a3c6177086df4937b6280031255360b6d137308554 es:

{
    'output': '0x',
    'stateDiff': {
        '0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5': {
            'balance': {
                '*': {
                    'from': '0x122291885d6222bc5ec',
                    'to': '0x12229193ca3f58565ec'
                }
            },
            'code': '=',
            'nonce': '=',
            'storage': {}
        },
        '0xaddba95f769b5d42c02e144102817eab9d00efd3': {
            'balance': '=',
            'code': '=',
            'nonce': '=',
            'storage': {
                '0x1ded6755a6d7d843883da8cd8948931cf9f8b1e8f8983ad77e1685ece0b92fc2': {
                    '*': {
                        'from': '0x0000000000000000000000000000000000000000000000000000000000000000',
                        'to': '0x000000000000000000000000000000000000000000000000000bfb8d0ebc5000'
                    }
                },
                '0x5020d77d5345022a5466af6f78731b10f92d31e17961c23ea6ce5c185dc75d49': {
                    '*': {
                        'from': '0x0000000000000000000000000000000000000000000000000000000000000000',
                        'to': '0x0000000000000000000000000000000000000000000000000000e1412a5f1c00'
                    }
                }
            }
        },
        '0xf2eeb980b60b9ed146636c65ecc5e8f27a8aed40': {
            'balance': {
                '*': {
                    'from': '0x2c4c7111f4801a8',
                    'to': '0x2c410434bee61a8'
                }
            },
            'code': '=',
            'nonce': {
                '*': {
                    'from': '0x143',
                    'to': '0x144'
                }
            },
            'storage': {}
        }
    },
    'trace': [{
        'action': {
            'callType': 'call',
            'from': '0xf2eeb980b60b9ed146636c65ecc5e8f27a8aed40',
            'gas': '0x9008',
            'input': '0xa9059cbb000000000000000000000000e034e561ce112c5f261f15d447e8f2436d9625040000000000000000000000000000000000000000000000000000e130de0be000',
            'to': '0xaddba95f769b5d42c02e144102817eab9d00efd3',
            'value': '0x0'
        },
        'result': {
            'gasUsed': '0x3924',
            'output': '0x'
        },
        'subtraces': 0,
        'traceAddress': [],
        'type': 'call'
    }],
    'vmTrace': None
}

Según https://solidity.readthedocs.io/en/v0.4.24/miscellaneous.html#layout-of-state-variables-in-storage , "el valor correspondiente a una clave de mapeo k se encuentra en keccak256(k . p ) donde . es concatenación". Soy consciente del pad izquierdo necesario, de acuerdo con "keccack(LeftPad32(key, 0), LeftPad32(map position, 0))" https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat , aunque las funciones hash parecen manejar esto internamente.

Probé el Web3.sha3() para esta clave de almacenamiento keccak256, y otros problemas en StackOverflow mencionan que Solidity emplea un algoritmo hash ligeramente diferente. Nevertheless, also by using the proper Web3.soliditySha3() I do not arrive at a hash that would present me the key found in the the trace (here, 0x1ded6755a6d7d843883da8cd8948931cf9f8b1e8f8983ad77e1685ece0b92fc2 or 0x5020d77d5345022a5466af6f78731b10f92d31e17961c23ea6ce5c185dc75d49 ) to the account 0xf2eeb980b60b9ed146636c65ecc5e8f27a8aed40 .

¿Cuál es la forma correcta (preferiblemente con web3.py) de cifrar la cuenta y la posición para llegar a la clave de almacenamiento de mapas?

Daniel, ¿cómo estás determinando el valor de p. En realidad, esto está relacionado con la posición de la variable de mapeo y lo necesita.
Para la posición, intenté enumerar la posición de acuerdo con la lógica de "ranura" de programtheblockchain.com/posts/2018/03/09/… . Lamentablemente sin éxito.

Respuestas (1)

Como mencionó @jaime en los comentarios, necesita la positionvariable de mapeo para encontrar la relación entre addressy storage key.

Puede encontrar un código que he escrito para detectar el positioncontrato erc20 básico a continuación (usando web3.py):

    import json
    from web3.auto.infura import w3
    from eth_utils import remove_0x_prefix, to_int, to_checksum_address, to_hex
    
    # erc20 contract address
    CONTRACT_ADDRESS = '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7'
    # address with non zero token balance
    HOLDER_ADDRESS = '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb' 

    def getStorageAtIndex(i):
        pos = str(i).rjust(64, '0')
        key = remove_0x_prefix(HOLDER_ADDRESS).rjust(64, '0').lower()
        storage_key = to_hex(w3.sha3(hexstr=key + pos))
        return to_int(w3.eth.getStorageAt(CONTRACT_ADDRESS, storage_key))

    for i in range(0, 20):
        if getStorageAtIndex(i) != 0:
            print("position is {}".format(i))

Resultado:
>>> position is 5

Una vez que tenga el valor correcto de la posición, puede obtener fácilmente los valores almacenados en el contrato.

    pos = str(5).rjust(64, '0')
    key = remove_0x_prefix(HOLDER_ADDRESS).rjust(64, '0').lower()
    storage_key = to_hex(w3.sha3(hexstr=key + pos))
    w3.eth.getStorageAt(CONTRACT_ADDRESS, storage_key)