交易流程
以太坊的转账流程基本是这样的:
- 发起交易:指定目标地址和交易金额,以及必需的gas/gasLimit
- 交易签名:使用账户私钥对交易进行签名
- 和比特币一样,也是先对交易本身求hash,再进行签名
- 提交交易:验签交易,并将交易提交到交易缓冲池
- 广播交易:通知以太坊虚拟机吧交易信息广播给其他节点
Transaction
主要包括: Txid、TxSize、from地址、收款人地址、交易Amount、Gas相关、签名信息
这里面和的数据结构和比特币不同,并没有Input[]的概念,因为以太坊的世界是有状态机的,记录了该用户的余额,不需要才UTXO来计算用户余额是否充足
type Transaction struct {
//交易数据
data txdata
hash atomic.Value
size atomic.Value
//钱包根据 from来找到
//account := accounts.Account{Address: args.From}
from atomic.Value
}
type txdata struct {
//发送者发起的交易总数
AccountNonce uint64 `json:"nonce" gencodec:"required"`
//交易的Gas价格
Price *big.Int `json:"gasPrice" gencodec:"required"`
//交易允许消耗的最大Gas
GasLimit uint64 `json:"gas" gencodec:"required"`
//交易接收者地址
Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
//交易额
Amount *big.Int `json:"value" gencodec:"required"`
//其他数据
Payload []byte `json:"input" gencodec:"required"`
// Signature values
// 交易相关签名数据
V *big.Int `json:"v" gencodec:"required"`
R *big.Int `json:"r" gencodec:"required"`
S *big.Int `json:"s" gencodec:"required"`
// This is only used when marshaling to JSON.
//交易HAsh
Hash *common.Hash `json:"hash" rlp:"-"`
}
交易签名的生成
根据 私钥 + txHash 生成签名,返回一个byte[],再拆分为3个big.Int属性,按[R || S || V]格式
//根据ECDSA算法生成签名,以字节数组的形式返回 按[R || S || V]格式
func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
return secp256k1.Sign(hash, seckey)
}
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
r, s, v, err := signer.SignatureValues(tx, sig)
if err != nil {
return nil, err
}
cpy := &Transaction{data: tx.data}
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
return cpy, nil
}
// Signature values
// 交易相关签名数据
V *big.Int `json:"v" gencodec:"required"`
R *big.Int `json:"r" gencodec:"required"`
S *big.Int `json:"s" gencodec:"required"`
交易池
比较重要的参数:
- pending map[common.Address]*txList // All currently processable transactions
- queue map[common.Address]*txList // Queued but non-processable transactions
- all *txLookup // all = queue + pending
- priced *txPricedList // All transactions sorted by price
交易池的添加逻辑:
- 先给pool加锁
- 把交易添加到queue里面(待处理交易),先check是否在交易池中已经存在 txid
./core/tx_pool.go
type TxPool struct {
config TxPoolConfig
chainconfig *params.ChainConfig
chain blockChain
gasPrice *big.Int
txFeed event.Feed
scope event.SubscriptionScope
chainHeadCh chan ChainHeadEvent
chainHeadSub event.Subscription
signer types.Signer
mu sync.RWMutex
currentState *state.StateDB // Current state in the blockchain head
pendingState *state.ManagedState // Pending state tracking virtual nonces
currentMaxGas uint64 // Current gas limit for transaction caps
locals *accountSet // Set of local transaction to exempt from eviction rules
journal *txJournal // Journal of local transaction to back up to disk
pending map[common.Address]*txList // All currently processable transactions
queue map[common.Address]*txList // Queued but non-processable transactions
beats map[common.Address]time.Time // Last heartbeat from each known account
all *txLookup // All transactions to allow lookups
priced *txPricedList // All transactions sorted by price
wg sync.WaitGroup // for shutdown sync
homestead bool
}
添加到交易池
- 判断all中是否已经存在了,拒绝重复添加交易
- 验证交易的合法性
- Gas的设置,不能小于pool的最小值设置,也不能大于currentMaxGas
- TxSize不能超过32kb
- 签名认证
- 确认Nonce的顺序
- 确认余额是否足够 // Cost = amount + gasprice * gaslimit Cost > Balance
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
if tx.Size() > 32*1024 {
return ErrOversizedData
}
if tx.Value().Sign() < 0 {
return ErrNegativeValue
}
// Ensure the transaction doesn't exceed the current block limit gas.
if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
// Make sure the transaction is signed properly
from, err := types.Sender(pool.signer, tx)
// Drop non-local transactions under our own minimal accepted gas price
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
return ErrUnderpriced
}
// Ensure the transaction adheres to nonce ordering
if pool.currentState.GetNonce(from) > tx.Nonce() {
return ErrNonceTooLow
}
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
return ErrInsufficientFunds
}
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
if tx.Gas() < intrGas {
return ErrIntrinsicGas
}
return nil
}