以太坊事件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 <https://etherscan.io/tx/0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4>
            - 合约1的code <https://etherscan.io/address/0x20bf6672497941bd3e4ec5fd551de5c31e0a898a#code>
            - 合约2的code <https://etherscan.io/address/0x78b039921e84e726eb72e7b1212bb35504c645ca#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<SetherToken>

/**
 * @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 {
    ....
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容