您正在查看: Ethereum-新手教程 分类下的文章

以太坊交易中的Txn Type由来

测试交易

>>> web3.eth.send_transaction({
  'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601',
  'from': web3.eth.coinbase,
  'value': 12345,
  'gas': 21000,
  'maxFeePerGas': web3.toWei(250, 'gwei'),
  'maxPriorityFeePerGas': web3.toWei(2, 'gwei'),
})

测试查询

https://goerli.etherscan.io/tx/0x4a14aa0f0b18a8c7aa5677fdd057ffdb77791497d849da164ad42574ce778e5f
显示为

Txn Type: 2 (EIP-1559)

查看交易体

curl https://goerli.infura.io/v3/87aba2e668f9410498cdf74bc7a35467 -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":["0x4a14aa0f0b18a8c7aa5677fdd057ffdb77791497d849da164ad42574ce778e5f"],"id":1}' | jq

返回

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "accessList": [],
        "blockHash": "0x5114398c54853f6ad6df7002ff88fad8f95c4ea74a32048275e72795e9381050",
        "blockNumber": "0x51a84c",
        "chainId": "0x5",
        "from": "0x10210572d6b4924af7ef946136295e9b209e1fa0",
        "gas": "0x5208",
        "gasPrice": "0xad731380",
        "hash": "0x4a14aa0f0b18a8c7aa5677fdd057ffdb77791497d849da164ad42574ce778e5f",
        "input": "0x",
        "maxFeePerGas": "0xad731384",
        "maxPriorityFeePerGas": "0xad731379",
        "nonce": "0xf9",
        "r": "0x86e133483aff5415fffc73b30b9c781438655362fe070160d1baf5c6dd4a7b43",
        "s": "0x1241e3560cb2e2354168adfe25f3a6c361694b422ff92bc9eebc124403be846e",
        "to": "0x48f155527f25eb1d4cb2aa32b7e84692aa0025c0",
        "transactionIndex": "0xb",
        "type": "0x2",
        "v": "0x1",
        "value": "0x8ac7230489e80000"
    }
}

链上交易体中存在type字段

跟踪type加入时机

web3.py sdk举例

https://github.com/ethereum/web3.py/blob/810b5904466896143315d68352cdc1add93fea1a/web3/_utils/transactions.py#L42
https://github.com/ethereum/web3.py/blob/810b5904466896143315d68352cdc1add93fea1a/web3/_utils/transactions.py#L108

def fill_transaction_defaults(web3: "Web3", transaction: TxParams) -> TxParams:
    """
    if web3 is None, fill as much as possible while offline
    """
    defaults = {}
    for key, default_getter in TRANSACTION_DEFAULTS.items():
        if key not in transaction:
            if key == 'gasPrice' and any(_ in transaction for _ in (
                'maxFeePerGas', 'maxPriorityFeePerGas'
            )):  # if EIP-1559 params in transaction, do not set a default gasPrice if missing
                continue

            if callable(default_getter):
                if web3 is not None:
                    default_val = default_getter(web3, transaction)
                else:
                    raise ValueError("You must specify %s in the transaction" % key)

            else:
                default_val = default_getter
            defaults[key] = default_val

    if 'type' not in transaction and any(_ in transaction for _ in (
        'maxFeePerGas', 'maxPriorityFeePerGas'
    )):
        # default transaction type to '2' if 1559 transaction params are present
        defaults['type'] = '0x2'

    return merge(defaults, transaction)

可以看到当参数中包含maxFeePerGas, maxPriorityFeePerGas任一字段时,则认为是['type'] = '0x2'

结论

交易中的type字段由相应的sdk根据参数规则,自动填充

使用OpenZeppelin编写可升级合约

为了方便社区新手入门,本文尽可能的详细

准备合约编译环境

测试系统:WSL ubuntu 20.04
node: v10.19.0
npm: 6.14.4

创建环境工程

mkdir myContract
cd myContract
sudo npm i -g truffle
truffle init

此时目录下会生成truffle工程相关文件

contracts  migrations  test  truffle-config.js

编写测试合约

需要部署三个合约,分别是

  1. Parms(逻辑合约)
  2. ProxyAdmin(管理合约)
  3. TransparentUpgradeProxy(代理合约,DAPP直接交互的合约地址)

Params合约代码

myContract/contract/Parms.sol文件,并输入

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract Params is Initializable,OwnableUpgradeable {
    function initialize()public initializer{
        __Context_init_unchained();
        __Ownable_init_unchained();
    }
    mapping(string => uint256) private uint256Params;

    event Uint256ParamSetted(string indexed _key,uint256 _value);

    function SetUint256Param(string memory _key,uint256 _value) external onlyOwner{
        uint256Params[_key] = _value;
        emit Uint256ParamSetted(_key,_value);
    }


    function GetUint256Param(string memory _key)public view returns(uint256){
        return uint256Params[_key];
    }
}

ProxyAdmin合约代码

myContract/contract/ProxyAdmin.sol文件,并输入

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
 * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
 */
contract ProxyAdmin is Ownable {
    /**
     * @dev Returns the current implementation of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("implementation()")) == 0x5c60da1b
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Returns the current admin of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("admin()")) == 0xf851a440
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Changes the admin of `proxy` to `newAdmin`.
     *
     * Requirements:
     *
     * - This contract must be the current admin of `proxy`.
     */
    function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
        proxy.changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
        proxy.upgradeTo(implementation);
    }

    /**
     * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
     * {TransparentUpgradeableProxy-upgradeToAndCall}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgradeAndCall(
        TransparentUpgradeableProxy proxy,
        address implementation,
        bytes memory data
    ) public payable virtual onlyOwner {
        proxy.upgradeToAndCall{value: msg.value}(implementation, data);
    }
}

TransparentUpgradeableProxy 代理合约代码

myContract/contract/TransparentUpgradeProxy.sol文件,并输入

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is ERC1967Proxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
     */
    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable ERC1967Proxy(_logic, _data) {
        assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _changeAdmin(admin_);
    }

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     */
    modifier ifAdmin() {
        if (msg.sender == _getAdmin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function admin() external ifAdmin returns (address admin_) {
        admin_ = _getAdmin();
    }

    /**
     * @dev Returns the current implementation.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function implementation() external ifAdmin returns (address implementation_) {
        implementation_ = _implementation();
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
     */
    function changeAdmin(address newAdmin) external virtual ifAdmin {
        _changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
     */
    function upgradeTo(address newImplementation) external ifAdmin {
        _upgradeToAndCall(newImplementation, bytes(""), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
        _upgradeToAndCall(newImplementation, data, true);
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view virtual returns (address) {
        return _getAdmin();
    }

    /**
     * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
     */
    function _beforeFallback() internal virtual override {
        require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
        super._beforeFallback();
    }
}

修改truffle solc版本为0.8.0

打开truffle-config.js,修改solc版本

compilers: {
    solc: {
      version: "^0.8.0"
    ...

编译合约

首次需要安装依赖

npm i --save @openzeppelin/contracts-upgradeable
npm i --save @openzeppelin/contracts

编译合约

truffle complite

部署合约

等待完成后,会在build\contracts找到对应的编译后的合约
如何使用编译后的文件进行合约部署,参考《MyEtherWallet使用》

Params合约部署完的合约地址:0x808189EB5932Cfe710A849752CDb54F5fb1b85DA
ProxyAdmin合约部署完的合约地址:0x9b05f1F378B52011215464f4Ad4666BC80B7bcA5

部署TransparentUpgradeableProxy时,
LOGIC:逻辑合约地址,这里为 0x808189EB5932Cfe710A849752CDb54F5fb1b85DA
ADMIN
:管理合约地址,这里为 0x9b05f1F378B52011215464f4Ad4666BC80B7bcA5
_DATA:逻辑合约初始化方法调用数据,这里为0x8129fc1c(只调用initialize方法,initialize方法没有入参,如果有参数也是支持的)

0x8129fc1c由以下获得

web3js.eth.abi.encodeFunctionCall({
    name: 'initialize',
    type: 'function',
    inputs: []
}, []);
>0x8129fc1c

部署后的地址是:0x95e19C3609DE02291840CE9093c75e233cF2Cf08

此时用MyEtherWallet执行合约地址:0x95e19C3609DE02291840CE9093c75e233cF2Cf08,使用Params合约的abi执行
调用SetUint256Param,设置_key=K,_VALUE=1,并通过GetUint256Param进行验证
此时完成了基本的通过TransparentUpgradeableProxy代理合约调用Params逻辑合约的过程。

下面开始演示通过ProxyAdmin合约将Params逻辑合约升级

升级Params逻辑合约

修改Params合约,并部署

function GetUint256Param(string memory _key)public view returns(uint256){
    uint256 v = uint256Params[_key];
    return v+1;
}

新部署后的地址为0xBf72f7C1e39B76D7A5b702E16251Fdd132Cd6618

调用ProxyAdmin进行升级

ProxyAdmin提供两个方法进行升级

  1. upgrade,需要传入proxy地址,新的逻辑实现地址
  2. upgradeAndCall,需要传入roxy地址,新的逻辑实现地址,初始化调用数据

本例中,由于数据是保存在代理合约中,这份数据已经初始化过了,不需要再初始化,所以调用upgrade方法即可,参数如下:

  • proxy: 0x95e19C3609DE02291840CE9093c75e233cF2Cf08
  • implementation: 0xBf72f7C1e39B76D7A5b702E16251Fdd132Cd6618

至此,合约升级完毕。调用GetUint256Param方法进行验证,获得_key=K的value为2,合约升级成功。

参考

https://docs.openzeppelin.com/contracts/3.x/api/proxy
https://www.cnblogs.com/cqvoip/p/15033402.html
https://docs.openzeppelin.com/upgrades-plugins/1.x/

https://medium.com/@kenschiller/making-ethereum-smart-contracts-upgradable-aa16a4256d32

eth.pendingTransactions 不返回任何挂起的交易

在测试eth.pendingTransactions时,无法获取到数据,查看代码(Github Code)

// PendingTransactions returns the transactions that are in the transaction pool
// and have a from address that is one of the accounts this node manages.
func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) {
    pending, err := s.b.GetPoolTransactions()
    if err != nil {
        return nil, err
    }
    accounts := make(map[common.Address]struct{})
    for _, wallet := range s.b.AccountManager().Wallets() {
        for _, account := range wallet.Accounts() {
            accounts[account.Address] = struct{}{}
        }
    }
    curHeader := s.b.CurrentHeader()
    transactions := make([]*RPCTransaction, 0, len(pending))
    for _, tx := range pending {
        from, _ := types.Sender(s.signer, tx)
        if _, exists := accounts[from]; exists {
            transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()))
        }
    }
    return transactions, nil
}

原因

由于查询eth.pendingTransactions性能消耗较大,所以代码加了一层限制,必须查询当前交易的签名者地址加到当前节点地址中

./geth attach ipc:./data/geth.ipc
personal.importRawKey("e9bc9ae610535。。。。f4f49c025cc","passwrod..")

注意

由于当前私钥可能被外网猜测,所以当前查询节点不能对外提供RPC服务,只能内网被业务服务调用。并做好节点访问权限安全防范

参考

https://github.com/ethereum/go-ethereum/issues/21138