以太坊go-ethereum源码研读(二)applyTransaction相关

这篇具有很好参考价值的文章主要介绍了以太坊go-ethereum源码研读(二)applyTransaction相关。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

在前面以太坊go-ethereum源码研读(一)中所讨论的Process函数调用了该applyTransaction函数来执行交易相关指令,于是进一步对该函数进行分析,以明晰以太坊交易的全过程。
分析过程中一些结构体或函数在以太坊go-ethereum源码研读(一)中已经讨论到,对这些结构体和函数则不在此文中过多赘述。
同时对于创建合约的create函数和进一步执行交易的call函数在下一篇研读中继续分析。

相关变量

名字 定义 解释
TxGas uint64 = 21000 位于params\protocol_params.go,是交易所需的gas量
TxGasContractCreation uint64 = 53000 位于params\protocol_params.go,家园版本硬分叉将通过交易创建智能合约的费用从21000提高到53000
TxDataZeroGas uint64 = 4 位于params\protocol_params.go,字节为0的数据的价格
TxDataNonZeroGasFrontier uint64 = 68 位于params\protocol_params.go,前沿版本字节不为0的数据的价格
TxDataNonZeroGasEIP2028 uint64 = 16 位于params\protocol_params.go,EIP2028版本字节不为0的数据的价格
TxAccessListAddressGas uint64 = 2400 位于params\protocol_params.go,访问列表中指定地址的花费
TxAccessListStorageKeyGas uint64 = 1900 位于params\protocol_params.go,访问列表中合约存储地址的花费
RefundQuotient uint64 = 2 位于params\protocol_params.go,退款上限的最大除数,即在EIP-3529前最多有一半的gas能被退回
RefundQuotientEIP3529 uint64 = 5 位于params\protocol_params.go,在EIP-3529后最多有五分之一的gas能被退回了

相关结构体

TxContext

位于core\vm\evm.go

// TxContext provides the EVM with information about a transaction.
// All fields can change between transactions.
type TxContext struct {
	// Message information
	Origin   common.Address // Provides information for ORIGIN
	GasPrice *big.Int       // Provides information for GASPRICE
}
名字 定义
Origin 事务发起方的地址
GasPrice Gas的价格

事务信息相关结构体。
BlockContext进行对比,进一步理解区块信息和事务信息的不同所在。

StateTransition

位于core\state_transition.go

type StateTransition struct {
	gp         *GasPool
	msg        Message
	gas        uint64
	gasPrice   *big.Int
	gasFeeCap  *big.Int
	gasTipCap  *big.Int
	initialGas uint64
	value      *big.Int
	data       []byte
	state      vm.StateDB
	evm        *vm.EVM
}

事务状态信息

名字 定义
GasPrice Gas的价格
gasFeeCap 用户能支付给矿工的最大单价限额
gasTipCap 支付给矿工的消费,支付该费用则矿工优先打包你的交易

相关函数

applyTransaction

func applyTransaction(msg types.Message, config *params.ChainConfig, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
	// Create a new context to be used in the EVM environment.
	txContext := NewEVMTxContext(msg)
	evm.Reset(txContext, statedb)

	// Apply the transaction to the current state (included in the env).
	result, err := ApplyMessage(evm, msg, gp)
	if err != nil {
		return nil, err
	}

	// Update the state with pending changes.
	var root []byte
	if config.IsByzantium(blockNumber) {
		statedb.Finalise(true)
	} else {
		root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
	}
	*usedGas += result.UsedGas

	// Create a new receipt for the transaction, storing the intermediate root and gas used
	// by the tx.
	receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
	if result.Failed() {
		receipt.Status = types.ReceiptStatusFailed
	} else {
		receipt.Status = types.ReceiptStatusSuccessful
	}
	receipt.TxHash = tx.Hash()
	receipt.GasUsed = result.UsedGas

	// If the transaction created a contract, store the creation address in the receipt.
	if msg.To() == nil {
		receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
	}

	// Set the receipt logs and create the bloom filter.
	receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
	receipt.BlockHash = blockHash
	receipt.BlockNumber = blockNumber
	receipt.TransactionIndex = uint(statedb.TxIndex())
	return receipt, err
}

位于core\state_processor.go
首先调用NewEVMTxContext函数根据已转换为Message格式的事务信息建立虚拟机事务上下文信息,因前述Process函数中已根据区块上下文信息建立了虚拟机整体的上下文信息,故需再调用Reset函数重设事务上下文信息。
然后调用ApplyMessage函数在当前上下文下执行事务。

NewEVMTxContext

位于core\evm.go

func NewEVMTxContext(msg Message) vm.TxContext {
	return vm.TxContext{
		Origin:   msg.From(),
		GasPrice: new(big.Int).Set(msg.GasPrice()),
	}
}

根据事务信息建立虚拟机上下文信息。

Reset

位于core\vm\evm.go

// Reset resets the EVM with a new transaction context.Reset
// This is not threadsafe and should only be done very cautiously.
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
	evm.TxContext = txCtx
	evm.StateDB = statedb
}

重设虚拟机事务上下文,要注意该函数并不是线程安全的。

ApplyMessage

// ApplyMessage computes the new state by applying the given message
// against the old state within the environment.
//
// ApplyMessage returns the bytes returned by any EVM execution (if it took place),
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) {
	return NewStateTransition(evm, msg, gp).TransitionDb()
}

位于core\state_transition.go
该函数通过应用给定消息来计算新的状态,返回EVM执行后的返回字节和使用的gas,执行失败的话还会返回error。
首先调用NewStateTransition初始化一个事务状态对象,然后调用对象中的TransitionDb来应用当前消息并转移状态,最后返回evm的执行结果。

NewStateTransition

// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
	return &StateTransition{
		gp:        gp,
		evm:       evm,
		msg:       msg,
		gasPrice:  msg.GasPrice(),
		gasFeeCap: msg.GasFeeCap(),
		gasTipCap: msg.GasTipCap(),
		value:     msg.Value(),
		data:      msg.Data(),
		state:     evm.StateDB,
	}
}

位于core\state_transition.go
初始化并返回一个事务状态对象。

TransitionDb

// TransitionDb will transition the state by applying the current message and
// returning the evm execution result with following fields.
//
// - used gas:
//      total gas used (including gas being refunded)
// - returndata:
//      the returned data from evm
// - concrete execution error:
//      various **EVM** error which aborts the execution,
//      e.g. ErrOutOfGas, ErrExecutionReverted
//
// However if any consensus issue encountered, return the error directly with
// nil evm execution result.
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
	// First check this message satisfies all consensus rules before
	// applying the message. The rules include these clauses
	//
	// 1. the nonce of the message caller is correct
	// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)
	// 3. the amount of gas required is available in the block
	// 4. the purchased gas is enough to cover intrinsic usage
	// 5. there is no overflow when calculating intrinsic gas
	// 6. caller has enough balance to cover asset transfer for **topmost** call

	// Check clauses 1-3, buy gas if everything is correct
	if err := st.preCheck(); err != nil {
		return nil, err
	}
	msg := st.msg
	sender := vm.AccountRef(msg.From())
	homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
	istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
	london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber)
	contractCreation := msg.To() == nil

	// Check clauses 4-5, subtract intrinsic gas if everything is correct
	gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
	if err != nil {
		return nil, err
	}
	if st.gas < gas {
		return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas)
	}
	st.gas -= gas

	// Check clause 6
	if msg.Value().Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From(), msg.Value()) {
		return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex())
	}

	// Set up the initial access list.
	if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin {
		st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
	}
	var (
		ret   []byte
		vmerr error // vm errors do not effect consensus and are therefore not assigned to err
	)
	if contractCreation {
		ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
	} else {
		// Increment the nonce for the next transaction
		st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
		ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
	}

	if !london {
		// Before EIP-3529: refunds were capped to gasUsed / 2
		st.refundGas(params.RefundQuotient)
	} else {
		// After EIP-3529: refunds are capped to gasUsed / 5
		st.refundGas(params.RefundQuotientEIP3529)
	}
	effectiveTip := st.gasPrice
	if london {
		effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
	}
	st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))

	return &ExecutionResult{
		UsedGas:    st.gasUsed(),
		Err:        vmerr,
		ReturnData: ret,
	}, nil
}

位于core\state_transition.go
该函数用于应用消息,转移状态,返回的执行结果中包括使用的gas,返回的evm数据,和报错。
该函数首先检查消息是否满足共识机制的规则。检查项包括

消息的nonce是否正确
调用者是否有足够余额支付油费
区块中gas量(即gaspool)是否满足该交易使用
购买的gas是否足够内部使用
计算内部gas时是否有溢出
调用者有足够的余额支付交易费用

该函数首先调用preCheck函数来检查前3条条款是否满足。
然后根据msg.To是否为空判断该交易是否为创建合约。

随后调用IntrinsicGas检查购买的gas量是否足够覆盖内部数据。对其返回的gas值进行比较,不足则报错,否则则减去其对应的量。

随后首先判断是否有交易金额,若有则调用CanTransfer来检查调用者是否有足够余额支付交易金额。

接下来查询需要满足的规则,若为Berlin版本,则需要考虑预编译的accessList来减轻了 EIP-2929 引入的一些 gas 成本增加。(可深究)
(参考链接: Ethereum Berlin Upgrade Announcement)

因为因虚拟机产生的错误并不属于共识层面,故这里引入一个变量vmerr用于表示虚拟机层面的错误。

若该交易为创建合约,则调用Create函数,不然则设置交易nonce加1,调用Call函数来执行。

随后根据不同版本,以及剩余gas量调用refundGas进行退款。

接下来计算小费的值,取min(gasTipCap,最大可支付单价和强制执行的最小单价的差值)为小费单价值。最后乘上gasUsed并得到小费总值并加在结算给矿工金额的Coinbase中。

最后返回执行结果,即花费的gas量,虚拟机的报错,和虚拟机返回数据。

preCheck

func (st *StateTransition) preCheck() error {
	// Only check transactions that are not fake
	if !st.msg.IsFake() {
		// Make sure this transaction's nonce is correct.
		stNonce := st.state.GetNonce(st.msg.From())
		if msgNonce := st.msg.Nonce(); stNonce < msgNonce {
			return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
				st.msg.From().Hex(), msgNonce, stNonce)
		} else if stNonce > msgNonce {
			return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
				st.msg.From().Hex(), msgNonce, stNonce)
		} else if stNonce+1 < stNonce {
			return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
				st.msg.From().Hex(), stNonce)
		}
		// Make sure the sender is an EOA
		if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
			return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
				st.msg.From().Hex(), codeHash)
		}
	}
	// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
	if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
		// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
		if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 {
			if l := st.gasFeeCap.BitLen(); l > 256 {
				return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
					st.msg.From().Hex(), l)
			}
			if l := st.gasTipCap.BitLen(); l > 256 {
				return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
					st.msg.From().Hex(), l)
			}
			if st.gasFeeCap.Cmp(st.gasTipCap) < 0 {
				return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
					st.msg.From().Hex(), st.gasTipCap, st.gasFeeCap)
			}
			// This will panic if baseFee is nil, but basefee presence is verified
			// as part of header validation.
			if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 {
				return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow,
					st.msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee)
			}
		}
	}
	return st.buyGas()
}

位于core\state_transition.go
该函数首先检查交易是否为假,而在上一篇文章提到的AsMessage函数中已将IsFake设置为false。同时还会检查交易的nonce值是否正确,倘若出错,则根据错误中的nonce大小报不同的错误类型。通过判断它的codeHash值是否为空,来检查发起方是否为一个外部账户(EOA)。
同时若为伦敦分叉,则要求最大可支付给单价必须大于其要求强制执行的最低费用,同时判断单价和小费是否有溢出,以及小费是否比单价还要大,根据这些错误报告不同的错误类型。
最后调用buyGas函数检查gas值的一些要求,同时购买gas。

buyGas

func (st *StateTransition) buyGas() error {
	mgval := new(big.Int).SetUint64(st.msg.Gas())
	mgval = mgval.Mul(mgval, st.gasPrice)
	balanceCheck := mgval
	if st.gasFeeCap != nil {
		balanceCheck = new(big.Int).SetUint64(st.msg.Gas())
		balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap)
		balanceCheck.Add(balanceCheck, st.value)
	}
	if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
		return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
	}
	if err := st.gp.SubGas(st.msg.Gas()); err != nil {
		return err
	}
	st.gas += st.msg.Gas()

	st.initialGas = st.msg.Gas()
	st.state.SubBalance(st.msg.From(), mgval)
	return nil
}

位于core\state_transition.go
该函数检查了发起者账户余额是否足够支付最大单价下的油费即(gasFeeCap*gas量),同时检查区块的gaspool是否有足够gas支持本次交易。都满足后则将交易状态中的初始gas值设为当前消息中的gas值,同时从发起者账户中扣除油费(gasPrice * gas量)。
最后无错误即返回nil值。

IntrinsicGas

// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) {
	// Set the starting gas for the raw transaction
	var gas uint64
	if isContractCreation && isHomestead {
		gas = params.TxGasContractCreation
	} else {
		gas = params.TxGas
	}
	// Bump the required gas by the amount of transactional data
	if len(data) > 0 {
		// Zero and non-zero bytes are priced differently
		var nz uint64
		for _, byt := range data {
			if byt != 0 {
				nz++
			}
		}
		// Make sure we don't exceed uint64 for all data combinations
		nonZeroGas := params.TxDataNonZeroGasFrontier
		if isEIP2028 {
			nonZeroGas = params.TxDataNonZeroGasEIP2028
		}
		if (math.MaxUint64-gas)/nonZeroGas < nz {
			return 0, ErrGasUintOverflow
		}
		gas += nz * nonZeroGas

		z := uint64(len(data)) - nz
		if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
			return 0, ErrGasUintOverflow
		}
		gas += z * params.TxDataZeroGas
	}
	if accessList != nil {
		gas += uint64(len(accessList)) * params.TxAccessListAddressGas
		gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
	}
	return gas, nil
}

位于core\state_transition.go
首先它先判断交易是否为创建合约以及是否为“家园”硬分叉来计算该交易的基础价格。
(链接: 家园硬分叉的介绍)
同时枚举数据中的为零字节量和非零字节量并记录,根据对应版本,计算各自所需gas量。并在计算过程中考虑了gas累加可能导致的上溢问题。同时计算EVM 执行过程中会触及的账户和合约存储位置的gas花费。
最后返回所需gas值。(奇怪的是,对于accessList产生的gas量累加不需要检查溢出吗?)

CanTransfer

// CanTransfer checks whether there are enough funds in the address' account to make a transfer.
// This does not take the necessary gas in to account to make the transfer valid.
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
	return db.GetBalance(addr).Cmp(amount) >= 0
}

位于core\evm.go
检查用户的余额是否足够支付交易金额。

Rules

位于params\config.go

// Rules ensures c's ChainID is not nil.
func (c *ChainConfig) Rules(num *big.Int) Rules {
	chainID := c.ChainID
	if chainID == nil {
		chainID = new(big.Int)
	}
	return Rules{
		ChainID:          new(big.Int).Set(chainID),
		IsHomestead:      c.IsHomestead(num),
		IsEIP150:         c.IsEIP150(num),
		IsEIP155:         c.IsEIP155(num),
		IsEIP158:         c.IsEIP158(num),
		IsByzantium:      c.IsByzantium(num),
		IsConstantinople: c.IsConstantinople(num),
		IsPetersburg:     c.IsPetersburg(num),
		IsIstanbul:       c.IsIstanbul(num),
		IsBerlin:         c.IsBerlin(num),
		IsLondon:         c.IsLondon(num),
	}
}

根据区块号查询并返回当前需满足的规则。

refundGas

func (st *StateTransition) refundGas(refundQuotient uint64) {
	// Apply refund counter, capped to a refund quotient
	refund := st.gasUsed() / refundQuotient
	if refund > st.state.GetRefund() {
		refund = st.state.GetRefund()
	}
	st.gas += refund

	// Return ETH for remaining gas, exchanged at the original rate.
	remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
	st.state.AddBalance(st.msg.From(), remaining)

	// Also return remaining gas to the block gas counter so it is
	// available for the next transaction.
	st.gp.AddGas(st.gas)
}

位于core\state_transition.go
根据不同版本,计算最大可退款gas数,和当前剩余量进行比较,取最小值。
将当前状态的gas量加上该值,并根据当前状态的gas量计算需要退给调用者的eth,同时将区块的gaspool里加上当前状态剩余gas量。文章来源地址https://www.toymoban.com/news/detail-424859.html

到了这里,关于以太坊go-ethereum源码研读(二)applyTransaction相关的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【区块链 | 智能合约】Ethereum源代码(2)- go-ethereum 客户端入口代码和Node分析

    上篇提到用 make geth 来编译geth客户端。我们来看看make file做了什么: 执行了 ci.go 里面做了两件事情 1,ln -s命令在build/_workspace/ 目录上生成了go-etherum的一个文件镜像,不占用磁盘空间,与源文件同步更新 2

    2024年02月03日
    浏览(41)
  • 【区块链 | 智能合约】Ethereum源代码(10)- 以太坊Downloader源码分析

    上一节分析到Fetcher用于同步网络节点的新区块和新的交易数据,如果新区块和本地最新的区块相隔距离较远,说明本地区块数据太旧,Fetcher就不会同步这些区块。这时候就要借助Downloader来同步完整的区块数据。 一,启动Downloader handler 初始化的时候会进行Downloader的初始化:

    2024年02月15日
    浏览(43)
  • 【区块链 | 智能合约】Ethereum源代码(8)- Ethereum服务和以太坊P2P协议发送广播源码分析

    在“【区块链 | 智能合约】Ethereum源代码(2)- go-ethereum 客户端入口代码和Node分析”一文中,我们提到Ethereum作为一个service,被Node 注册进去。Node start的时候会启动其注册的所有服务,Ethereum service也是一样。 初始化方法

    2024年01月21日
    浏览(52)
  • 【区块链 | 智能合约】Ethereum源代码(11)- 以太坊核心BlockChain源码分析

    前面几节都在分析以太坊的通信协议,怎么广播,怎么同步,怎么下载。这一节讲讲以太坊的核心模块BlockChain,也就是以太坊的区块链。 一,BlockChain的初始化 Ethereum服务初始化func init() 的时候会调用core.SetupGenesisBlock来加载创始区块。顾名思义,创始区块就是以太坊区块链中

    2024年02月08日
    浏览(46)
  • Go Ethereum源码学习笔记000

    这个专栏的内容是免费的,因为自己这边都是基于开源库和开源内容整理的学习笔记,在这个过程中进行增删改查,将自己的理解融入其中,所以这里用开源的精神分享给大家:Free software, free knowledge。当然,开源精神和软件付费/知识付费并不冲突,而是求同存异。大家觉

    2024年02月14日
    浏览(32)
  • Go Ethereum源码学习笔记 001 Geth Start

    首先读者需要具备Go语言基础,至少要通关菜鸟教程,知道Go语言的基本语法,这些基础教程网络上非常多,请大家自行学习。 具备语言基础了,还需要在开始这一章之前做一些准备工作: 安装Go SDK,即Go语言的开发环境; 安装GoLand,即Go语言的IDE,当然也可以选择VSCode等其

    2024年02月14日
    浏览(46)
  • Ethereum以太坊事件日志查询参数

    详见:https://www.quicknode.com/docs/ethereum/eth_getLogs address:合约地址 fromBlock:开始区块 toBlock:结束区块 topics:主题数组 blockHash:区块哈希,优先级高于fromBlock、toBlock 这里主要介绍topics参数,其他参数都比较好理解,topics是长度为4的数组集合,topic分为2种:一种事件签名topic,

    2024年02月07日
    浏览(42)
  • 118 以太坊 ethereum hardhat :编译 artifacts

    • 介绍 • 构建信息文件 • 读取artifacts • 目录结构 • hardhat Tutorials , hardhat 教程 • Contact 联系方式 使用 Hardhat 编译会为每个编译的合约生成两个文件(不是每个 .sol 文件):一个工件和一个调试文件。 artifact 拥有部署和与合约交互所需的所有信息。这些与大多数工具兼容

    2023年04月08日
    浏览(38)
  • 什么是Ethereum以太坊(ETH)?以及以太坊的一些基础知识

    Ethereum以太坊(ETH)是由Vitalik Buterin所创建,一种允许智能合约和去中心化应用程序(dapps)在其网络上运行的加密货币。 以太坊是仅次于比特币的第二大加密货币,它是一个基于区块链的平台,用于创建去中心化应用程序(dapps)。 比特币被设计成一种货币和价值储存手段,而

    2024年02月03日
    浏览(37)
  • 11M 以太坊 ethereum OpenZeppelin : 开发智能合约

    • 设置项目 • 第一份合同 • 编译 Solidity • 添加更多合约 • 使用 OpenZeppelin 合约 • OpenZeppelin Tutorials 教程 • Contact 联系方式 创建项目后的第一步是安装开发工具。 以太坊最流行的开发框架是Hardhat,我们用ethers.js介绍了它最常见的用途。下一个最受欢迎的是使用web3.js的

    2024年02月11日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包