Skip to content

Commit

Permalink
Added opConvert
Browse files Browse the repository at this point in the history
  • Loading branch information
jdowning100 committed Apr 18, 2024
1 parent ce3873a commit ee13241
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 0 deletions.
75 changes: 75 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,81 @@ func opETX(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
return nil, nil
}

// opConvert creates an external transaction that converts Quai to Qi
// the ETX is added to the current context's cache and must go through Prime
// opConvert is intended to be called in a contract
func opConvert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
// We use it as a temporary value
temp := stack.pop() // following opCall protocol
// Pop other call parameters.
addr, uint256Value, etxGasLimit := stack.pop(), stack.pop(), stack.pop()
bigValue := uint256Value.ToBig()
toAddr := common.Bytes20ToAddress(addr.Bytes20(), interpreter.evm.chainConfig.Location)
// Verify address is in shard
if !common.IsInChainScope(toAddr.Bytes(), interpreter.evm.chainConfig.Location) {
temp.Clear()
stack.push(&temp)
fmt.Printf("%x is not in chain scope, but opConvert was called\n", toAddr)
return nil, nil // following opCall protocol
} else if !toAddr.IsInQiLedgerScope() {
temp.Clear()
stack.push(&temp)
fmt.Printf("%x is not in Qi ledger scope, but opConvert was called\n", toAddr)
return nil, nil // following opCall protocol
} else if bigValue.Cmp(params.MinQuaiConversionAmount) < 0 {
temp.Clear()
stack.push(&temp)
fmt.Printf("%x cannot convert less than %d\n", scope.Contract.self.Address(), params.MinQuaiConversionAmount.Uint64())
return nil, nil // following opCall protocol
}
sender := scope.Contract.self.Address()
internalSender, err := sender.InternalAndQuaiAddress()
if err != nil {
fmt.Printf("%x opConvert error: %s\n", scope.Contract.self.Address(), err.Error())
return nil, nil
}

fee := uint256.NewInt(0)
gasPrice, _ := uint256.FromBig(interpreter.evm.GasPrice)
fee.Mul(gasPrice, &etxGasLimit) // optional: add gasPrice (base fee) and gasTipCap
total := uint256.NewInt(0)
total.Add(&uint256Value, fee)
// Fail if we're trying to transfer more than the available balance
if total.Sign() == 0 || !interpreter.evm.Context.CanTransfer(interpreter.evm.StateDB, scope.Contract.self.Address(), total.ToBig()) {
temp.Clear()
stack.push(&temp)
fmt.Printf("%x cannot transfer %d\n", scope.Contract.self.Address(), total.Uint64())
return nil, nil
}

interpreter.evm.StateDB.SubBalance(internalSender, total.ToBig())

interpreter.evm.ETXCacheLock.RLock()
index := len(interpreter.evm.ETXCache)
interpreter.evm.ETXCacheLock.RUnlock()
if index > math.MaxUint16 {
temp.Clear()
stack.push(&temp)
fmt.Println("opConvert overflow error: too many ETXs in cache")
return nil, nil
}

// create external transaction
etxInner := types.ExternalTx{Value: bigValue, To: &toAddr, Sender: sender, OriginatingTxHash: interpreter.evm.Hash, ETXIndex: uint16(index), Gas: etxGasLimit.Uint64()}
etx := types.NewTx(&etxInner)

interpreter.evm.ETXCacheLock.Lock()
interpreter.evm.ETXCache = append(interpreter.evm.ETXCache, etx)
interpreter.evm.ETXCacheLock.Unlock()

temp.SetOne() // following opCall protocol
stack.push(&temp)

return nil, nil
}

// opIsAddressInternal is used to determine if an address is internal or external based on the current chain context
func opIsAddressInternal(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
addr := scope.Stack.peek()
Expand Down
7 changes: 7 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -992,5 +992,12 @@ func newInstructionSet() JumpTable {
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
CONVERT: {
execute: opConvert,
constantGas: params.ETXGas,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
writes: true,
},
}
}
3 changes: 3 additions & 0 deletions core/vm/opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ const (
CREATE2
ETX
ISADDRINTERNAL
CONVERT
STATICCALL OpCode = 0xfa
REVERT OpCode = 0xfd
SELFDESTRUCT OpCode = 0xff
Expand Down Expand Up @@ -383,6 +384,7 @@ var opCodeToString = map[OpCode]string{
CREATE2: "CREATE2",
ETX: "ETX",
ISADDRINTERNAL: "ISADDRINTERNAL",
CONVERT: "CONVERT",
STATICCALL: "STATICCALL",
REVERT: "REVERT",
SELFDESTRUCT: "SELFDESTRUCT",
Expand Down Expand Up @@ -546,6 +548,7 @@ var stringToOp = map[string]OpCode{
"SELFDESTRUCT": SELFDESTRUCT,
"ETX": ETX,
"ISADDRINTERNAL": ISADDRINTERNAL,
"CONVERT": CONVERT,
}

// StringToOp finds the opcode whose name is stored in `str`.
Expand Down

0 comments on commit ee13241

Please sign in to comment.