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

[EVM] Contract deploy change return value #5606

Merged
merged 35 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
fc3e408
update contract deploy to return result
Mar 29, 2024
a5c5217
update account type
Mar 29, 2024
794de19
update comments
Mar 29, 2024
0aa5fa7
return result on deploy
Mar 29, 2024
0ffb4d6
update deploy handler to return address as result
Mar 29, 2024
b7a5bfa
account fix comment
Mar 29, 2024
9e81add
update the test with new result type
Mar 29, 2024
dc70221
handler tests fix
Mar 29, 2024
5db7042
panic on error only
Apr 2, 2024
9745641
assign result in emulator
Apr 2, 2024
5ddbfb2
overwrite deployed address for call
Apr 2, 2024
9d3d855
update evm test
Apr 2, 2024
8819c86
handler test update
Apr 2, 2024
2c3a9ae
fix evm test assertion
Apr 2, 2024
04cf4c8
fix contract test
Apr 2, 2024
82520ec
update comment
Apr 2, 2024
a1e51e9
add contract address field
Apr 11, 2024
48445ed
update name
Apr 11, 2024
5bfcb86
add deployed contract address
Apr 11, 2024
d5f9a84
use deployed contract address field
Apr 11, 2024
1269e91
add init contract address
Apr 11, 2024
3fb8536
result to evm result
Apr 11, 2024
611156d
test the evm deploy result
Apr 11, 2024
bc60205
specify deployment contract
Apr 11, 2024
e0ff8fa
add empty address check for non-deploy
Apr 11, 2024
46bef61
fix deploy address test
Apr 11, 2024
403b9af
change result to be optional
Apr 22, 2024
904ee8c
fix name change
Apr 23, 2024
167bee1
change the result type for deployed contract to be pointer, so it can…
Apr 23, 2024
4a7b100
make the result deployed address pointer and optional
Apr 23, 2024
49b7f43
set optional if contract address not present
Apr 23, 2024
cc1e6a2
test changes with optional
Apr 23, 2024
0a0b559
additional check for contract addr
Apr 23, 2024
7be61a9
Merge branch 'master' into gregor/evm/deploy-result
devbugging Apr 23, 2024
7458cfe
Merge branch 'master' into gregor/evm/deploy-result
devbugging Apr 24, 2024
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
8 changes: 5 additions & 3 deletions fvm/evm/emulator/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,9 @@ func (proc *procedure) deployAt(
return res, nil
}

res.DeployedContractAddress = &to

proc.state.SetCode(addr, ret)
res.DeployedContractAddress = to
return res, proc.commitAndFinalize()
}

Expand Down Expand Up @@ -443,9 +444,10 @@ func (proc *procedure) run(
res.GasConsumed = execResult.UsedGas
if !execResult.Failed() { // collect vm errors
res.ReturnedValue = execResult.ReturnData
// If the transaction created a contract, store the creation address in the receipt.
// If the transaction created a contract, store the creation address in the receipt,
if msg.To == nil {
res.DeployedContractAddress = types.NewAddress(gethCrypto.CreateAddress(msg.From, msg.Nonce))
deployedAddress := types.NewAddress(gethCrypto.CreateAddress(msg.From, msg.Nonce))
res.DeployedContractAddress = &deployedAddress
}
// replace tx index and tx hash
res.Logs = proc.state.Logs(
Expand Down
9 changes: 6 additions & 3 deletions fvm/evm/emulator/emulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ func TestContractInteraction(t *testing.T) {
nonce)
res, err := blk.DirectCall(call)
require.NoError(t, err)
contractAddr = res.DeployedContractAddress
require.NotNil(t, res.DeployedContractAddress)
contractAddr = *res.DeployedContractAddress
expectedHash, err := call.Hash()
require.NoError(t, err)
require.Equal(t, expectedHash, res.TxHash)
Expand Down Expand Up @@ -434,7 +435,8 @@ func TestDeployAtFunctionality(t *testing.T) {
),
)
require.NoError(t, err)
require.Equal(t, target, res.DeployedContractAddress)
require.NotNil(t, res.DeployedContractAddress)
require.Equal(t, target, *res.DeployedContractAddress)
})
RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
require.NotNil(t, target)
Expand Down Expand Up @@ -519,7 +521,8 @@ func TestSelfdestruct(t *testing.T) {
0),
)
require.NoError(t, err)
contractAddr = res.DeployedContractAddress
require.NotNil(t, res.DeployedContractAddress)
contractAddr = *res.DeployedContractAddress
})

RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) {
Expand Down
34 changes: 27 additions & 7 deletions fvm/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func TestEVMRun(t *testing.T) {

assert(res.status == EVM.Status.successful, message: "unexpected status")
assert(res.errorCode == 0, message: "unexpected error code")
assert(res.deployedContract == nil, message: "unexpected deployed contract")
}
}
`,
Expand Down Expand Up @@ -100,7 +101,12 @@ func TestEVMRun(t *testing.T) {
access(all)
fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result {
let coinbase = EVM.EVMAddress(bytes: coinbaseBytes)
return EVM.run(tx: tx, coinbase: coinbase)
let res = EVM.run(tx: tx, coinbase: coinbase)

assert(res.status == EVM.Status.successful, message: "unexpected status")
assert(res.errorCode == 0, message: "unexpected error code")

return res
}
`,
sc.EVMContract.Address.HexWithPrefix(),
Expand Down Expand Up @@ -134,6 +140,7 @@ func TestEVMRun(t *testing.T) {
require.NoError(t, err)
require.Equal(t, types.StatusSuccessful, res.Status)
require.Equal(t, types.ErrCodeNoError, res.ErrorCode)
require.Nil(t, res.DeployedContractAddress)
require.Equal(t, num, new(big.Int).SetBytes(res.ReturnedValue).Int64())
})
})
Expand Down Expand Up @@ -666,7 +673,7 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) {
import FlowToken from %s

access(all)
fun main(): [UInt8; 20] {
fun main(code: [UInt8]): EVM.Result {
let admin = getAuthAccount(%s)
.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)!
let minter <- admin.createNewMinter(allowedAmount: 2.34)
Expand All @@ -676,28 +683,41 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) {
let cadenceOwnedAccount <- EVM.createCadenceOwnedAccount()
cadenceOwnedAccount.deposit(from: <-vault)

let address = cadenceOwnedAccount.deploy(
code: [],
gasLimit: 53000,
let res = cadenceOwnedAccount.deploy(
code: code,
gasLimit: 1000000,
value: EVM.Balance(attoflow: 1230000000000000000)
)
destroy cadenceOwnedAccount
return address.bytes
return res
}
`,
sc.EVMContract.Address.HexWithPrefix(),
sc.FlowToken.Address.HexWithPrefix(),
sc.FlowServiceAccount.Address.HexWithPrefix(),
))

script := fvm.Script(code)
script := fvm.Script(code).
WithArguments(json.MustEncode(
cadence.NewArray(
ConvertToCadence(testContract.ByteCode),
).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type{})),
))

_, output, err := vm.Run(
ctx,
script,
snapshot)
require.NoError(t, err)
require.NoError(t, output.Err)

res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value)
require.NoError(t, err)
require.Equal(t, types.StatusSuccessful, res.Status)
require.Equal(t, types.ErrCodeNoError, res.ErrorCode)
require.NotNil(t, res.DeployedContractAddress)
// we strip away first few bytes because they contain deploy code
require.Equal(t, testContract.ByteCode[17:], []byte(res.ReturnedValue))
})
})
}
Expand Down
11 changes: 6 additions & 5 deletions fvm/evm/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func NewContractHandler(
func (h *ContractHandler) DeployCOA(uuid uint64) types.Address {
res, err := h.deployCOA(uuid)
panicOnErrorOrInvalidOrFailedState(res, err)
return res.DeployedContractAddress
return *res.DeployedContractAddress
}

func (h *ContractHandler) deployCOA(uuid uint64) (*types.Result, error) {
Expand Down Expand Up @@ -557,12 +557,13 @@ func (a *Account) transfer(to types.Address, balance types.Balance) (*types.Resu
}

// Deploy deploys a contract to the EVM environment
// the new deployed contract would be at the returned address and
// the new deployed contract would be at the returned address
// contained in the result summary as data and
// the contract data is not controlled by the caller accounts
func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) types.Address {
func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) *types.ResultSummary {
res, err := a.deploy(code, gaslimit, balance)
panicOnErrorOrInvalidOrFailedState(res, err)
return types.Address(res.DeployedContractAddress)
panicOnError(err)
return res.ResultSummary()
}

func (a *Account) deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) (*types.Result, error) {
Expand Down
32 changes: 18 additions & 14 deletions fvm/evm/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) {
aa := handler.NewAddressAllocator()

result := &types.Result{
DeployedContractAddress: types.Address(testutils.RandomAddress(t)),
ReturnedValue: testutils.RandomData(t),
GasConsumed: testutils.RandomGas(1000),
ReturnedValue: testutils.RandomData(t),
GasConsumed: testutils.RandomGas(1000),
Logs: []*gethTypes.Log{
testutils.GetRandomLogFixture(t),
testutils.GetRandomLogFixture(t),
Expand Down Expand Up @@ -590,7 +589,11 @@ func TestHandler_COA(t *testing.T) {
require.Equal(t, bal, foa.Balance())

testContract := testutils.GetStorageTestContract(t)
addr := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.NewBalanceFromUFix64(0))
result := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.NewBalanceFromUFix64(0))
require.NotNil(t, result.DeployedContractAddress)
addr := *result.DeployedContractAddress
// skip first few bytes as they are deploy codes
assert.Equal(t, testContract.ByteCode[17:], []byte(result.ReturnedValue))
require.NotNil(t, addr)

num := big.NewInt(22)
Expand All @@ -606,7 +609,7 @@ func TestHandler_COA(t *testing.T) {
math.MaxUint64,
types.NewBalanceFromUFix64(0))

require.Equal(t, num, new(big.Int).SetBytes(res.ReturnedValue))
require.Equal(t, num, res.ReturnedValue.AsBigInt())
})
})
})
Expand Down Expand Up @@ -655,8 +658,11 @@ func TestHandler_COA(t *testing.T) {
foa.Deposit(vault)

testContract := testutils.GetStorageTestContract(t)
addr := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.EmptyBalance)
require.NotNil(t, addr)
result := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.EmptyBalance)
require.NotNil(t, result.DeployedContractAddress)
addr := *result.DeployedContractAddress
require.Equal(t, types.StatusSuccessful, result.Status)
require.Equal(t, types.ErrCodeNoError, result.ErrorCode)

ret := foa.Call(
addr,
Expand Down Expand Up @@ -686,9 +692,8 @@ func TestHandler_TransactionRun(t *testing.T) {
aa := handler.NewAddressAllocator()

result := &types.Result{
DeployedContractAddress: types.Address(testutils.RandomAddress(t)),
ReturnedValue: testutils.RandomData(t),
GasConsumed: testutils.RandomGas(1000),
ReturnedValue: testutils.RandomData(t),
GasConsumed: testutils.RandomGas(1000),
Logs: []*gethTypes.Log{
testutils.GetRandomLogFixture(t),
testutils.GetRandomLogFixture(t),
Expand Down Expand Up @@ -731,10 +736,9 @@ func TestHandler_TransactionRun(t *testing.T) {
aa := handler.NewAddressAllocator()

result := &types.Result{
VMError: gethVM.ErrOutOfGas,
DeployedContractAddress: types.Address(testutils.RandomAddress(t)),
ReturnedValue: testutils.RandomData(t),
GasConsumed: testutils.RandomGas(1000),
VMError: gethVM.ErrOutOfGas,
ReturnedValue: testutils.RandomData(t),
GasConsumed: testutils.RandomGas(1000),
Logs: []*gethTypes.Log{
testutils.GetRandomLogFixture(t),
testutils.GetRandomLogFixture(t),
Expand Down
29 changes: 21 additions & 8 deletions fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -183,21 +183,34 @@ contract EVM {

/// returns the data that is returned from
/// the evm for the call. For coa.deploy
/// calls it returns the address bytes of the
/// newly deployed contract.
/// calls it returns the code deployed to
/// the address provided in the contractAddress field.
access(all)
let data: [UInt8]

/// returns the newly deployed contract address
/// if the transaction caused such a deployment
/// otherwise the value is nil.
access(all)
let deployedContract: EVMAddress?

init(
status: Status,
errorCode: UInt64,
gasUsed: UInt64,
data: [UInt8]
data: [UInt8],
contractAddress: [UInt8; 20]?
) {
self.status = status
self.errorCode = errorCode
self.gasUsed = gasUsed
self.data = data

if let addressBytes = contractAddress {
self.deployedContract = EVMAddress(bytes: addressBytes)
} else {
self.deployedContract = nil
}
}
}

Expand Down Expand Up @@ -270,20 +283,20 @@ contract EVM {
}

/// Deploys a contract to the EVM environment.
/// Returns the address of the newly deployed contract
/// Returns the result which contains address of
/// the newly deployed contract
access(all)
fun deploy(
code: [UInt8],
gasLimit: UInt64,
value: Balance
): EVMAddress {
let addressBytes = InternalEVM.deploy(
): Result {
return InternalEVM.deploy(
from: self.addressBytes,
code: code,
gasLimit: gasLimit,
value: value.attoflow
)
return EVMAddress(bytes: addressBytes)
) as! Result
}

/// Calls a function with the given data.
Expand Down
Loading
Loading