交易流程
以太坊的轉賬流程基本是這樣的:
- 發起交易:指定目標地址和交易金額,以及必需的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
}