假设有个合约ERC20充值方法，方法接收msg.sender发送的ERC20代币，并增加合约中的余额统计 import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; ... function rechargeMain(address mainChainErc20Addr, uint256 amount) payable external notContract returns (bool){ ... require(amount > 0, "The recharge amount is too small"); require(IERC20(mainChainErc20Addr).balanceOf(msg.sender) >= amount, "Insufficient contract account balance"); IERC20(mainChainErc20Addr).transfer(address(this), amount); ... return true; } 执行后提示 execution reverted: ERC20: transfer amount exceeds balance 经测试（Github Code） function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } _msgSender()的地址为合约地址，测试交易（跳转地址） 所以我们需要使用transferFrom，代码修改为 import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; ... function rechargeMain(address mainChainErc20Addr, uint256 amount) payable external notContract returns (bool){ ... require(amount > 0, "The recharge amount is too small"); require(IERC20(mainChainErc20Addr).balanceOf(msg.sender) >= amount, "Insufficient contract account balance"); IERC20(mainChainErc20Addr).transferFrom(msg.sender, address(this), amount); ... return true; } 执行完，提示 execution reverted: ERC20: transfer amount exceeds allowance 查看代码（Github code） function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, _msgSender(), currentAllowance - amount); } return true; } 需要先批准交易金额（津贴）（Github Code） function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } 查看当前剩余允许值，（Github Code） function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } 也可以执行increaseAllowance或decreaseAllowance增量增减调整 对于此次需求，执行rechargeMain前，需要先执行津贴充值 increaseAllowance(address spender, uint256 addedValue) 执行increaseAllowance的地址为当前充值转出地址 spender为当前转入合约地址 addedValue为当前充值金额 测试交易：https://ropsten.etherscan.io/tx/0x0f65cb2a7ca2bf0d6b1fdd65e943afb5c6305573dc3c3a960c2f86f71757c897
推荐 ：https://wizard.openzeppelin.com/ basic erc20 Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ ␊ contract MyToken is ERC20 {␊ constructor() ERC20("MyToken", "MTK") {}␊ }␊ ` erc20 with snapshots Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";␊ import "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ contract MyToken is ERC20, ERC20Snapshot, Ownable {␊ constructor() ERC20("MyToken", "MTK") {}␊ ␊ function snapshot() public onlyOwner {␊ _snapshot();␊ }␊ ␊ // The following functions are overrides required by Solidity.␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ override(ERC20, ERC20Snapshot)␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ }␊ ` erc20 burnable Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";␊ ␊ contract MyToken is ERC20, ERC20Burnable {␊ constructor() ERC20("MyToken", "MTK") {}␊ }␊ ` erc20 burnable with snapshots Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";␊ import "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ contract MyToken is ERC20, ERC20Burnable, ERC20Snapshot, Ownable {␊ constructor() ERC20("MyToken", "MTK") {}␊ ␊ function snapshot() public onlyOwner {␊ _snapshot();␊ }␊ ␊ // The following functions are overrides required by Solidity.␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ override(ERC20, ERC20Snapshot)␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ }␊ ` erc20 pausable Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/security/Pausable.sol";␊ import "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ contract MyToken is ERC20, Pausable, Ownable {␊ constructor() ERC20("MyToken", "MTK") {}␊ ␊ function pause() public onlyOwner {␊ _pause();␊ }␊ ␊ function unpause() public onlyOwner {␊ _unpause();␊ }␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ whenNotPaused␊ override␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ }␊ ` erc20 pausable with roles Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/security/Pausable.sol";␊ import "@openzeppelin/contracts/access/AccessControl.sol";␊ ␊ contract MyToken is ERC20, Pausable, AccessControl {␊ bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊ ␊ constructor() ERC20("MyToken", "MTK") {␊ _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);␊ _setupRole(PAUSER_ROLE, msg.sender);␊ }␊ ␊ function pause() public onlyRole(PAUSER_ROLE) {␊ _pause();␊ }␊ ␊ function unpause() public onlyRole(PAUSER_ROLE) {␊ _unpause();␊ }␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ whenNotPaused␊ override␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ }␊ ` erc20 burnable pausable Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";␊ import "@openzeppelin/contracts/security/Pausable.sol";␊ import "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ contract MyToken is ERC20, ERC20Burnable, Pausable, Ownable {␊ constructor() ERC20("MyToken", "MTK") {}␊ ␊ function pause() public onlyOwner {␊ _pause();␊ }␊ ␊ function unpause() public onlyOwner {␊ _unpause();␊ }␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ whenNotPaused␊ override␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ }␊ ` erc20 burnable pausable with snapshots Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";␊ import "@openzeppelin/contracts/access/Ownable.sol";␊ import "@openzeppelin/contracts/security/Pausable.sol";␊ ␊ contract MyToken is ERC20, ERC20Burnable, ERC20Snapshot, Ownable, Pausable {␊ constructor() ERC20("MyToken", "MTK") {}␊ ␊ function snapshot() public onlyOwner {␊ _snapshot();␊ }␊ ␊ function pause() public onlyOwner {␊ _pause();␊ }␊ ␊ function unpause() public onlyOwner {␊ _unpause();␊ }␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ whenNotPaused␊ override(ERC20, ERC20Snapshot)␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ }␊ ` erc20 preminted Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ ␊ contract MyToken is ERC20 {␊ constructor() ERC20("MyToken", "MTK") {␊ _mint(msg.sender, 1000 * 10 ** decimals());␊ }␊ }␊ ` erc20 premint of 0 Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ ␊ contract MyToken is ERC20 {␊ constructor() ERC20("MyToken", "MTK") {}␊ }␊ ` erc20 mintable Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ contract MyToken is ERC20, Ownable {␊ constructor() ERC20("MyToken", "MTK") {}␊ ␊ function mint(address to, uint256 amount) public onlyOwner {␊ _mint(to, amount);␊ }␊ }␊ ` erc20 mintable with roles Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/access/AccessControl.sol";␊ ␊ contract MyToken is ERC20, AccessControl {␊ bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");␊ ␊ constructor() ERC20("MyToken", "MTK") {␊ _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);␊ _setupRole(MINTER_ROLE, msg.sender);␊ }␊ ␊ function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {␊ _mint(to, amount);␊ }␊ }␊ ` erc20 permit Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";␊ ␊ contract MyToken is ERC20, ERC20Permit {␊ constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}␊ }␊ ` erc20 votes Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";␊ ␊ contract MyToken is ERC20, ERC20Permit, ERC20Votes {␊ constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}␊ ␊ // The following functions are overrides required by Solidity.␊ ␊ function _afterTokenTransfer(address from, address to, uint256 amount)␊ internal␊ override(ERC20, ERC20Votes)␊ {␊ super._afterTokenTransfer(from, to, amount);␊ }␊ ␊ function _mint(address to, uint256 amount)␊ internal␊ override(ERC20, ERC20Votes)␊ {␊ super._mint(to, amount);␊ }␊ ␊ function _burn(address account, uint256 amount)␊ internal␊ override(ERC20, ERC20Votes)␊ {␊ super._burn(account, amount);␊ }␊ }␊ ` erc20 flashmint Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol";␊ ␊ contract MyToken is ERC20, ERC20FlashMint {␊ constructor() ERC20("MyToken", "MTK") {}␊ }␊ ` erc20 full upgradeable transparent Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20SnapshotUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ ␊ contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20SnapshotUpgradeable, AccessControlUpgradeable, PausableUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable {␊ bytes32 public constant SNAPSHOT_ROLE = keccak256("SNAPSHOT_ROLE");␊ bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊ bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");␊ ␊ function initialize() initializer public {␊ __ERC20_init("MyToken", "MTK");␊ __ERC20Burnable_init();␊ __ERC20Snapshot_init();␊ __AccessControl_init();␊ __Pausable_init();␊ __ERC20Permit_init("MyToken");␊ __ERC20FlashMint_init();␊ ␊ _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);␊ _setupRole(SNAPSHOT_ROLE, msg.sender);␊ _setupRole(PAUSER_ROLE, msg.sender);␊ _mint(msg.sender, 2000 * 10 ** decimals());␊ _setupRole(MINTER_ROLE, msg.sender);␊ }␊ ␊ function snapshot() public onlyRole(SNAPSHOT_ROLE) {␊ _snapshot();␊ }␊ ␊ function pause() public onlyRole(PAUSER_ROLE) {␊ _pause();␊ }␊ ␊ function unpause() public onlyRole(PAUSER_ROLE) {␊ _unpause();␊ }␊ ␊ function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {␊ _mint(to, amount);␊ }␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ whenNotPaused␊ override(ERC20Upgradeable, ERC20SnapshotUpgradeable)␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ ␊ // The following functions are overrides required by Solidity.␊ ␊ function _afterTokenTransfer(address from, address to, uint256 amount)␊ internal␊ override(ERC20Upgradeable, ERC20VotesUpgradeable)␊ {␊ super._afterTokenTransfer(from, to, amount);␊ }␊ ␊ function _mint(address to, uint256 amount)␊ internal␊ override(ERC20Upgradeable, ERC20VotesUpgradeable)␊ {␊ super._mint(to, amount);␊ }␊ ␊ function _burn(address account, uint256 amount)␊ internal␊ override(ERC20Upgradeable, ERC20VotesUpgradeable)␊ {␊ super._burn(account, amount);␊ }␊ }␊ ` erc20 full upgradeable uups Snapshot 1 `// SPDX-License-Identifier: MIT␊ pragma solidity ^0.8.2;␊ ␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20SnapshotUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊ ␊ contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20SnapshotUpgradeable, AccessControlUpgradeable, PausableUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊ bytes32 public constant SNAPSHOT_ROLE = keccak256("SNAPSHOT_ROLE");␊ bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊ bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");␊ bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");␊ ␊ function initialize() initializer public {␊ __ERC20_init("MyToken", "MTK");␊ __ERC20Burnable_init();␊ __ERC20Snapshot_init();␊ __AccessControl_init();␊ __Pausable_init();␊ __ERC20Permit_init("MyToken");␊ __ERC20FlashMint_init();␊ __UUPSUpgradeable_init();␊ ␊ _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);␊ _setupRole(SNAPSHOT_ROLE, msg.sender);␊ _setupRole(PAUSER_ROLE, msg.sender);␊ _mint(msg.sender, 2000 * 10 ** decimals());␊ _setupRole(MINTER_ROLE, msg.sender);␊ _setupRole(UPGRADER_ROLE, msg.sender);␊ }␊ ␊ function snapshot() public onlyRole(SNAPSHOT_ROLE) {␊ _snapshot();␊ }␊ ␊ function pause() public onlyRole(PAUSER_ROLE) {␊ _pause();␊ }␊ ␊ function unpause() public onlyRole(PAUSER_ROLE) {␊ _unpause();␊ }␊ ␊ function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {␊ _mint(to, amount);␊ }␊ ␊ function _beforeTokenTransfer(address from, address to, uint256 amount)␊ internal␊ whenNotPaused␊ override(ERC20Upgradeable, ERC20SnapshotUpgradeable)␊ {␊ super._beforeTokenTransfer(from, to, amount);␊ }␊ ␊ function _authorizeUpgrade(address newImplementation)␊ internal␊ onlyRole(UPGRADER_ROLE)␊ override␊ {}␊ ␊ // The following functions are overrides required by Solidity.␊ ␊ function _afterTokenTransfer(address from, address to, uint256 amount)␊ internal␊ override(ERC20Upgradeable, ERC20VotesUpgradeable)␊ {␊ super._afterTokenTransfer(from, to, amount);␊ }␊ ␊ function _mint(address to, uint256 amount)␊ internal␊ override(ERC20Upgradeable, ERC20VotesUpgradeable)␊ {␊ super._mint(to, amount);␊ }␊ ␊ function _burn(address account, uint256 amount)␊ internal␊ override(ERC20Upgradeable, ERC20VotesUpgradeable)␊ {␊ super._burn(account, amount);␊ }␊ }␊ ` 转载自：https://github.com/OpenZeppelin/contracts-wizard/blob/0dbf5d669adcdb8e801fbb67a269fc7931613d66/packages/core/src/erc20.test.ts.md
pip3 install solc-select solc-select install 版本
测试交易 >>> 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根据参数规则，自动填充
在之前《隐私/匿名币》中说过Zcash 首次运用 zk-SNARKs 零知识证明技术验证交易有效性，原来的计划是先把基于以太坊的侧链开发完成，再做ZKP（Zero-Knowledge Proofs）的技术跟进，以解决匿名交易以及增强TPS扩容方案（交易指纹上链，交易数据链下存储）。 今天先做一些技术调研和储备，方便后续技术评估时的一些技术方向的把控 技术相关 《什么是 zk-SNARK？》 《零知识证明：STARKs 与 SNARKs》 《zk-SNARKs 介绍（零知识证明概述以及如何将 zk-SNARK 集成到以太坊中）》 https://zokrates.github.io/ https://docs.ethhub.io/ethereum-roadmap/privacy/ https://starkware.co/ https://docs.ethhub.io/built-on-ethereum/infrastructure/aztec-protocol/ https://scrt.network/blog/introducing-secret-network https://ethresear.ch/t/zether-the-first-privacy-mechanism-designed-for-ethereum/5029 https://ethereum.stackexchange.com/questions/49781/can-zksnarks-be-implemented-on-the-ethereum-ecosystem https://github.com/starkware-libs/ https://hackmd.io/@zkteam/gnark https://docs.gnark.consensys.net/en/latest/ 开源代码 https://github.com/zkcrypto/bellman/ https://github.com/matter-labs/bellman https://github.com/zcash/librustzcash (Groth16) https://github.com/zcash/mpc https://github.com/scipr-lab/libsnark https://github.com/Zokrates/ZoKrates https://github.com/AztecProtocol https://github.com/arnaucube/go-snark-study https://github.com/vocdoni/go-snark https://github.com/ConsenSys/gnark (Groth16, PlonK) https://github.com/ConsenSys/gnark-crypto https://github.com/nikkolasg/playsnark （Groth16, PHGR13） https://github.com/ConsenSys/quorum
zk-SNARKs 介绍（零知识证明概述以及如何将 zk-SNARK 集成到以太坊中）
在这篇文章中，我们旨在从实用的角度概述 zk-SNARK。我们会将实际的数学视为一个黑匣子，并尝试围绕如何使用它们建立一些直觉。我们还将给出最近在以太坊中集成 zk-SNARKs 的工作的简单应用。 零知识证明 零知识证明的目标是让验证者能够说服自己，证明者拥有秘密参数的知识，称为见证，满足某种关系，而不会将见证透露给验证者或其他任何人。 我们可以更具体地将其理解为有一个程序，表示为C，接受两个输入：C(x, w)。输入x是公开输入，w是秘密见证输入。该程序的输出是布尔值，即，或者true或false。然后给目标一个特定的公共输入x，证明证明者知道一个秘密输入w，使得C(x,w) == true。 我们将专门讨论非交互式零知识证明。这意味着证明本身是一组数据，无需证明者的任何交互即可对其进行验证。 示例程序 假设 Bob 得到了H某个值的散列，他希望有一个证明 Alice 知道s散列到的值H。通常 Alice 会通过给sBob来证明这一点，之后 Bob 会计算哈希并检查它是否等于H。 但是，假设 Alice 不想向sBob透露该值，而只是想证明她知道该值。她可以为此使用 zk-SNARK。 我们可以使用以下程序来描述 Alice 的场景，这里编写为一个 Javascript 函数： function C(x, w) { return ( sha256(w) == x );} 换句话说：程序接受一个公共散列x和一个秘密值w，true如果w等于SHA-256 散列，则返回x。 使用函数翻译 Alice 的问题，C(x,w)我们看到 Alice 需要创建一个证明，证明她拥有s这样的C(H, s) == true，而不必透露s。这是 zk-SNARKs 解决的一般问题。 zk-SNARK 的定义 甲 ZK-SNARK 包括三个算法 G, P, V 定义如下： 该 密钥生成器 G 需要一个秘密参数 lambda 和程序 C，并产生两个公开可用的按键， 证明键 pk，和一个 验证密钥 vk。这些密钥是公共参数，只需为给定程序生成一次 C。 该 证明 P 作为输入的证明键 pk，公共投入 x 和私人见证 w。该算法生成 证明 prf = P(pk, x, w) 者知道证人 w 并且证人满足程序的证明。 该 验证 V 单位计算 V(vk, x, prf) 其回报率 true ，如果证明是正确的， false 否则。因此，如果证明者知道w 满足 的证人，则此函数返回真 C(x,w) == true。 这里注意lambda 生成器中使用的秘密参数 。该参数有时会使在实际应用中使用 zk-SNARK 变得棘手。这样做的原因是任何知道这个参数的人都可以生成假证明。具体来说，给定任何程序 C 和公共输入， x 一个知道的人 lambda 可以生成一个证明 fake_prf ，以便 V(vk, x, fake_prf) 在true 不知道秘密的情况下 评估为 w。 因此，实际运行生成器需要一个非常安全的过程，以确保没有人知道并将参数保存在任何地方。这就是 Zcash 团队为了生成证明密钥和验证密钥而进行极其复杂的仪式的原因 ，同时确保“有毒废物”参数 lambda 在此过程中被销毁。 我们示例程序的 zk-SNARK Alice 和 Bob 在实践中如何使用 zk-SNARK 来让 Alice 证明她知道上面例子中的秘密值？ 首先，如上所述，我们将使用由以下函数定义的程序： function C(x, w) { return ( sha256(w) == x ); } 第一步是让 Bob 运行生成器 G 以创建证明密钥 pk 和验证密钥 vk。首先，随机生成 lambda 并将其用作输入： (pk, vk) = G(C, lambda) 小心处理参数 lambda，因为如果 Alice 了解 lambda 的值，她将能够创建假证明。Bob 将与 Alice 共享 pk 和 vk。 Alice 现在将扮演证明者的角色。她需要证明她知道哈希到已知哈希 H 的值 s。 她使用输入 pk、H 和 s 运行证明算法 P 以生成证明 prf： prf = P(pk, H, s) 接下来 Alice 向 Bob 提供证明 prf，Bob 运行验证函数 V(vk, H, prf)，在这种情况下会返回 true，因为 Alice 正确地知道秘密 s。Bob 可以确信 Alice 知道这个秘密，但 Alice 不需要向 Bob 透露秘密。 可重复使用的证明和验证密钥 在我们上面的例子中，如果 Bob 想向 Alice 证明他知道一个秘密，则不能使用 zk-SNARK，因为 Alice 无法知道 Bob 没有保存 lambda 参数。鲍勃可能能够伪造证明。 如果一个程序对很多人有用（比如 Zcash 的例子），一个独立于 Alice 和 Bob 的可信独立组可以运行生成器并创建证明密钥 pk 和验证密钥 vk，这样就不会有人了解 lambda。 任何相信该团体没有作弊的人都可以使用这些密钥进行未来的互动。 以太坊中的 zk-SNARK 开发人员已经开始将 zk-SNARKs 集成到以太坊中。这看起来像什么？具体来说，您可以将验证算法的构建块以预编译合约的形式添加到以太坊中。方法如下：在链下运行生成器以生成证明密钥和验证密钥。然后，任何证明者都可以使用证明密钥来创建证明，也是链下的。然后，您可以在智能合约中运行通用验证算法，使用证明、验证密钥和公共输入作为输入参数。然后，您可以使用验证算法的结果来触发其他链上活动。 示例：保密交易 这是一个简单的例子，说明 zk-SNARKs 如何帮助保护以太坊的隐私。假设我们有一个简单的代币合约。通常，代币合约的核心是从地址到余额的映射： mapping (address => uint256) balances; 我们将保留相同的基本核心，除了用余额的散列替换余额： mapping (address => bytes32) balanceHashes; 我们不会隐藏交易的发送者或接收者。但我们会隐藏余额和发送金额。此属性有时称为机密交易。 我们将使用两个 zk-SNARK 将令牌从一个帐户发送到另一个帐户。一份证明由发送方创建，一份由接收方创建。 通常在一个代币合约中，为了使一笔大小值的交易有效，我们需要验证以下内容： balances[fromAddress] >= value 我们的 zk-SNARK 需要证明这是成立的，并且更新后的哈希值与更新后的余额相匹配。 主要思想是发送者将使用他们的起始余额和交易价值作为私人输入。作为公共输入，他们使用起始余额、期末余额和价值的哈希值。同样，接收者将使用起始余额和价值作为秘密输入。作为公共输入，他们使用起始余额、期末余额和价值的哈希值。 下面是我们将用于发送者 zk-SNARK 的程序，其中 x 代表公共输入，w 代表私人输入。 function senderFunction(x, w) { return ( w.senderBalanceBefore > w.value && sha256(w.value) == x.hashValue && sha256(w.senderBalanceBefore) == x.hashSenderBalanceBefore && sha256(w.senderBalanceBefore - w.value) == x.hashSenderBalanceAfter ) } 接收器使用的程序如下： function receiverFunction(x, w) { return ( sha256(w.value) == x.hashValue && sha256(w.receiverBalanceBefore) == x.hashReceiverBalanceBefore && sha256(w.receiverBalanceBefore + w.value) == x.hashReceiverBalanceAfter ) } 程序检查发送余额是否大于发送的值，并检查所有散列是否匹配。一组受信任的人将为我们的 zk-SNARK 生成证明和验证密钥。我们称它们为 confTxSenderPk、confTxSenderVk、confTxReceiverPk 和 confTxReceiverVk。 在代币合约中使用 zk-SNARKs 看起来像这样： function transfer(address _to, bytes32 hashValue, bytes32 hashSenderBalanceAfter, bytes32 hashReceiverBalanceAfter, bytes zkProofSender, bytes zkProofReceiver) { bytes32 hashSenderBalanceBefore = balanceHashes[msg.sender]; bytes32 hashReceiverBalanceBefore = balanceHashes[_to]; bool senderProofIsCorrect = zksnarkverify(confTxSenderVk, [hashSenderBalanceBefore, hashSenderBalanceAfter, hashValue], zkProofSender); bool receiverProofIsCorrect = zksnarkverify(confTxReceiverVk, [hashReceiverBalanceBefore, hashReceiverBalanceAfter, hashValue], zkProofReceiver); if(senderProofIsCorrect && receiverProofIsCorrect) { balanceHashes[msg.sender] = hashSenderBalanceAfter; balanceHashes[_to] = hashReceiverBalanceAfter; } } 因此，区块链上唯一的更新是余额的哈希值，而不是余额本身。但是，我们可以知道所有余额都已正确更新，因为我们可以自行检查证明是否已得到验证。 细节 上述保密交易方案主要是为了说明如何在以太坊上使用 zk-SNARKs 的一个实际例子。为了创建一个强大的机密交易方案，我们需要解决一些问题： 用户需要在客户端跟踪他们的余额，如果您失去余额，这些代币将无法恢复。余额也许可以使用从签名密钥派生的密钥加密存储在链上。 余额需要使用 32 字节的数据并编码熵，以防止反向散列计算余额的能力。 需要处理发送到未使用地址的边缘情况。 发送方需要与接收方交互才能发送。一个人可能有一个系统，发送者使用他们的证据来发起交易。然后接收方可以在区块链上看到他们有一个“待处理的传入交易”并可以完成它。 原文：https://consensys.net/blog/developers/introduction-to-zk-snarks/
新技术之间的冲突 纵观历史，总是有类似的技术大约在同一时间进入市场，寻求类似的结果，但以不同的方式解决问题。当这种市场现象发生时，采用者应该尝试客观地评估每一项技术。 由于 STARK 阵营和 SNARK 阵营都对各自的技术充满热情，我们认为对这两种技术进行客观比较会很有趣。 STARKs 与 SNARKs 快速复习一下，零知识证明技术使一方能够向另一方证明他们知道某些事情，而证明者不必自己传达信息来证明他们的知识。它们既是一种隐私增强技术，因为它们减少了用户之间需要提供的信息量，又是一种扩展技术，因为它们可以允许以更快的速度验证证明，因为它们不包含全部信息非私有系统的信息。 当今市场上最引人注目的两种零知识技术是 zk-STARKs 和 zk-SNARKs。两者都是双方证明知识的方法的首字母缩写词：zk-STARK代表零知识可扩展的透明知识论证，zk-SNARK代表零知识简洁非交互式知识论证。本文将深入探讨从文化和技术的角度分析这两种不同的零知识技术之间的核心差异。此外，这两种零知识技术本质上都是非交互式的，这意味着代码可以部署并自主运行。 下面，我们有几个表格描述了两种技术之间的一些高级差异。我们还将深入研究段落格式的差异。 资料来源：物质实验室 SNARK 2012 年 1 月，加州大学伯克利分校一位名叫 Alessandro Chiesa 的教授与人合着了一篇论文，为他们首次构建的零知识证明创造了术语 zk-SNARK。Zk-SNARK 在其基础上依赖于椭圆曲线来保证安全性。密码学中的椭圆曲线在基本假设下运行，即找到相对于公知基点的随机椭圆曲线元素的离散对数是不可行的。 虽然关于椭圆曲线随机数生成器是否存在后门一直存在很大争议，但整个算法通常仍然是安全的。尽管侧信道攻击中有几个流行的漏洞，但可以通过多种技术轻松缓解。量子攻击确实笼罩在基于椭圆曲线的密码学上，但打破其安全模型所需的量子计算尚未广泛应用。 除了基于椭圆曲线之外，zk-SNARK 还需要可信设置。可信设置是指密钥的初始创建事件，用于创建私人交易所需的证明以及这些证明的验证。最初，当这些密钥被创建时，在验证密钥和发送私人交易的密钥之间有一个隐藏的参数链接。如果用于在可信设置事件中创建这些密钥的秘密没有被破坏，则可以利用这些秘密通过虚假验证来伪造交易，从而使持有者能够执行诸如凭空创建新令牌并使用它们等操作用于交易。由于 zk-SNARKs 的隐私特性，将无法验证凭空创建的代币实际上是凭空创造。话虽如此，信任设置仅在最初需要 因此，基于 SNARK 的网络的用户必须依赖于正确执行可信设置的事实，这意味着与可信设置密钥相关的秘密已被销毁，并且不会由监督仪式的个人持有。对可信设置的依赖一直是 SNARK 批评者最关注的领域之一。话虽如此，开发人员只需要最初使用可信设置，而不是持续使用。 SNARK 的另一个重要批评领域是它们不具有量子抗性。一旦量子计算在很大程度上可用，SNARKs 背后的隐私技术将被打破。当然，SNARKs 的支持者正确地指出，当使用量子计算机时，我们将面临更多问题，例如破坏 RSA 和大多数钱包基础设施。 话虽如此，尽管存在与可信设置相关的问题，但实际上 SNARK 的采用速度比 STARK 快得多的原因有很多。SNARKs 比 STARKs 早几年被发现，这使该技术在采用方面取得了重要的领先优势。Zcash 是较早的数字资产项目之一，它在区块链开发社区中普及了 SNARK 的使用。由于 Zcash 和其他 SNARKs 的采用者，SNARKs 拥有最多的开发人员库、已发布的代码、项目和积极致力于该技术的开发人员。除了 Zcash，新兴的 DEX Loopring 也使用了 SNARK。如果开发人员想开始使用零知识技术，他们在使用 SNARK 方面将比 STARK 获得更多支持。 此外，据估计 SNARK仅需要 STARK 所需气体的 24%，这意味着与 SNARK 进行交易对最终用户来说要便宜得多。最后，SNARKs 的证明大小比 STARKs 小得多，这意味着它需要更少的链上存储。 STARKs 虽然 SNARKs 在文档和开发人员支持方面比 STARKs 有一些明显的优势，但 STARKs 确实提供了一些独特的好处。但首先，让我们从技术角度深入了解 STARK 是什么。 Eli Ben-Sasson、Iddo Bentov、Yinon Horeshy 和 Michael Riabzev 在2018 年撰写了第一篇描述 STARK 的论文。与 SNARK 不同，STARK 的基础技术依赖于哈希函数。马上，依靠散列函数提供了一些好处，例如抗量子。此外，开始在网络中使用 STARK 不需要可信设置。 话虽如此，STARKs 的证明尺寸远大于 SNARKs，这意味着验证 STARKs 比 SNARKs 花费更多的时间，并且也导致 STARKs 需要更多的 gas。 此外，由于缺乏开发人员文档和社区，开发人员将很难使用 STARK。虽然有一些项目创建了基于 STARK 的扩展解决方案，例如STARKWARE，但 SNARKs 社区仍然要大得多。 虽然两个开发者社区都支持 SNARKs 和 STARKs，但以太坊基金会特别表达了对利用 Starks 的 STARKware 的声音支持。事实上，以太坊基金会向 STARKware 提供了 1200 万美元的赠款，这清楚地表明了他们对新兴技术的投入。 此外，虽然与 SNARK 相比，STARK 的文档相形见绌，但技术社区最近为那些希望实施尖端技术的人开发了更多资源。 感谢 Anish Mohammad 的洞察力和 专业知识。 原文：https://consensys.net/blog/blockchain-explained/zero-knowledge-proofs-starks-vs-snarks/
Zcash 是 zk-SNARKs 的第一个广泛应用，zk-SNARKs 是一种新颖的零知识密码学形式。Zcash 强大的隐私保证源于这样一个事实，即 Zcash 中的屏蔽交易可以在区块链上完全加密，但仍然可以通过使用 zk-SNARK 证明在网络的共识规则下验证其有效性。 首字母缩略词 zk-SNARK 代表“零知识简洁的非交互式知识论证”，指的是一种证明结构，在这种结构中，人们可以证明拥有某些信息，例如一个秘密密钥，而无需透露该信息，并且之间没有任何交互证明者和验证者。 “零知识”证明允许一方（证明者）向另一方（验证者）证明一个陈述是真实的，而不会透露超出陈述本身有效性的任何信息。例如，给定一个随机数的散列，证明者可以说服验证者确实存在一个具有该散列值的数字，而无需透露它是什么。 在零知识“知识证明”中，证明者不仅可以说服验证者该数字存在，而且他们实际上知道这样一个数字——同样，无需透露有关该数字的任何信息。“Proof”和“Argument”之间的区别是非常技术性的，我们不在这里讨论。 “简洁”的零知识证明可以在几毫秒内得到验证，即使是关于非常大的程序的语句，证明长度也只有几百字节。在第一个零知识协议中，证明者和验证者必须来回通信多轮，但在“非交互式”结构中，证明由从证明者发送到验证者的单个消息组成。目前，生成非交互式且足够短以发布到区块链的零知识证明的最有效的已知方法是具有初始设置阶段，该阶段生成在证明者和验证者之间共享的公共参考字符串。我们将这个公共引用字符串称为系统的公共参数。 如果有人可以访问用于生成这些参数的秘密随机性，他们将能够创建对验证者来说看起来有效的虚假证明。对于 Zcash，这意味着恶意方可以制造假币。为了防止这种情况发生，Zcash 通过精心设计的多方仪式生成了公共参数。要了解有关我们的参数生成仪式的更多信息，并查看我们为防止暴露 Zcash 所必需的秘密随机性（例如，计算机被喷灯）而采取的预防措施，请访问我们的Paramgen 页面。要了解有关参数生成协议背后的数学原理的更多信息，请阅读我们关于该主题的博客文章或白皮书 ( 1 , 2 )。 如何在 Zcash 中构建 zk-SNARK 为了在 Zcash 中实现零知识隐私，根据网络的共识规则确定交易有效性的函数必须返回交易是否有效的答案，而不会泄露其执行计算的任何信息。这是通过在 zk-SNARKs 中编码一些网络的共识规则来完成的。在高层次上，zk-SNARK 的工作原理是首先将您想要证明的内容转换为关于了解某些代数方程的解的等效形式。在下一节中，我们简要概述了如何将确定有效交易的规则转换为方程，然后可以对候选解决方案进行评估，而无需向验证方程的各方透露任何敏感信息。 Computation → Arithmetic Circuit → R1CS → QAP → zk-SNARK 将我们的交易有效性函数转化为数学表示的第一步是将逻辑步骤分解为尽可能小的操作，从而创建一个“算术电路”。类似于布尔电路，其中程序被编译成离散的单步，如 AND、OR、NOT，当程序被转换为算术电路时，它被分解为由加法、减法等基本算术运算组成的单步，乘法和除法（尽管在我们的特殊情况下，我们将避免使用除法）。 以下是用于计算表达式 (a+b)(bc) 的算术电路的示例： 看看这样的电路，我们可以将输入值 a、b、c 视为在电线上从左到右“行进”到输出电线。我们的下一步是构建所谓的 1 级约束系统或 R1CS，以检查值是否“正确移动”。在这个例子中，R1CS 将确认，例如，从 b 和 c 进入的乘法门出来的值是 b*c。 在这个 R1CS 表示中，验证器必须检查许多约束——几乎电路的每条线路都有一个约束。（由于技术原因，事实证明我们只对来自乘法门的电线有约束。）在 2012 年关于该主题的论文中，Gennaro、Gentry、Parno 和 Raykova 提出了一种“将所有这些约束捆绑在一起”的好方法. 此方法使用称为二次算术程序 (QAP) 的电路表示。需要检查的单个约束现在在多项式之间而不是在数字之间。多项式可能非常大，但这没关系，因为当多项式之间不存在同一性时，它在大多数情况下都不会成立。因此，您只需检查两个多项式是否在随机选择的一个点上匹配 以便以高概率正确验证证明。 如果证明者事先知道验证者会选择检查哪一点，他们可能能够制作无效的多项式，但仍然满足该点的身份。与ZK-SNARKs，复杂的数学技术，如同态加密和配对的椭圆曲线的用于评价多项式“盲目地” -即在不知道哪个点正被评估。上面描述的公共参数用于确定将检查哪个点，但采用加密形式，以便证明者和验证者都不知道它是什么。 到目前为止的描述主要解决了如何获得“SNARKs”中的 S 和 N——如何获得一个简短的、非交互式的、单消息证明——但没有解决“zk”（零知识）部分，它允许证明者维护其秘密输入的机密性。事实证明，在这个阶段，通过让证明者使用仍然满足所需身份的原始多项式的“随机移位”，可以轻松添加“zk”部分。 有关 Zcash 中 zk-SNARKs 背后关键概念的分步深入解释，请参阅我们的 SNARKs 解释器系列，其中包含以下帖子： 同态隐藏 多项式的盲评估 系数检验与假设的知识 如何使多项式的盲评估可验证 从计算到多项式 匹诺曹协议 椭圆曲线的配对 Zcash 使用bellman，这是一个用于 zk-SNARK 的 Rust 语言库。在 Sapling 升级之前，Zcash 使用了 C++ 库libsnark 的一个分支。要更深入地了解用于 Zcash 的 zk-SNARK 的协议，请参阅有关Pinocchio 协议的论文，该协议一直使用到 Sapling 升级，以及目前使用的Jens Groth 的 zk-SNARK。 如何应用 zk-SNARKs 来创建屏蔽交易 在比特币中，交易通过将发送者地址、接收者地址以及公共区块链上的输入和输出值链接起来来验证。Zcash 使用 zk-SNARKs 来证明有效交易的条件已得到满足，而无需透露有关所涉及的地址或价值的任何关键信息。屏蔽交易的发送者构建了一个证明来证明，很有可能： 输入值总和为每个屏蔽传输的输出值。 发件人证明他们拥有输入票据的私人消费密钥，从而赋予他们消费的权力。 输入票据的私人支出密钥以加密方式链接到整个交易的签名，这样交易就不能被不知道这些私人密钥的一方修改。 此外，屏蔽交易必须满足下面描述的一些其他条件。 比特币跟踪未花费的交易输出 (UTXO) 以确定哪些交易是可以花费的。在 Zcash 中，UTXO 的屏蔽等价物称为“承诺”，花费承诺涉及揭示“无效者”。Zcash 节点保存所有已创建承诺的列表，以及已披露的所有无效者。承诺和无效符存储为哈希，以避免披露有关承诺的任何信息，或哪些无效符与哪些承诺相关。 对于由屏蔽支付创建的每个新票据，都会发布一个承诺，其中包含以下各项的散列：票据发送到的地址、发送的金额、该票据唯一的数字“rho”（后来用于派生无效符）和一个随机随机数。 Commitment = HASH（收件人地址，金额，rho，r） 当一个受保护的交易被花费时，发送者使用他们的花费密钥来发布一个无效符，它是来自尚未花费的现有承诺的秘密唯一编号（“rho”）的散列，并提供零知识证明证明他们被授权消费。该散列不能已经在跟踪区块链中每个节点保存的已用交易的无效符集合中。 Nullifier = HASH（支出密钥，rho） 屏蔽交易的零知识证明验证了，除了上面列出的条件外，以下断言也是正确的： 对于每个输入注释，都存在显露的承诺。 无效符和票据承诺计算正确。 输出音符的无效符与任何其他音符的​​无效符发生冲突是不可行的。 除了用于控制地址的支出密钥之外，Zcash 还使用一组证明和验证密钥来创建和检查证明。这些密钥在上面讨论的公共参数仪式中生成，并在 Zcash 网络中的所有参与者之间共享。对于每个受保护的交易，发送方使用他们的证明密钥来生成他们输入有效的证明。矿工通过使用验证密钥检查证明者的计算来检查受保护的交易是否遵循共识规则。Zcash 的证明生成方式需要证明者预先做更多的工作，但它简化了验证，从而将主要的计算工作卸载给交易的创建者（这就是为什么创建屏蔽的 Zcash 交易可能需要几个秒， Zcash 的屏蔽交易的隐私依赖于标准的、久经考验的密码学（散列函数和流密码），但它是 zk-SNARKs 的添加，与承诺和无效系统一起应用，允许屏蔽交易的发送者和接收者证明加密交易是有效的。为加密货币提供隐私的其他方法依赖于模糊交易之间的联系，但 Zcash 交易可以存储在完全加密的区块链上这一事实为加密货币应用开辟了新的可能性. 加密交易允许各方享受公共区块链的好处，同时仍然保护他们的隐私。计划中的未来升级将允许用户自行决定有选择地披露有关屏蔽交易的信息。有关Zcash 的未来计划，请参阅我们的 Zcash 近期博客文章。 有关如何在 Zcash 中构建屏蔽交易的更深入解释，请参阅我们关于屏蔽地址之间的交易如何工作的博客文章。有关当前 Zcash 协议的完整详细信息，请参阅我们的协议规范。 zk-SNARKs 的未来应用 在 Zcash 中创建屏蔽交易只是 zk-SNARK 的许多可能应用中的一个例子。理论上，您可以使用 zk-SNARK 来验证任何关系，而不会泄露输入或泄漏信息。为复杂函数生成证明仍然是计算密集型的，对于许多应用程序来说不实用，但Zcash 团队正在推动优化 zk-SNARKs 的边界，并且已经通过更有效的实现开辟了新天地。 就目前而言，Zcash 的 zk-SNARK 实现可以添加到任何现有的分布式账本解决方案中，作为企业用例的零知识安全层。Zcash 团队的科学家是世界上最博学的 zk-SNARKs 研究人员之一，并不断致力于提出新的应用程序并提高零知识协议的效率。如果你的业务需要，可以从零知识证明或具有强大的隐私blockchain应用的解决方案中受益，取得联系我们的业务发展团队。 原文：https://z.cash/technology/zksnarks/
https://blog.web3labs.com/announcing-web3j-support-for-eip-1559 https://github.com/ethers-io/ethers.js/issues/1610 https://github.com/ethers-io/ethers.js/commit/7a12216cfbd3f86b917451924957471b8be21a8b https://web3py.readthedocs.io/en/stable/web3.eth.html#web3.eth.Eth.send_transaction
常见问题 EIP-1559 是什么？ EIP-1559 将改变以太坊的市场机制来支付交易费用。从根本上讲，EIP-1559 摆脱了首价拍卖，取而代之的是固定价格拍卖。这一变化的意义在于，提交交易的人不必猜测需要多少 gas，因为下一个区块中将包含明确的“基本费用”。对于想要优先处理交易的用户或应用程序，他们可以添加“小费”以支付矿工费用。您可以在此处更详细地了解 EIP-1559 以及它将如何改变以太坊。 EIP-1559 会使 ETH 通货紧缩吗？ EIP-1559 烧伤花掉的交易费基本费用的ETH。该 ETH 从供应中移除。在 EIP-1559 下，ETH 变得更加稀缺，因为以太坊上的所有交易都会消耗一定数量的 ETH。 对 EIP-1559 的通货紧缩程度进行准确建模是很困难的，因为您必须预测诸如预期交易之类的变量，甚至更难预测的是预期网络拥堵。从理论上讲，发生的交易越多，基本费用的燃烧对整个以太坊供应的通货紧缩压力就越大。根据网络上发生的交易数量，ETH 供应可能会在不同时间减少或膨胀更多 在与 Proof of Stake 合并后，Justin Drake 的模型估计每天将发行 1,000 ETH，并将燃烧 6,000 ETH 作为“最佳猜测”。假设有更多验证者加入，Staking APR 为 6.7%，那么年供应变化将为 -160 万 ETH，年供应率降低 1.4%。 尽管人们越来越意识到 MEV 和潜在的 EIP 会带来更高的透明度，但我们可以预期，随着机构金融交易者使用 DeFi 协议，套利机会只会变得更加复杂。这可能意味着每个区块的小费最终可能会比基本费用多得多。朱苏和Hasu实际预测，不到一半的今天的费用可以通过EIP 1559年 已经烧掉了多少 ETH？ 你可以在这里跟踪。 作为 EIP-1559 的用户，我有什么变化？ 这个想法是使用户的gas费用或多或少透明。因此，钱包将能够有更好的估计并使交易费用更可预测。他们不必过多依赖外部预言机，因为基本费用由协议本身管理。将有额外的用户体验优势，例如自动化费用竞标机制，从而减少交易确认的延迟。 此更改何时推出？ EIP-1559 将于 8 月 5 日在以太坊主网上上线，届时我们将开始推出此更改。目前，它在 Ropsten、Rinkeby、Goerli 网络中可用。 费用 EIP-1559 会使汽油更便宜吗？ 不，这不是 EIP 的意图。作为更可预测的基本费用的副作用，如果我们假设费用的可预测性意味着用户不会频繁地为 Gas 多付钱，那么 EIP-1559 可能会导致 Gas 价格有所下降。使用 EIP-1559，在区块填充超过 50% 后，基础费用将增加和减少 12.5%。例如，如果一个区块 100% 满，基础费用增加 12.5%；如果是 50%，基本费用将相同；如果是 0%，基本费用将减少 12.5%。 应用程序向汇总和第 2 层的持续移动将大大降低费用。 如何设置正确的gas费用？ MetaMask 在设置 gas 费用时会为您提供三个选项： 高：这是掉期或其他时间敏感交易的最佳选择。如果掉期处理时间过长，它通常会失败，您可能会损失资金。 中：这有利于发送资产、提取资产或其他非时间敏感但重要的交易。 低：仅应为处理时间不太重要的交易选择较低的 gas 费用。由于费用较低，很难预测您的交易何时（或是否）会成功。 我应该编辑gas费用吗？ 您可以在高、中或低汽油费之间进行选择。请参阅上面的问题以了解这些含义。此外，您可以在“高级选项”设置中编辑您的 gas 限额、优先费用和最高费用。这将覆盖 MetaMask 为您“推荐”的低、中或高设置。 我的交易在 EIP-1559 上的速度有多快？ 这暂时还不得而知。预测是由于 gas 费的可预测性，交易将进行得更快。 汽油费是如何计算的？ Gas 费用由区块的满量决定。EIP-1559 建立了交易包含的市场费率，并允许以太坊拥有双倍的区块空间（通过将每个区块的最大 gas 限制从 12.5M 增加到 25M）。然后它将块定位为只有 50% 满。 当网络利用率 > 50% 时，基础费用增加 当网络利用率 < 50% 时，基础费用减少 随着时间的推移，以太坊的区块大小将平均为相同的大小，但这种额外的容量允许具有交易包容性的灵活性。从本质上讲，它将消除设置天然气价格以进行交易的额外步骤。 MetaMask 的汽油费是否比其他任何地方都高？ 不，MetaMask 使用与其他钱包如何估算 gas 价格类似的 gas 估算预言机的汇编。我们相信我们可以提供有竞争力的气体估算。 估计的gas费用与我实际支付的费用有多少不同？ 估计的gas费用是一个范围。此范围的上限是用户将为交易支付的最高金额。在 EIP-1559 推出后，我们应该能够从我们的 gas 估计范围分析我们的交易包含率。 我应该考虑采用不同的方法来为掉期交易和其他交易设置 gas 费用吗？ MetaMask 将提供三个选项来设置 gas 费用：高、中和低（更多详细信息在“我如何设置正确的 gas 费用？”上方的常见问题解答中），并且会预先选择我们认为与您的交易类型相关的费用。如果您愿意，可以更改此设置，但我们会提供最佳建议。对于掉期等时间敏感的交易，我们建议并会预先选择“高”的gas费用。 什么是基本费用/最高费用/优先费用/等。是什么意思？ 有关（新）术语的详细定义，请参阅我们的词汇表。 由于我设置了“最高优先费用”，我如何才能看到我实际为交易支付的金额？ 您可以查看已支付的估计 gas 费用的交易活动，或在区块浏览器（如 Etherscan）上查看交易收据中的“有效GasPrice”。 我可以在没有优先费（“矿工小费”）的情况下进行交易吗？ 是的，你可以，但有可能其他交易（确实包括优先费）将被优先考虑，因为矿工被激励包含优先费的交易。 典型用户会选择“小费”金额还是为用户预选的总费用的一部分？ “小费”将作为用户在提交交易时看到的总体“汽油费”中包含在内。您还可以“编辑汽油费”以增加或减少称为“优先费”的“小费”。 在哪里可以看到他们为小费支付的费用？像 Etherscan 这样的区块浏览器现在会显示这些信息吗？ 大多数工具将相应更新以显示与 EIP 相关的新信息。交易收据中现在将有一个“effectiveGasPrice”，它返回交易执行后支付的价格（即基本费用+优先费用）。 集成 EIP-1559 如何与 MetaMask 上的 Trezor/Ledger 集成一起工作？ Trezor 和 Ledger 尚不支持 EIP-1559，因此 MetaMask 将回退到 EIP-1559 之前的气体控制。 是在钱包还是 dapp 级别上实现？换句话说，我所有的 dapp 连接都会显示钱包中的新界面，还是只显示采用 EIP-1559 的 dapp？ 如果 dapp 尚未切换到新的 EIP-1559 字段，MetaMask 将检测到这一点并使用 gasPrice 作为 maxFeePerGas。这意味着用户可能会为他们的交易多付钱。 用户体验 EIP-1559 还能如何改变钱包用户体验？ 这将因钱包而异。这个想法是让基于区块需求的费用对用户更加透明。像 MetaMask 这样的钱包将能够有更好的估计，并且不必过多依赖外部预言机，因为基本费用由协议本身管理。此外，在使用 MetaMask 时，您可以决定高、中或低 gas 费。虽然会预先选择推荐类型，但用户可以在确认交易之前进行更改。此外，用户可以在“高级”设置中更改最高费用、最高优先费用和 Gwei。这样做将覆盖初始的高、中或低设置。 在网络拥塞期间，用户体验是否会有所不同？ 在网络严重拥堵期间，基本费用将根据每个区块超过理想气体限制的需求量调整 12.5%，直到需求减弱。与首价拍卖不同，用户将更好地了解网络的拥堵程度，因为基本费用有多高。如果它太拥挤，用户可以支付或不支付该价格，就像他们在商店购买商品一样。或者，他们提交较低的费用并等待未来价格下跌。 开发者常见问题 作为 EIP-1559 的开发人员，我有什么变化？ 我们建议 dapp 开发者和网络在伦敦分叉发生后立即分别切换到 EIP-1559 字段和区块头。如果不是，传统的 gasPrice 将用作 maxFeePerGas，这意味着用户可能会为他们的交易支付过高的费用。 如何使用 MetaMask 为我的 dapp 实施 EIP-1559？ 您可以通读EIP-1559 规范，也可以查看Ethereum 的 JSON-RPC 规范中的 EIP-1559 部分。 是在用户级别还是 dapp 级别上实现？换句话说，如果我实施 EIP-1559，我的所有 dapp 用户会看到新的 gas 费用设置，还是只有同意采用 EIP-1559 的用户？ 两者都是。用户通过客户端（钱包）与以太坊网络交互。用户还可以通过 dapp 与网络交互。如果 dapp 没有切换到新的 EIP-1559 字段，MetaMask 将检测到这一点并显示 1559 之前的气体估计 UI。 谁估计 gas 估计值，dapp 还是 MetaMask？ MetaMask. 所有的客户团队都准备好迎接 EIP-1559 了吗？ 是的，客户规格已被冻结。伦敦硬分叉发布时间表如下 是否有关于复仇dapps任何潜在的风险？ 答：所有的改变都是有风险的，但以太坊社区在强大的软件开发和协调方面有着良好的记录。EIP-1559 给对时间敏感的网络参与者（例如预言机）带来了一些潜在风险。Oracle 通常提供支持 DeFi 生态系统中各种参与者所需的定价信息。例如，Compound 需要知道用户抵押品的估值（即价格 x 资产数量），以确定他们的头寸是否需要清算。这些资产的估值必须不断更新，Compound 依靠预言机来更新这些信息。可以在这篇文章中找到有关这方面的更多信息。 对于伦敦硬分叉，我的项目可能需要考虑哪些更改？ EIP-1559（和 EIP-3198）的技术变化可能会影响您的项目，包括新的区块格式、新的交易格式、用于选择 gas 价格的新值、新的 JSON RPC 调用和几个 JSON RPC 的行为改变调用。有关这些更改的详细信息，请参阅此Infura 博客文章。 词汇表 EIP-1559（以太坊改进协议 1559） 该提案最初由 Vitalik Buterin 创建，目的是通过不向矿工支付以太坊用户通过竞标天然气费而支付的天然气费来降低每笔交易的成本。以太坊用户现在可以根据网络的内部平均值对交易的平均汽油价格进行更准确的估计。如果我们假设费用的可预测性意味着用户不会频繁地为 gas 支付过高的费用，那么更可预测的基本费用的副作用可能会导致 gas 价格有所下降。有关 EIP-1559 将如何改变以太坊的更多信息，请参见此处。 Gas fee Gas 是指以太坊区块链上的交易费用。这是用户为验证或完成交易而支付的费用。 Base fee（基本费用） 由协议产生。表示将交易包含在区块中（即完成交易）所需的最小 gasUsed 乘数。这是交易费用中被烧掉的部分。 Max Priority fee - 即优先费或矿工的小费 MetaMask 最初将根据前一个区块的历史记录设置此金额。但是，用户将被允许在高级设置中编辑此金额。它被添加到交易中并代表支付给矿工的交易费用的一部分。 Max fee MetaMask 最初将根据前一个区块的历史记录设置此金额。但是，用户将被允许在高级设置中编辑此金额。它代表用户愿意为其交易支付的最大金额（包括基本费用和最高优先级费用）。每gas最高费用与基本费用+最高优先费用之间的差额将“退还”给用户。 Gas limit 交易可能消耗的最大gas单位数量。 Gwei Gwei 是以太的单位，最小的面额，代表 gigawei（或 1,000,000,000）。¹ Gwei 用于支付 gas 费用，或者说是用户支付的费用，以补偿处理和验证交易所需的计算能量在复仇blockchain.² Slippage （滑点） 滑点是报价和执行价格之间的预期百分比差异。 原文：https://metamask.io/1559