Commit b9ed1518 authored by pcw109550's avatar pcw109550

op-node: Span batch protectedBits implementation

parent a35425c5
...@@ -26,7 +26,7 @@ import ( ...@@ -26,7 +26,7 @@ import (
// spanBatch := SpanBatchType ++ prefix ++ payload // spanBatch := SpanBatchType ++ prefix ++ payload
// prefix := rel_timestamp ++ l1_origin_num ++ parent_check ++ l1_origin_check // prefix := rel_timestamp ++ l1_origin_num ++ parent_check ++ l1_origin_check
// payload := block_count ++ origin_bits ++ block_tx_counts ++ txs // payload := block_count ++ origin_bits ++ block_tx_counts ++ txs
// txs := contract_creation_bits ++ y_parity_bits ++ tx_sigs ++ tx_tos ++ tx_datas ++ tx_nonces ++ tx_gases // txs := contract_creation_bits ++ y_parity_bits ++ tx_sigs ++ tx_tos ++ tx_datas ++ tx_nonces ++ tx_gases ++ protected_bits
var ErrTooBigSpanBatchSize = errors.New("span batch size limit reached") var ErrTooBigSpanBatchSize = errors.New("span batch size limit reached")
......
...@@ -18,7 +18,7 @@ type spanBatchTxs struct { ...@@ -18,7 +18,7 @@ type spanBatchTxs struct {
// this field must be manually set // this field must be manually set
totalBlockTxCount uint64 totalBlockTxCount uint64
// 7 fields // 8 fields
contractCreationBits *big.Int contractCreationBits *big.Int
yParityBits *big.Int yParityBits *big.Int
txSigs []spanBatchSignature txSigs []spanBatchSignature
...@@ -26,8 +26,11 @@ type spanBatchTxs struct { ...@@ -26,8 +26,11 @@ type spanBatchTxs struct {
txGases []uint64 txGases []uint64
txTos []common.Address txTos []common.Address
txDatas []hexutil.Bytes txDatas []hexutil.Bytes
protectedBits *big.Int
txTypes []int // intermediate variables which can be recovered
txTypes []int
totalLegacyTxCount uint64
} }
type spanBatchSignature struct { type spanBatchSignature struct {
...@@ -91,6 +94,62 @@ func (btx *spanBatchTxs) decodeContractCreationBits(r *bytes.Reader) error { ...@@ -91,6 +94,62 @@ func (btx *spanBatchTxs) decodeContractCreationBits(r *bytes.Reader) error {
return nil return nil
} }
// protectedBits is bitlist right-padded to a multiple of 8 bits
func (btx *spanBatchTxs) encodeProtectedBits(w io.Writer) error {
protectedBitBufferLen := btx.totalLegacyTxCount / 8
if btx.totalLegacyTxCount%8 != 0 {
protectedBitBufferLen++
}
protectedBitBuffer := make([]byte, protectedBitBufferLen)
for i := 0; i < int(btx.totalLegacyTxCount); i += 8 {
end := i + 8
if end > int(btx.totalLegacyTxCount) {
end = int(btx.totalLegacyTxCount)
}
var bits uint = 0
for j := i; j < end; j++ {
bits |= btx.protectedBits.Bit(j) << (j - i)
}
protectedBitBuffer[i/8] = byte(bits)
}
if _, err := w.Write(protectedBitBuffer); err != nil {
return fmt.Errorf("cannot write protected bits: %w", err)
}
return nil
}
// protectedBits is bitlist right-padded to a multiple of 8 bits
func (btx *spanBatchTxs) decodeProtectedBits(r *bytes.Reader) error {
protectedBitBufferLen := btx.totalLegacyTxCount / 8
if btx.totalLegacyTxCount%8 != 0 {
protectedBitBufferLen++
}
// avoid out of memory before allocation
if protectedBitBufferLen > MaxSpanBatchSize {
return ErrTooBigSpanBatchSize
}
protectedBitBuffer := make([]byte, protectedBitBufferLen)
_, err := io.ReadFull(r, protectedBitBuffer)
if err != nil {
return fmt.Errorf("failed to read protected bits: %w", err)
}
protectedBits := new(big.Int)
for i := 0; i < int(btx.totalLegacyTxCount); i += 8 {
end := i + 8
if end > int(btx.totalLegacyTxCount) {
end = int(btx.totalLegacyTxCount)
}
bits := protectedBitBuffer[i/8]
for j := i; j < end; j++ {
bit := uint((bits >> (j - i)) & 1)
protectedBits.SetBit(protectedBits, j, bit)
}
}
btx.protectedBits = protectedBits
return nil
}
func (btx *spanBatchTxs) contractCreationCount() (uint64, error) { func (btx *spanBatchTxs) contractCreationCount() (uint64, error) {
if btx.contractCreationBits == nil { if btx.contractCreationBits == nil {
return 0, errors.New("dev error: contract creation bits not set") return 0, errors.New("dev error: contract creation bits not set")
...@@ -290,6 +349,9 @@ func (btx *spanBatchTxs) decodeTxDatas(r *bytes.Reader) error { ...@@ -290,6 +349,9 @@ func (btx *spanBatchTxs) decodeTxDatas(r *bytes.Reader) error {
} }
txDatas = append(txDatas, txData) txDatas = append(txDatas, txData)
txTypes = append(txTypes, txType) txTypes = append(txTypes, txType)
if txType == types.LegacyTxType {
btx.totalLegacyTxCount++
}
} }
btx.txDatas = txDatas btx.txDatas = txDatas
btx.txTypes = txTypes btx.txTypes = txTypes
...@@ -300,13 +362,23 @@ func (btx *spanBatchTxs) recoverV(chainID *big.Int) error { ...@@ -300,13 +362,23 @@ func (btx *spanBatchTxs) recoverV(chainID *big.Int) error {
if len(btx.txTypes) != len(btx.txSigs) { if len(btx.txTypes) != len(btx.txSigs) {
return errors.New("tx type length and tx sigs length mismatch") return errors.New("tx type length and tx sigs length mismatch")
} }
if btx.protectedBits == nil {
return errors.New("dev error: protected bits not set")
}
protectedBitsIdx := 0
for idx, txType := range btx.txTypes { for idx, txType := range btx.txTypes {
bit := uint64(btx.yParityBits.Bit(idx)) bit := uint64(btx.yParityBits.Bit(idx))
var v uint64 var v uint64
switch txType { switch txType {
case types.LegacyTxType: case types.LegacyTxType:
// EIP155 protectedBit := btx.protectedBits.Bit(protectedBitsIdx)
v = chainID.Uint64()*2 + 35 + bit protectedBitsIdx++
if protectedBit == 0 {
v = 27 + bit
} else {
// EIP-155
v = chainID.Uint64()*2 + 35 + bit
}
case types.AccessListTxType: case types.AccessListTxType:
v = bit v = bit
case types.DynamicFeeTxType: case types.DynamicFeeTxType:
...@@ -341,6 +413,9 @@ func (btx *spanBatchTxs) encode(w io.Writer) error { ...@@ -341,6 +413,9 @@ func (btx *spanBatchTxs) encode(w io.Writer) error {
if err := btx.encodeTxGases(w); err != nil { if err := btx.encodeTxGases(w); err != nil {
return err return err
} }
if err := btx.encodeProtectedBits(w); err != nil {
return err
}
return nil return nil
} }
...@@ -366,6 +441,9 @@ func (btx *spanBatchTxs) decode(r *bytes.Reader) error { ...@@ -366,6 +441,9 @@ func (btx *spanBatchTxs) decode(r *bytes.Reader) error {
if err := btx.decodeTxGases(r); err != nil { if err := btx.decodeTxGases(r); err != nil {
return err return err
} }
if err := btx.decodeProtectedBits(r); err != nil {
return err
}
return nil return nil
} }
...@@ -408,9 +486,14 @@ func convertVToYParity(v uint64, txType int) (uint, error) { ...@@ -408,9 +486,14 @@ func convertVToYParity(v uint64, txType int) (uint, error) {
var yParityBit uint var yParityBit uint
switch txType { switch txType {
case types.LegacyTxType: case types.LegacyTxType:
// EIP155: v = 2 * chainID + 35 + yParity if isProtectedV(v, txType) {
// v - 35 = yParity (mod 2) // EIP-155: v = 2 * chainID + 35 + yParity
yParityBit = uint((v - 35) & 1) // v - 35 = yParity (mod 2)
yParityBit = uint((v - 35) & 1)
} else {
// unprotected legacy txs must have v = 27 or 28
yParityBit = uint(v - 27)
}
case types.AccessListTxType: case types.AccessListTxType:
yParityBit = uint(v) yParityBit = uint(v)
case types.DynamicFeeTxType: case types.DynamicFeeTxType:
...@@ -421,6 +504,15 @@ func convertVToYParity(v uint64, txType int) (uint, error) { ...@@ -421,6 +504,15 @@ func convertVToYParity(v uint64, txType int) (uint, error) {
return yParityBit, nil return yParityBit, nil
} }
func isProtectedV(v uint64, txType int) bool {
if txType == types.LegacyTxType {
// if EIP-155 applied, v = 2 * chainID + 35 + yParity
return v != 27 && v != 28
}
// every non legacy tx are protected
return true
}
func newSpanBatchTxs(txs [][]byte, chainID *big.Int) (*spanBatchTxs, error) { func newSpanBatchTxs(txs [][]byte, chainID *big.Int) (*spanBatchTxs, error) {
totalBlockTxCount := uint64(len(txs)) totalBlockTxCount := uint64(len(txs))
var txSigs []spanBatchSignature var txSigs []spanBatchSignature
...@@ -431,11 +523,21 @@ func newSpanBatchTxs(txs [][]byte, chainID *big.Int) (*spanBatchTxs, error) { ...@@ -431,11 +523,21 @@ func newSpanBatchTxs(txs [][]byte, chainID *big.Int) (*spanBatchTxs, error) {
var txTypes []int var txTypes []int
contractCreationBits := new(big.Int) contractCreationBits := new(big.Int)
yParityBits := new(big.Int) yParityBits := new(big.Int)
protectedBits := new(big.Int)
totalLegacyTxCount := uint64(0)
for idx := 0; idx < int(totalBlockTxCount); idx++ { for idx := 0; idx < int(totalBlockTxCount); idx++ {
var tx types.Transaction var tx types.Transaction
if err := tx.UnmarshalBinary(txs[idx]); err != nil { if err := tx.UnmarshalBinary(txs[idx]); err != nil {
return nil, errors.New("failed to decode tx") return nil, errors.New("failed to decode tx")
} }
if tx.Type() == types.LegacyTxType {
protectedBit := uint(0)
if tx.Protected() {
protectedBit = uint(1)
}
protectedBits.SetBit(protectedBits, int(totalLegacyTxCount), protectedBit)
totalLegacyTxCount++
}
if tx.Protected() && tx.ChainId().Cmp(chainID) != 0 { if tx.Protected() && tx.ChainId().Cmp(chainID) != 0 {
return nil, fmt.Errorf("protected tx has chain ID %d, but expected chain ID %d", tx.ChainId(), chainID) return nil, fmt.Errorf("protected tx has chain ID %d, but expected chain ID %d", tx.ChainId(), chainID)
} }
...@@ -481,5 +583,7 @@ func newSpanBatchTxs(txs [][]byte, chainID *big.Int) (*spanBatchTxs, error) { ...@@ -481,5 +583,7 @@ func newSpanBatchTxs(txs [][]byte, chainID *big.Int) (*spanBatchTxs, error) {
txTos: txTos, txTos: txTos,
txDatas: txDatas, txDatas: txDatas,
txTypes: txTypes, txTypes: txTypes,
protectedBits: protectedBits,
totalLegacyTxCount: totalLegacyTxCount,
}, nil }, nil
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment