以太坊事件log分析

合约调用的时候回产生event log(事件日志),这个event log会记录在一个调用合约的交易的receipt函数中。通过对event log的分析可以得到跟多这个交易的一些详细内容

  1. 获得event log
    首先获得一个块中的交易数组
        web3.eth.getBlock(blockHashOrNumber, true, function(error, blockData) {}
    其次根据返回的blockData.transactions获得交易数组
    随后可以根据交易数组获得每个交易的hash
    最后
        var data = web3.eth.getTransactionReceipt(hash);
        获得这个交易的更多交易细节
        var logsArr = data.logs;
        得到了event log
    
  2. 分析
    1. 规则
        例如:代币的购买,会触发代币的Transfer事件,这是ERC20标准的基本规定
        1. 对事件的基本定义
            event Transfer(address indexed from, address indexed to, uint256 value);
            事件名字:Transfer
            事件的参数:address, address, uint256,且此事件的from和to参数前有indexed标记,value没有indexed标记
            事件的规则是:
                topic[0]: keccak(Transfer(address,address,uint256)),对事件的字符做keccak散列运算
                topic[1]: address类型from参数补齐64位
                topic[1]: address类型to参数补齐64位
                data: 没有indexed标记的value的值转化为16进制,并补齐64位
            具体的实现如下:
                // 从from这个账户发送 value个toekn(代币) 到to这个账户
                Transfer(address indexed from, address indexed to, uint256 value)
                // 事件名字散列值
                topic[0] = keccak(Transfer(address,address,uint256)) = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
                // 后续的就是参数中有indexed标志的参数 所传递的值
                // fromeAddress 补齐64位
                topic[1] = 0x0000000000000000000000000000000000000000000000000000000000000000
                topic[2] = 0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550
                // 然后是另一个不带indexed标志的参数的 值传入
                // 这就是一个数字转化为16进制 然后补齐64位
                data = 0x00000000000000000000000000000000000000000000002be4fb8c854544b555
    2. 具体的事件的分析
        1. 基本交易信息
            - 交易hash 
            - 合约1的code 
            - 合约2的code 
            事件的记录由合约代码决定
        2. 交易描述
            普通用户地址向合约地址1转账1.75eth,所触发的事件分析
        3.事件日志字段
            1. 第一个事件日志
                "address": "0x78b039921e84e726eb72e7b1212bb35504c645ca",
                "topics": [
                    "0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885",
                    "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
                ],
                "data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555"
                即合约地址为0x78b039921e84e726eb72e7b1212bb35504c645ca的合约触发了事件,这个合约地址时合约2
                根据对代码的分析以及keccak散列后的结果对比,确定是
                Mint(address indexed to, uint256 amount)
                即对字符:Mint(address,uint256) 处理后的0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885
                这个日志,就是对第二个参数topics[1]的地址0xd53487c6b3a88dded611079b7ae7b377f4888550分发了data字段内容(转化后为809.709931333333333333),即转了809.709931333333333333个Sether币
            2. 第二个事件日志
                "address": "0x78b039921e84e726eb72e7b1212bb35504c645ca",
                "topics": [
                    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
                    "0x0000000000000000000000000000000000000000000000000000000000000000",
                    "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
                ],
                "data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555"
                合约2触发的事件,将代币分配到某个地址的操作
                Transfer(address indexed from, address indexed to, uint256 value)
                对Transfer(address,address,uint256) 处理得0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
                从0x地址,实际就是从空地址转账,也就是代币增发(类似于系统又印了多少钞票),然后分配给0xd53487c6b3a88dded611079b7ae7b377f4888550
                data字段是分配的具体数字: 809.709931333333333333
            3. 第三个事件日志
                "address": "0x20bf6672497941bd3e4ec5fd551de5c31e0a898a",
                "topics": [
                    "0xde080401e79886a3afdeac3fa6426ade55f095bfd9fbe1065b017f1e90479a86",
                    "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550",
                    "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
                ],
                "data": "0x000000000000000000000000000000000000000000000000181449a077eb7c0000000000000000000000000000000000000000000000002be4fb8c854544b555"
                这个是合约1触发的事件日志
                SethTokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount)
                对字符:SethTokenPurchase(address,address,uint256,uint256) 处理后0xde080401e79886a3afdeac3fa6426ade55f095bfd9fbe1065b017f1e90479a86
                这件事我的理解就是,记录了0xd53487c6b3a88dded611079b7ae7b377f4888550地址花了1.73509271 eth购买了809.709931333333333333 个Sether币
                data字段是两个字段,可以去除0x字符后,然后64位分割为两个字段,然后转化为10进制即可。
    
  3. 遗留问题
    从普通地址转账给合约地址的eth,通过合约处理后,转给了合约的钱包地址,这个交易信息,怎么获取?
    被下面合约代码处理了
    // 钱包地址调用transfer函数,这个函数是一个公开函数,就是类似于evm中默认有的
    wallet.transfer(msg.value);
    
    这种合约内部的交易怎么处理?
    
    参考:合约内部交易信息获取

交易的真实数据信息如下

{
    "blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
    "blockNumber": 4906655,
    "contractAddress": null,
    "cumulativeGasUsed": 2815773,
    "from": "0xd53487c6b3a88dded611079b7ae7b377f4888550",
    "gasUsed": 73886,
    "logs": [
        {
            "address": "0x78b039921e84e726eb72e7b1212bb35504c645ca",
            "topics": [
                "0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885",
                "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
            ],
            "data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555",
            "blockNumber": 4906655,
            "transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
            "transactionIndex": 105,
            "blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
            "logIndex": 16,
            "removed": false
        },
        {
            "address": "0x78b039921e84e726eb72e7b1212bb35504c645ca",
            "topics": [
                "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
                "0x0000000000000000000000000000000000000000000000000000000000000000",
                "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
            ],
            "data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555",
            "blockNumber": 4906655,
            "transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
            "transactionIndex": 105,
            "blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
            "logIndex": 17,
            "removed": false
        },
        {
            "address": "0x20bf6672497941bd3e4ec5fd551de5c31e0a898a",
            "topics": [
                "0xde080401e79886a3afdeac3fa6426ade55f095bfd9fbe1065b017f1e90479a86",
                "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550",
                "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
            ],
            "data": "0x000000000000000000000000000000000000000000000000181449a077eb7c0000000000000000000000000000000000000000000000002be4fb8c854544b555",
            "blockNumber": 4906655,
            "transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
            "transactionIndex": 105,
            "blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
            "logIndex": 18,
            "removed": false
        }
    ],
    "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000800800000020008020000008000000000002000000000000200000000000000000000000020000000000000000000800000000000000400000000010000000000000000000000000000000200000000000000000000000000000000000000000000080000000000000000000000000000000002000000000000000000000000000000002000000000000000000000000000044000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000",
    "status": "0x1",
    "to": "0x20bf6672497941bd3e4ec5fd551de5c31e0a898a",
    "transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
    "transactionIndex": 105
}

合约1的代码摘抄如下
合约1

....上半部分和合约2的代码一样....
部分代码如下

contract MintableToken is StandardToken, Ownable {  
  // 定义的时间Mint
  event Mint(address indexed to, uint256 amount);
  event MintFinished();

  bool public mintingFinished = false;

  .....

  /**
   * @dev Function to mint tokens
   * @param _to The address that will receive the minted tokens.
   * @param _amount The amount of tokens to mint.
   * @return A boolean that indicates if the operation was successful.
   */
  // mint函数
  function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) {
    totalSupply = totalSupply.add(_amount);
    balances[_to] = balances[_to].add(_amount);
    // 调用Mint事件
    Mint(_to, _amount);
    // 调用Transfer事件进行代币分发
    Transfer(0x0, _to, _amount);
    return true;
  }

  ....
}

/**
 * @title SetherToken
 * @dev Sether ERC20 Token that can be minted.
 * It is meant to be used in sether crowdsale contract.
 */
// 继承于MintableToken,可以使用它的函数调用
contract SetherToken is MintableToken {
    
    string public constant name = "Sether";
    string public constant symbol = "SETH";
    uint8 public constant decimals = 18;

    function getTotalSupply() public returns (uint256) {
        return totalSupply;
    }
}

/**
 * @title SetherBaseCrowdsale
 * @dev SetherBaseCrowdsale is a base contract for managing a sether token crowdsale.
 */
contract SetherBaseCrowdsale {
    using SafeMath for uint256;

    // The token being sold
    // 被分发的代币
    SetherToken public token;

    ....

    // address where funds are collected
    // 募集的资金都转到这个钱包地址
    address public wallet;

    ......

    /**
    * event for token purchase logging
    * @param purchaser who paid for the tokens
    * @param beneficiary who got the tokens
    * @param value weis paid for purchase
    * @param amount amount of tokens purchased
    */
    event SethTokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
    
    // 和合约名字一样的函数,合约创建时自动调用
    function SetherBaseCrowdsale(uint256 _rate, address _wallet) {
        require(_rate > 0);
        require(_wallet != address(0));
        // 创建代币合约,并存储代币合约地址
        token = createTokenContract();
        rate = _rate;
        // 钱包地址
        wallet = _wallet;
    }

    // fallback function can be used to buy tokens
    // fallback function 当有账户向这个地址转真实的eth时,自动触发这个函数
    function () payable {
        // 调用buyTokens函数
        buyTokens(msg.sender);
    }

    // low level token purchase function
    function buyTokens(address beneficiary) public payable {
        ....
        // 代币调用mint函数,进而触发这个函数中事件
        token.mint(beneficiary, tokens);
        // 触发SethTokenPurchase事件
        SethTokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
        // 调用forwardFunds函数
        forwardFunds();
    }
    
    // send ether to the fund collection wallet
    // 将收到的eth转到钱包地址
    function forwardFunds() internal {
        // 钱包地址调用transfer函数,这个函数是一个公开函数,就是类似于evm中默认有的
        wallet.transfer(msg.value);
    }

    .....
}

/**
 * @title SetherMultiStepCrowdsale
 * @dev Multi-step payment policy contract that extends SetherBaseCrowdsale
 */
contract SetherMultiStepCrowdsale is SetherBaseCrowdsale {
    .....
}

/**
 * @title SetherCappedCrowdsale
 * @dev Extension of SetherBaseCrowdsale with a max amount of funds raised
 */
contract SetherCappedCrowdsale is SetherMultiStepCrowdsale {
    .....
}

/**
 * @title SetherStartableCrowdsale
 * @dev Extension of SetherBaseCrowdsale where an owner can start the crowdsale
 */
contract SetherStartableCrowdsale is SetherBaseCrowdsale, Ownable {
  .....
}

/**
 * @title SetherFinalizableCrowdsale
 * @dev Extension of SetherBaseCrowdsale where an owner can do extra work
 * after finishing.
 */
contract SetherFinalizableCrowdsale is SetherBaseCrowdsale, Ownable {
  .....
}

/**
 * @title SetherCrowdsale
 * @dev This is Sether's crowdsale contract.
 */
contract SetherCrowdsale is SetherCappedCrowdsale, SetherStartableCrowdsale, SetherFinalizableCrowdsale {
    .....
}

合约2的代码摘抄如下,合约2的代码就是合约1代码的上半部分
合约2

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  ....
}

library SafeMath {
  ....
}

contract ERC20Basic {
  ....
}

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
contract ERC20 is ERC20Basic {
  ....
}

contract BasicToken is ERC20Basic {
  ....
}

/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * @dev https://github.com/ethereum/EIPs/issues/20
 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is ERC20, BasicToken {
    ....
}

contract MintableToken is StandardToken, Ownable {
  ....
}

/**
 * @title SetherToken
 * @dev Sether ERC20 Token that can be minted.
 * It is meant to be used in sether crowdsale contract.
 */
contract SetherToken is MintableToken {
    ....
}

你可能感兴趣的:(以太坊事件log分析)