Skip to content

Commit

Permalink
Paying the miner the conversion fee after the fork
Browse files Browse the repository at this point in the history
  • Loading branch information
gameofpointers committed Nov 15, 2024
1 parent 4e969e7 commit ed3371a
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 29 deletions.
57 changes: 46 additions & 11 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ import (
"github.com/dominant-strategies/go-quai/core/types"
"github.com/dominant-strategies/go-quai/core/vm"
"github.com/dominant-strategies/go-quai/crypto"
"github.com/dominant-strategies/go-quai/crypto/multiset"
"github.com/dominant-strategies/go-quai/ethdb"
"github.com/dominant-strategies/go-quai/event"
"github.com/dominant-strategies/go-quai/log"
"github.com/dominant-strategies/go-quai/crypto/multiset"
"github.com/dominant-strategies/go-quai/params"
"github.com/dominant-strategies/go-quai/trie"
)
Expand Down Expand Up @@ -1210,12 +1210,25 @@ func ValidateQiTxOutputsAndSignature(tx *types.Transaction, chain ChainContext,
if currentHeader.NumberU64(common.ZONE_CTX) >= params.GoldenAgeForkNumberV2 && totalConvertQitOut.Cmp(types.Denominations[params.MinQiConversionDenomination]) < 0 {
return nil, fmt.Errorf("tx %032x emits convert UTXO with value %d less than minimum conversion denomination", tx.Hash(), totalConvertQitOut.Uint64())
}

if currentHeader.NumberU64(common.ZONE_CTX) >= params.GoldenAgeForkNumberV2 {
// Since this transaction contains a conversion, check if the required conversion gas is paid
// The user must pay this to the miner now, but it is only added to the block gas limit when the ETX is played in the destination
requiredGas += params.QiToQuaiConversionGas
minimumFeeInQuai = new(big.Int).Mul(new(big.Int).SetUint64(requiredGas), currentHeader.BaseFee())
if txFeeInQuai.Cmp(minimumFeeInQuai) < 0 {
return nil, fmt.Errorf("tx %032x has insufficient fee for base fee * gas, have %d want %d", tx.Hash(), txFeeInQit.Uint64(), minimumFeeInQuai.Uint64())
}
}
ETXPCount++
if ETXPCount > etxPLimit {
return nil, fmt.Errorf("tx [%v] emits too many cross-prime ETXs for block. emitted: %d, limit: %d", tx.Hash().Hex(), ETXPCount, etxPLimit)
}
usedGas += params.ETXGas
txFeeInQit.Sub(txFeeInQit, txFeeInQit) // Fee goes entirely to gas to pay for conversion

if currentHeader.NumberU64(common.ZONE_CTX) < params.GoldenAgeForkNumberV2 {
txFeeInQit.Sub(txFeeInQit, txFeeInQit) // Fee goes entirely to gas to pay for conversion
}
}

if usedGas > currentHeader.GasLimit() {
Expand Down Expand Up @@ -1448,25 +1461,47 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, checkSig bool, isFir
if currentHeader.NumberU64(common.ZONE_CTX) >= params.GoldenAgeForkNumberV2 && totalConvertQitOut.Cmp(types.Denominations[params.MinQiConversionDenomination]) < 0 {
return nil, nil, nil, fmt.Errorf("tx %032x emits convert UTXO with value %d less than minimum conversion denomination", tx.Hash(), totalConvertQitOut.Uint64()), nil
}
// Since this transaction contains a conversion, the rest of the tx gas is given to conversion
remainingTxFeeInQuai := misc.QiToQuai(parent, txFeeInQit)
// Fee is basefee * gas, so gas remaining is fee remaining / basefee
remainingGas := new(big.Int).Div(remainingTxFeeInQuai, currentHeader.BaseFee())
if remainingGas.Uint64() > (currentHeader.GasLimit() / params.MinimumEtxGasDivisor) {
// Limit ETX gas to max ETX gas limit (the rest is burned)
remainingGas = new(big.Int).SetUint64(currentHeader.GasLimit() / params.MinimumEtxGasDivisor)
var etxInner types.ExternalTx
if currentHeader.NumberU64(common.ZONE_CTX) < params.GoldenAgeForkNumberV2 {
// Since this transaction contains a conversion, the rest of the tx gas is given to conversion
remainingTxFeeInQuai := misc.QiToQuai(parent, txFeeInQit)
// Fee is basefee * gas, so gas remaining is fee remaining / basefee
remainingGas := new(big.Int).Div(remainingTxFeeInQuai, currentHeader.BaseFee())
if remainingGas.Uint64() > (currentHeader.GasLimit() / params.MinimumEtxGasDivisor) {
// Limit ETX gas to max ETX gas limit (the rest is burned)
remainingGas = new(big.Int).SetUint64(currentHeader.GasLimit() / params.MinimumEtxGasDivisor)
}
ETXPCount++
if ETXPCount > *etxPLimit {
return nil, nil, nil, fmt.Errorf("tx [%v] emits too many cross-prime ETXs for block. emitted: %d, limit: %d", tx.Hash().Hex(), ETXPCount, etxPLimit), nil
}
etxInner = types.ExternalTx{Value: totalConvertQitOut, To: &convertAddress, Sender: common.ZeroAddress(location), EtxType: types.ConversionType, OriginatingTxHash: tx.Hash(), Gas: remainingGas.Uint64()} // Value is in Qits not Denomination
} else {
// Since this transaction contains a conversion, check if the required conversion gas is paid
// The user must pay this to the miner now, but it is only added to the block gas limit when the ETX is played in the destination
requiredGas += params.QiToQuaiConversionGas
minimumFeeInQuai = new(big.Int).Mul(new(big.Int).SetUint64(requiredGas), currentHeader.BaseFee())
if txFeeInQuai.Cmp(minimumFeeInQuai) < 0 {
return nil, nil, nil, fmt.Errorf("tx %032x has insufficient fee for base fee * gas: %d, have %d want %d", tx.Hash(), requiredGas, txFeeInQit.Uint64(), minimumFeeInQuai.Uint64()), nil
}
ETXPCount++
if ETXPCount > *etxPLimit {
return nil, nil, nil, fmt.Errorf("tx [%v] emits too many cross-prime ETXs for block. emitted: %d, limit: %d", tx.Hash().Hex(), ETXPCount, etxPLimit), nil
}
etxInner = types.ExternalTx{Value: totalConvertQitOut, To: &convertAddress, Sender: common.ZeroAddress(location), EtxType: types.ConversionType, OriginatingTxHash: tx.Hash(), Gas: 0} // Value is in Qits not Denomination
}
ETXPCount++
if ETXPCount > *etxPLimit {
return nil, nil, nil, fmt.Errorf("tx [%v] emits too many cross-prime ETXs for block. emitted: %d, limit: %d", tx.Hash().Hex(), ETXPCount, etxPLimit), nil
}
etxInner := types.ExternalTx{Value: totalConvertQitOut, To: &convertAddress, Sender: common.ZeroAddress(location), EtxType: types.ConversionType, OriginatingTxHash: tx.Hash(), Gas: remainingGas.Uint64()} // Value is in Qits not Denomination
*usedGas += params.ETXGas
if err := gp.SubGas(params.ETXGas); err != nil {
return nil, nil, nil, err, nil
}
etxs = append(etxs, &etxInner)
txFeeInQit.Sub(txFeeInQit, txFeeInQit) // Fee goes entirely to gas to pay for conversion
if currentHeader.NumberU64(common.ZONE_CTX) < params.GoldenAgeForkNumberV2 {
txFeeInQit.Sub(txFeeInQit, txFeeInQit) // Fee goes entirely to gas to pay for conversion
}
}
elapsedTime = time.Since(stepStart)
stepTimings["Fee Verification"] = elapsedTime
Expand Down
55 changes: 37 additions & 18 deletions core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -2187,29 +2187,48 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment, primeTermi
if env.wo.NumberU64(common.ZONE_CTX) >= params.GoldenAgeForkNumberV2 && totalConvertQitOut.Cmp(types.Denominations[params.MinQiConversionDenomination]) < 0 {
return fmt.Errorf("tx %032x emits convert UTXO with value %d less than minimum conversion denomination", tx.Hash(), totalConvertQitOut.Uint64())
}
// Since this transaction contains a conversion, the rest of the tx gas is given to conversion
remainingTxFeeInQuai := misc.QiToQuai(parent, txFeeInQit)
// Fee is basefee * gas, so gas remaining is fee remaining / basefee
remainingGas := new(big.Int).Div(remainingTxFeeInQuai, env.wo.BaseFee())
if remainingGas.Uint64() > (env.wo.GasLimit() / params.MinimumEtxGasDivisor) {
// Limit ETX gas to max ETX gas limit (the rest is burned)
remainingGas = new(big.Int).SetUint64(env.wo.GasLimit() / params.MinimumEtxGasDivisor)
}
if remainingGas.Uint64() < params.TxGas {
// Minimum gas for ETX is TxGas
return fmt.Errorf("tx %032x has insufficient remaining gas for conversion ETX, have %d want %d", tx.Hash(), remainingGas.Uint64(), params.TxGas)
}
ETXPCount++ // conversion is technically a cross-prime ETX
if ETXPCount > env.etxPLimit {
return fmt.Errorf("tx [%v] emits too many cross-prime ETXs for block. emitted: %d, limit: %d", tx.Hash().Hex(), ETXPCount, env.etxPLimit)
}
etxInner := types.ExternalTx{Value: totalConvertQitOut, To: &convertAddress, Sender: common.ZeroAddress(location), EtxType: types.ConversionType, OriginatingTxHash: tx.Hash(), Gas: remainingGas.Uint64()} // Value is in Qits not Denomination
var etxInner types.ExternalTx
if env.wo.NumberU64(common.ZONE_CTX) < params.GoldenAgeForkNumberV2 {
// Since this transaction contains a conversion, the rest of the tx gas is given to conversion
remainingTxFeeInQuai := misc.QiToQuai(parent, txFeeInQit)
// Fee is basefee * gas, so gas remaining is fee remaining / basefee
remainingGas := new(big.Int).Div(remainingTxFeeInQuai, env.wo.BaseFee())
if remainingGas.Uint64() > (env.wo.GasLimit() / params.MinimumEtxGasDivisor) {
// Limit ETX gas to max ETX gas limit (the rest is burned)
remainingGas = new(big.Int).SetUint64(env.wo.GasLimit() / params.MinimumEtxGasDivisor)
}
if remainingGas.Uint64() < params.TxGas {
// Minimum gas for ETX is TxGas
return fmt.Errorf("tx %032x has insufficient remaining gas for conversion ETX, have %d want %d", tx.Hash(), remainingGas.Uint64(), params.TxGas)
}
ETXPCount++ // conversion is technically a cross-prime ETX
if ETXPCount > env.etxPLimit {
return fmt.Errorf("tx [%v] emits too many cross-prime ETXs for block. emitted: %d, limit: %d", tx.Hash().Hex(), ETXPCount, env.etxPLimit)
}
etxInner = types.ExternalTx{Value: totalConvertQitOut, To: &convertAddress, Sender: common.ZeroAddress(location), EtxType: types.ConversionType, OriginatingTxHash: tx.Hash(), Gas: remainingGas.Uint64()} // Value is in Qits not Denomination
} else {
// Since this transaction contains a conversion, check if the required conversion gas is paid
// The user must pay this to the miner now, but it is only added to the block gas limit when the ETX is played in the destination
requiredGas += params.QiToQuaiConversionGas
minimumFeeInQuai = new(big.Int).Mul(new(big.Int).SetUint64(requiredGas), env.wo.BaseFee())
if txFeeInQuai.Cmp(minimumFeeInQuai) < 0 {
return fmt.Errorf("tx %032x has insufficient fee for base fee * gas, have %d want %d", tx.Hash(), txFeeInQit.Uint64(), minimumFeeInQuai.Uint64())
}
ETXPCount++ // conversion is technically a cross-prime ETX
if ETXPCount > env.etxPLimit {
return fmt.Errorf("tx [%v] emits too many cross-prime ETXs for block. emitted: %d, limit: %d", tx.Hash().Hex(), ETXPCount, env.etxPLimit)
}
// Value is in Qits not Denomination
etxInner = types.ExternalTx{Value: totalConvertQitOut, To: &convertAddress, Sender: common.ZeroAddress(location), EtxType: types.ConversionType, OriginatingTxHash: tx.Hash(), Gas: 0} // Conversion gas is paid from the converted Quai balance (for new account creation, when redeemed)
}
gasUsed += params.ETXGas
if err := env.gasPool.SubGas(params.ETXGas); err != nil {
return err
}
etxs = append(etxs, &etxInner)
txFeeInQit.Sub(txFeeInQit, txFeeInQit) // Fee goes entirely to gas to pay for conversion
if env.wo.NumberU64(common.ZONE_CTX) < params.GoldenAgeForkNumberV2 {
txFeeInQit.Sub(txFeeInQit, txFeeInQit) // Fee goes entirely to gas to pay for conversion
}
}
env.wo.Header().SetGasUsed(gasUsed)
env.etxRLimit -= ETXRCount
Expand Down

0 comments on commit ed3371a

Please sign in to comment.