diff --git a/core/state_processor.go b/core/state_processor.go index b4e864c2cb..328c392556 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -577,7 +577,7 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty value := etx.Value() txGas := etx.Gas() if txGas < params.TxGas { - continue + return nil, nil, nil, nil, 0, 0, 0, nil, nil, fmt.Errorf("etx gas is less than the minimum gas required") } txGas -= params.TxGas if err := gp.SubGas(params.TxGas); err != nil { @@ -587,15 +587,19 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty totalEtxGas += params.TxGas denominations := misc.FindMinDenominations(value) outputIndex := uint16(0) + total := big.NewInt(0) + success := true // Iterate over the denominations in descending order for denomination := types.MaxDenomination; denomination >= 0; denomination-- { // If the denomination count is zero, skip it if denominations[uint8(denomination)] == 0 { continue } + for j := uint64(0); j < denominations[uint8(denomination)]; j++ { if txGas < params.CallValueTransferGas || outputIndex >= types.MaxOutputIndex { // No more gas, the rest of the denominations are lost but the tx is still valid + success = false break } txGas -= params.CallValueTransferGas @@ -612,9 +616,29 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty utxosCreatedDeleted.UtxosCreatedHashes = append(utxosCreatedDeleted.UtxosCreatedHashes, types.UTXOHash(etx.Hash(), outputIndex, utxo)) utxosCreatedDeleted.UtxosCreatedKeys = append(utxosCreatedDeleted.UtxosCreatedKeys, rawdb.UtxoKeyWithDenomination(etx.Hash(), outputIndex, utxo.Denomination)) p.logger.Debugf("Converting Quai to Qi %032x with denomination %d index %d lock %d\n", tx.Hash(), denomination, outputIndex, lock) + total.Add(total, types.Denominations[uint8(denomination)]) outputIndex++ } } + if success { + receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusSuccessful, GasUsed: etx.Gas() - txGas, TxHash: tx.Hash(), + Logs: []*types.Log{{ + Address: *etx.To(), + Topics: []common.Hash{types.QuaiToQiConversionTopic}, + Data: total.Bytes(), + }}, + } + } else { + receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: etx.Gas(), TxHash: tx.Hash(), + Logs: []*types.Log{{ + Address: *etx.To(), + Topics: []common.Hash{types.QuaiToQiConversionTopic}, + Data: total.Bytes(), + }}, + } + } + receipts = append(receipts, receipt) + allLogs = append(allLogs, receipt.Logs...) } else if !types.IsCoinBaseTx(tx) && !etx.ETXSender().Location().Equal(*etx.To().Location()) && etx.To().IsInQiLedgerScope() { // Regular Qi ETX utxo := types.NewUtxoEntry(types.NewTxOut(uint8(etx.Value().Uint64()), etx.To().Bytes(), big.NewInt(0))) // There are no more checks to be made as the ETX is worked so add it to the set diff --git a/core/types/transaction.go b/core/types/transaction.go index a857889871..05c716c5af 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -1307,3 +1307,17 @@ func IsConversionTx(tx *Transaction) bool { } return tx.EtxType() == ConversionType } + +func IsQiToQuaiConversionTx(tx *Transaction) bool { + if tx.Type() == ExternalTxType && tx.EtxType() == ConversionType && tx.To().IsInQuaiLedgerScope() { + return true + } + return false +} + +func IsQuaiToQiConversionTx(tx *Transaction) bool { + if tx.Type() == ExternalTxType && tx.EtxType() == ConversionType && tx.To().IsInQiLedgerScope() { + return true + } + return false +} diff --git a/core/types/utxo.go b/core/types/utxo.go index aa8cc89272..c20d83a13c 100644 --- a/core/types/utxo.go +++ b/core/types/utxo.go @@ -20,7 +20,10 @@ const ( MaxTrimDenomination = 5 ) -var MaxQi = new(big.Int).Mul(big.NewInt(math.MaxInt64), big.NewInt(params.Ether)) // This is just a default; determine correct value later +var ( + MaxQi = new(big.Int).Mul(big.NewInt(math.MaxInt64), big.NewInt(params.Ether)) // This is just a default; determine correct value later + QuaiToQiConversionTopic = crypto.Keccak256Hash([]byte("QuaiToQiConversion")) +) // Denominations is a map of denomination to number of Qi var Denominations map[uint8]*big.Int @@ -49,8 +52,8 @@ func init() { TrimDepths = make(map[uint8]uint64) TrimDepths[0] = params.GoldenAgeForkNumberV2 + 720 // 2 hours after fork starts from block 1 TrimDepths[1] = params.GoldenAgeForkNumberV2 + 720 // 2 hours - TrimDepths[2] = params.GoldenAgeForkNumberV2 + 1080 // 3 hours - TrimDepths[3] = params.GoldenAgeForkNumberV2 + 1080 // 3 hours + TrimDepths[2] = params.GoldenAgeForkNumberV2 + 1080 // 3 hours + TrimDepths[3] = params.GoldenAgeForkNumberV2 + 1080 // 3 hours TrimDepths[4] = params.GoldenAgeForkNumberV2 + 2160 // 6 hours TrimDepths[5] = params.GoldenAgeForkNumberV2 + 4320 // 12 hours } diff --git a/core/types/wo.go b/core/types/wo.go index db293955ca..e5bedf5023 100644 --- a/core/types/wo.go +++ b/core/types/wo.go @@ -449,7 +449,7 @@ func (wo *WorkObject) TransactionsWithReceipts() []*Transaction { // ignore the coinbase tx continue } - if !IsConversionTx(t) && (t.Type() == QuaiTxType || (t.Type() == ExternalTxType && t.To().IsInQuaiLedgerScope())) { + if t.Type() == QuaiTxType || (t.Type() == ExternalTxType && t.Type() != ConversionType && t.To().IsInQuaiLedgerScope()) || IsQuaiToQiConversionTx(t) { txs = append(txs, t) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3e14591ca9..05aca55ad6 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -885,6 +885,7 @@ func opETX(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte addr, value, etxGasLimit, gasTipCap, gasFeeCap, inOffset, inSize, accessListOffset, accessListSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Bytes20ToAddress(addr.Bytes20(), interpreter.evm.chainConfig.Location) // Verify address is not in context + // This means that a conversion cannot happen with opETX if common.IsInChainScope(toAddr.Bytes(), interpreter.evm.chainConfig.Location) { temp.Clear() stack.push(&temp) @@ -911,6 +912,21 @@ func opETX(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte return nil, nil } + if etxGasLimit.CmpUint64(math.MaxUint64) == 1 { + temp.Clear() + stack.push(&temp) + fmt.Printf("%x opETX error: gas limit %d is greater than maximum %d\n", scope.Contract.self.Address(), etxGasLimit, math.MaxInt64) + return nil, nil + } + + // Overflow not a problem here as overflow guarantees a number larger than txgas + if etxGasLimit.Uint64() < params.TxGas { + temp.Clear() + stack.push(&temp) + fmt.Printf("%x opETX error: gas limit %d is less than minimum %d\n", scope.Contract.self.Address(), etxGasLimit, params.TxGas) + return nil, nil + } + interpreter.evm.StateDB.SubBalance(internalSender, total.ToBig()) // Get the arguments from the memory. @@ -1004,6 +1020,14 @@ func opConvert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, nil } + // Overflow not a problem here as overflow guarantees a number larger than txgas + if etxGasLimit.Uint64() < params.TxGas { + temp.Clear() + stack.push(&temp) + fmt.Printf("%x opETX error: gas limit %d is less than minimum %d\n", scope.Contract.self.Address(), etxGasLimit, params.TxGas) + return nil, nil + } + interpreter.evm.StateDB.SubBalance(internalSender, total.ToBig()) interpreter.evm.ETXCacheLock.RLock() diff --git a/core/worker.go b/core/worker.go index 8763982b42..9fa619b234 100644 --- a/core/worker.go +++ b/core/worker.go @@ -1241,8 +1241,7 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t } } if txGas < params.TxGas { - // No gas, the result is a no-op but the tx is still valid - return nil, false, nil + return nil, false, fmt.Errorf("insufficient gas for Qi->Quai conversion") } txGas -= params.TxGas if err := env.gasPool.SubGas(params.TxGas); err != nil { @@ -1251,7 +1250,8 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t gasUsed += params.TxGas denominations := misc.FindMinDenominations(tx.Value()) outputIndex := uint16(0) - + total := big.NewInt(0) + success := true // Iterate over the denominations in descending order for denomination := types.MaxDenomination; denomination >= 0; denomination-- { // If the denomination count is zero, skip it @@ -1261,6 +1261,7 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t for j := uint64(0); j < denominations[uint8(denomination)]; j++ { if txGas < params.CallValueTransferGas || outputIndex >= types.MaxOutputIndex { // No more gas, the rest of the denominations are lost but the tx is still valid + success = false break } txGas -= params.CallValueTransferGas @@ -1271,9 +1272,32 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t // the ETX hash is guaranteed to be unique utxoHash := types.UTXOHash(tx.Hash(), outputIndex, types.NewUtxoEntry(types.NewTxOut(uint8(denomination), tx.To().Bytes(), lock))) env.utxosCreate = append(env.utxosCreate, utxoHash) + total.Add(total, types.Denominations[uint8(denomination)]) outputIndex++ } } + var receipt *types.Receipt + if success { + receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusSuccessful, GasUsed: tx.Gas() - txGas, TxHash: tx.Hash(), + Logs: []*types.Log{{ + Address: *tx.To(), + Topics: []common.Hash{types.QuaiToQiConversionTopic}, + Data: total.Bytes(), + }}, + } + } else { + receipt = &types.Receipt{Type: tx.Type(), Status: types.ReceiptStatusFailed, GasUsed: tx.Gas(), TxHash: tx.Hash(), + Logs: []*types.Log{{ + Address: *tx.To(), + Topics: []common.Hash{types.QuaiToQiConversionTopic}, + Data: total.Bytes(), + }}, + } + } + env.wo.Header().SetGasUsed(gasUsed) + env.txs = append(env.txs, tx) + env.gasUsedAfterTransaction = append(env.gasUsedAfterTransaction, gasUsed) + return receipt.Logs, true, nil } else { // This Qi ETX should cost more gas if err := env.gasPool.SubGas(params.CallValueTransferGas); err != nil { @@ -1282,11 +1306,13 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t utxoHash := types.UTXOHash(tx.OriginatingTxHash(), tx.ETXIndex(), types.NewUtxoEntry(types.NewTxOut(uint8(tx.Value().Uint64()), tx.To().Bytes(), common.Big0))) env.utxosCreate = append(env.utxosCreate, utxoHash) gasUsed += params.CallValueTransferGas + + env.wo.Header().SetGasUsed(gasUsed) + env.txs = append(env.txs, tx) + env.gasUsedAfterTransaction = append(env.gasUsedAfterTransaction, gasUsed) + return []*types.Log{}, false, nil } - env.wo.Header().SetGasUsed(gasUsed) - env.txs = append(env.txs, tx) - env.gasUsedAfterTransaction = append(env.gasUsedAfterTransaction, gasUsed) - return []*types.Log{}, false, nil + } else if tx.Type() == types.ExternalTxType && types.IsConversionTx(tx) && tx.To().IsInQuaiLedgerScope() { // Qi->Quai Conversion gasUsed := env.wo.GasUsed() + params.QiToQuaiConversionGas env.wo.Header().SetGasUsed(gasUsed)