Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow workshare hashes in coinbase ETX data #2509

Merged
merged 2 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
}
if tx.To().IsInQiLedgerScope() { // Qi coinbase
if block.PrimeTerminusNumber().Uint64() < params.ControllerKickInBlock { // parent must be controller kick in block
p.logger.Errorf("Qi coinbase tx %x is not allowed before controller kick in block %d", tx.Hash(), params.ControllerKickInBlock)
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
Expand All @@ -557,7 +558,9 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
}
lockup.Add(lockup, blockNumber)
value := params.CalculateCoinbaseValueWithLockup(tx.Value(), lockupByte, block.NumberU64(common.ZONE_CTX))
if len(tx.Data()) == 1 {
if len(tx.Data()) == 1+common.HashLength {
// Coinbase has no extra data or hash workshare hash as extra data
// Coinbase is valid
denominations := misc.FindMinDenominations(value)
outputIndex := uint16(0)
// Iterate over the denominations in descending order
Expand Down Expand Up @@ -586,7 +589,7 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
}
}
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusLocked, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
} else if len(tx.Data()) == common.AddressLength+1 || len(tx.Data()) == common.AddressLength+common.AddressLength+1 {
} else if len(tx.Data()) == 1+common.AddressLength+common.HashLength || len(tx.Data()) == 1+common.AddressLength+common.AddressLength+common.HashLength { // 1 byte for lockup, 20 bytes for recipient, 20 bytes for delegate (optional), 32 bytes for workshare hash
contractAddr := common.BytesToAddress(tx.Data()[1:common.AddressLength+1], nodeLocation)
internal, err := contractAddr.InternalAndQuaiAddress()
if err != nil {
Expand All @@ -596,11 +599,12 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
// No code at contract address
// Coinbase reward is lost
// Justification: We should not store a coinbase lockup that can never be claimed
p.logger.Errorf("Coinbase tx %x has no code at contract address %x", tx.Hash(), contractAddr)
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
} else {
var delegate common.Address
if len(tx.Data()) == common.AddressLength+common.AddressLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:], nodeLocation)
if len(tx.Data()) == common.AddressLength+common.AddressLength+common.HashLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:common.AddressLength+common.AddressLength+1], nodeLocation)
} else {
delegate = common.Zero
}
Expand All @@ -621,6 +625,7 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
} else {
// Coinbase data is either too long or too small
// Coinbase reward is lost
p.logger.Errorf("Coinbase tx %x has invalid data length %d", tx.Hash(), len(tx.Data()))
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
}
receipts = append(receipts, receipt)
Expand All @@ -630,14 +635,10 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
if err != nil {
return nil, nil, nil, nil, 0, 0, 0, nil, nil, fmt.Errorf("coinbase tx %x has invalid recipient: %w", tx.Hash(), err)
}
if len(tx.Data()) == 1 {
if len(tx.Data()) == 1+common.HashLength {
// Coinbase is valid, no gas used
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusLocked, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
} else if len(tx.Data()) != common.AddressLength+1 && len(tx.Data()) != common.AddressLength+common.AddressLength+1 {
// Coinbase data is either too long or too small
// Coinbase reward is lost
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
} else { // Quai coinbase lockup contract
} else if len(tx.Data()) == 1+common.AddressLength+common.HashLength || len(tx.Data()) == 1+common.AddressLength+common.AddressLength+common.HashLength { // Quai coinbase lockup contract
// Create params for uint256 lockup, uint256 balance, address recipient
lockup := new(big.Int).SetUint64(params.LockupByteToBlockDepth[lockupByte])
if lockup.Uint64() < params.ConversionLockPeriod {
Expand All @@ -654,11 +655,12 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
// No code at contract address
// Coinbase reward is lost
// Justification: We should not store a coinbase lockup that can never be claimed
p.logger.Errorf("Coinbase tx %x has no code at contract address %x", tx.Hash(), contractAddr)
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
} else {
var delegate common.Address
if len(tx.Data()) == common.AddressLength+common.AddressLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:], nodeLocation)
if len(tx.Data()) == common.AddressLength+common.AddressLength+common.HashLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:common.AddressLength+common.AddressLength+1], nodeLocation)
} else {
delegate = common.Zero
}
Expand All @@ -683,6 +685,11 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
receipt.GasUsed = params.TxGas
}
}
} else {
// Coinbase data is either too long or too small
// Coinbase reward is lost
p.logger.Errorf("Coinbase tx %x has invalid data length %d", tx.Hash(), len(tx.Data()))
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
Expand Down Expand Up @@ -1037,7 +1044,7 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
if shareReward.Cmp(common.Big0) == 0 {
shareReward = big.NewInt(1)
}
emittedEtxs = append(emittedEtxs, types.NewTx(&types.ExternalTx{To: &uncleCoinbase, Gas: params.TxGas, Value: shareReward, EtxType: types.CoinbaseType, OriginatingTxHash: originHash, ETXIndex: uint16(len(emittedEtxs)), Sender: uncleCoinbase, Data: share.Data()}))
emittedEtxs = append(emittedEtxs, types.NewTx(&types.ExternalTx{To: &uncleCoinbase, Gas: params.TxGas, Value: shareReward, EtxType: types.CoinbaseType, OriginatingTxHash: originHash, ETXIndex: uint16(len(emittedEtxs)), Sender: uncleCoinbase, Data: append(share.Data(), share.Hash().Bytes()...)}))
}
}

Expand Down
113 changes: 58 additions & 55 deletions core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ func (w *worker) GeneratePendingHeader(block *types.WorkObject, fill bool) (*typ
if shareReward.Cmp(common.Big0) == 0 {
shareReward = big.NewInt(1)
}
work.etxs = append(work.etxs, types.NewTx(&types.ExternalTx{To: &uncleCoinbase, Gas: params.TxGas, Value: shareReward, EtxType: types.CoinbaseType, OriginatingTxHash: originHash, ETXIndex: uint16(len(work.etxs)), Sender: uncleCoinbase, Data: share.Data()}))
work.etxs = append(work.etxs, types.NewTx(&types.ExternalTx{To: &uncleCoinbase, Gas: params.TxGas, Value: shareReward, EtxType: types.CoinbaseType, OriginatingTxHash: originHash, ETXIndex: uint16(len(work.etxs)), Sender: uncleCoinbase, Data: append(share.Data(), share.Hash().Bytes()...)}))
}
}

Expand Down Expand Up @@ -983,7 +983,9 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t
}
lockup.Add(lockup, env.wo.Number(w.hc.NodeCtx()))
value := params.CalculateCoinbaseValueWithLockup(tx.Value(), lockupByte, env.wo.NumberU64(common.ZONE_CTX))
if len(tx.Data()) == 1 {
if len(tx.Data()) == 1+common.HashLength {
// Coinbase has no extra data or hash workshare hash as extra data
// Coinbase is valid
denominations := misc.FindMinDenominations(value)
outputIndex := uint16(0)
// Iterate over the denominations in descending order
Expand Down Expand Up @@ -1012,7 +1014,7 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t
env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt)
return receipt.Logs, true, nil
} else if len(tx.Data()) == common.AddressLength+1 || len(tx.Data()) == common.AddressLength+common.AddressLength+1 {
} else if len(tx.Data()) == 1+common.AddressLength+common.HashLength || len(tx.Data()) == 1+common.AddressLength+common.AddressLength+common.HashLength { // 1 byte for lockup, 20 bytes for recipient, 20 bytes for delegate (optional), 32 bytes for workshare hash
contractAddr := common.BytesToAddress(tx.Data()[1:common.AddressLength+1], w.chainConfig.Location)
internal, err := contractAddr.InternalAndQuaiAddress()
if err != nil {
Expand All @@ -1031,8 +1033,8 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t
return []*types.Log{}, false, nil
}
var delegate common.Address
if len(tx.Data()) == common.AddressLength+common.AddressLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:], w.chainConfig.Location)
if len(tx.Data()) == common.AddressLength+common.AddressLength+common.HashLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:common.AddressLength+common.AddressLength+1], w.chainConfig.Location)
} else {
delegate = common.Zero
}
Expand Down Expand Up @@ -1074,8 +1076,8 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t
return nil, false, fmt.Errorf("coinbase tx %x has invalid recipient: %w", tx.Hash(), err)
}
var receipt *types.Receipt
if len(tx.Data()) == 1 {
// Coinbase has no extra data
if len(tx.Data()) == 1+common.HashLength {
// Coinbase has no extra data or hash workshare hash as extra data
// Coinbase is valid
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusLocked, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
gasUsed := env.wo.GasUsed()
Expand All @@ -1088,34 +1090,63 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t
env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt)
return []*types.Log{}, false, nil
} else if len(tx.Data()) != common.AddressLength+1 && len(tx.Data()) != common.AddressLength+common.AddressLength+1 {
// Coinbase data is either too long or too small
// Coinbase reward is lost
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
} else if len(tx.Data()) == 1+common.AddressLength+common.HashLength || len(tx.Data()) == 1+common.AddressLength+common.AddressLength+common.HashLength {
// Create params for uint256 lockup, uint256 balance, address recipient
lockup := new(big.Int).SetUint64(params.LockupByteToBlockDepth[lockupByte])
if lockup.Uint64() < params.ConversionLockPeriod {
return nil, false, fmt.Errorf("coinbase lockup period is less than the minimum lockup period of %d blocks", params.ConversionLockPeriod)
}
lockup.Add(lockup, env.wo.Number(w.hc.NodeCtx()))
contractAddr := common.BytesToAddress(tx.Data()[1:common.AddressLength+1], w.chainConfig.Location)
internal, err := contractAddr.InternalAndQuaiAddress()
if err != nil {
return nil, false, fmt.Errorf("coinbase tx %x has invalid recipient: %w", tx.Hash(), err)
}

if env.state.GetCode(internal) == nil || env.wo.NumberU64(common.ZONE_CTX) < params.CoinbaseLockupPrecompileKickInHeight {
// Coinbase data is either too long or too small
// Coinbase reward is lost
receipt := &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
gasUsed := env.wo.GasUsed()
gasUsed += gasUsedForCoinbase
env.wo.Header().SetGasUsed(gasUsed)
env.gasUsedAfterTransaction = append(env.gasUsedAfterTransaction, gasUsed)
env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt)
return []*types.Log{}, false, nil
}
var delegate common.Address
if len(tx.Data()) == common.AddressLength+common.AddressLength+common.HashLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:common.AddressLength+common.AddressLength+1], w.chainConfig.Location)
} else {
delegate = common.Zero
}
reward := params.CalculateCoinbaseValueWithLockup(tx.Value(), lockupByte, env.wo.NumberU64(common.ZONE_CTX))
// Add the lockup owned by the smart contract with the miner as beneficiary
delete, _, _, oldCoinbaseLockupHash, newCoinbaseLockupHash, err := vm.AddNewLock(env.state, env.batch, contractAddr, *tx.To(), delegate, common.OneInternal(w.chainConfig.Location), lockupByte, lockup.Uint64(), env.coinbaseLatestEpoch, reward, w.chainConfig.Location, w.logger, parent.ParentHash(common.ZONE_CTX), false)
if err != nil || newCoinbaseLockupHash == (common.Hash{}) {
return nil, false, fmt.Errorf("could not add new lock: %w", err)
}
// Store the new lockup key every time
env.utxosCreate = append(env.utxosCreate, newCoinbaseLockupHash)

if delete {
env.utxosDelete = append(env.utxosDelete, oldCoinbaseLockupHash)
}
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusLocked, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()} // todo: consider adding the reward to the receipt in a log

gasUsed := env.wo.GasUsed()
gasUsed += gasUsedForCoinbase
env.wo.Header().SetGasUsed(gasUsed)
env.gasUsedAfterTransaction = append(env.gasUsedAfterTransaction, gasUsed)
env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt)
return []*types.Log{}, false, nil
}
// Create params for uint256 lockup, uint256 balance, address recipient
lockup := new(big.Int).SetUint64(params.LockupByteToBlockDepth[lockupByte])
if lockup.Uint64() < params.ConversionLockPeriod {
return nil, false, fmt.Errorf("coinbase lockup period is less than the minimum lockup period of %d blocks", params.ConversionLockPeriod)
}
lockup.Add(lockup, env.wo.Number(w.hc.NodeCtx()))
contractAddr := common.BytesToAddress(tx.Data()[1:], w.chainConfig.Location)
internal, err := contractAddr.InternalAndQuaiAddress()
if err != nil {
return nil, false, fmt.Errorf("coinbase tx %x has invalid recipient: %w", tx.Hash(), err)
}
env.txs = append(env.txs, tx)

if env.state.GetCode(internal) == nil || env.wo.NumberU64(common.ZONE_CTX) < params.CoinbaseLockupPrecompileKickInHeight {
return receipt.Logs, true, nil
} else {
// Coinbase data is either too long or too small
// Coinbase reward is lost
receipt := &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()}
gasUsed := env.wo.GasUsed()
gasUsed += gasUsedForCoinbase
env.wo.Header().SetGasUsed(gasUsed)
Expand All @@ -1124,34 +1155,6 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t
env.receipts = append(env.receipts, receipt)
return []*types.Log{}, false, nil
}
var delegate common.Address
if len(tx.Data()) == common.AddressLength+common.AddressLength+1 {
delegate = common.BytesToAddress(tx.Data()[common.AddressLength+1:], w.chainConfig.Location)
} else {
delegate = common.Zero
}
reward := params.CalculateCoinbaseValueWithLockup(tx.Value(), lockupByte, env.wo.NumberU64(common.ZONE_CTX))
// Add the lockup owned by the smart contract with the miner as beneficiary
delete, _, _, oldCoinbaseLockupHash, newCoinbaseLockupHash, err := vm.AddNewLock(env.state, env.batch, contractAddr, *tx.To(), delegate, common.OneInternal(w.chainConfig.Location), lockupByte, lockup.Uint64(), env.coinbaseLatestEpoch, reward, w.chainConfig.Location, w.logger, parent.ParentHash(common.ZONE_CTX), false)
if err != nil || newCoinbaseLockupHash == (common.Hash{}) {
return nil, false, fmt.Errorf("could not add new lock: %w", err)
}
// Store the new lockup key every time
env.utxosCreate = append(env.utxosCreate, newCoinbaseLockupHash)

if delete {
env.utxosDelete = append(env.utxosDelete, oldCoinbaseLockupHash)
}
receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusLocked, GasUsed: gasUsedForCoinbase, TxHash: tx.Hash()} // todo: consider adding the reward to the receipt in a log

gasUsed := env.wo.GasUsed()
gasUsed += gasUsedForCoinbase
env.wo.Header().SetGasUsed(gasUsed)
env.gasUsedAfterTransaction = append(env.gasUsedAfterTransaction, gasUsed)
env.receipts = append(env.receipts, receipt)
env.txs = append(env.txs, tx)

return receipt.Logs, true, nil
}
}
if tx.Type() == types.ExternalTxType && tx.To().IsInQiLedgerScope() {
Expand Down
Loading