Skip to content

Commit

Permalink
Merge pull request #5276 from onflow/ramtin/5179-update-coa-address-a…
Browse files Browse the repository at this point in the history
…llocation

[Flow EVM] Updating COA address allocation
  • Loading branch information
ramtinms authored Jan 30, 2024
2 parents 3a71c54 + 72bf78c commit 5445289
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 89 deletions.
49 changes: 15 additions & 34 deletions fvm/evm/handler/addressAllocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@ import (
const (
ledgerAddressAllocatorKey = "AddressAllocator"
uint64ByteSize = 8
addressPrefixLen = 12
)

var (
// prefixes:
// the first 12 bytes of addresses allocation
// leading zeros helps with storage and all zero is reserved for the EVM precompiles
FlowEVMPrecompileAddressPrefix = [addressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
FlowEVMCOAAddressPrefix = [addressPrefixLen]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
// addressIndexShuffleSeed is used for shuffling address index
// shuffling index is used to make address postfixes look random
addressIndexShuffleSeed = uint64(0xFFEEDDCCBBAA9987)
)

type AddressAllocator struct {
Expand All @@ -43,32 +37,12 @@ func (aa *AddressAllocator) COAFactoryAddress() types.Address {
}

// AllocateCOAAddress allocates an address for COA
func (aa *AddressAllocator) AllocateCOAAddress() (types.Address, error) {
data, err := aa.led.GetValue(aa.flexAddress[:], []byte(ledgerAddressAllocatorKey))
if err != nil {
return types.Address{}, err
}
// default value for uuid is 1
uuid := uint64(1)
if len(data) > 0 {
uuid = binary.BigEndian.Uint64(data)
}

target := MakeCOAAddress(uuid)

// store new uuid
newData := make([]byte, 8)
binary.BigEndian.PutUint64(newData, uuid+1)
err = aa.led.SetValue(aa.flexAddress[:], []byte(ledgerAddressAllocatorKey), newData)
if err != nil {
return types.Address{}, err
}

return target, nil
func (aa *AddressAllocator) AllocateCOAAddress(uuid uint64) types.Address {
return MakeCOAAddress(uuid)
}

func MakeCOAAddress(index uint64) types.Address {
return makePrefixedAddress(index, FlowEVMCOAAddressPrefix)
return makePrefixedAddress(shuffleAddressIndex(index), types.FlowEVMCOAAddressPrefix)
}

func (aa *AddressAllocator) AllocatePrecompileAddress(index uint64) types.Address {
Expand All @@ -77,13 +51,20 @@ func (aa *AddressAllocator) AllocatePrecompileAddress(index uint64) types.Addres
}

func MakePrecompileAddress(index uint64) types.Address {
return makePrefixedAddress(index, FlowEVMPrecompileAddressPrefix)
return makePrefixedAddress(index, types.FlowEVMExtendedPrecompileAddressPrefix)
}

func makePrefixedAddress(index uint64, prefix [addressPrefixLen]byte) types.Address {
func makePrefixedAddress(
index uint64,
prefix [types.FlowEVMSpecialAddressPrefixLen]byte,
) types.Address {
var addr types.Address
prefixIndex := types.AddressLength - uint64ByteSize
copy(addr[:prefixIndex], prefix[:])
binary.BigEndian.PutUint64(addr[prefixIndex:], index)
return addr
}

func shuffleAddressIndex(preShuffleIndex uint64) uint64 {
return uint64(preShuffleIndex * addressIndexShuffleSeed)
}
16 changes: 10 additions & 6 deletions fvm/evm/handler/addressAllocator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@ func TestAddressAllocator(t *testing.T) {
adr := aa.AllocatePrecompileAddress(3)
expectedAddress := types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000010000000000000003"))
require.Equal(t, expectedAddress, adr)
// check conforming to types
require.False(t, types.IsACOAAddress(adr))

// test default value fall back
adr, err = aa.AllocateCOAAddress()
require.NoError(t, err)
expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000020000000000000001"))
adr = aa.AllocateCOAAddress(1)
expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffeeddccbbaa9987"))
require.Equal(t, expectedAddress, adr)
// check conforming to types
require.True(t, types.IsACOAAddress(adr))

// continous allocation logic
adr, err = aa.AllocateCOAAddress()
require.NoError(t, err)
expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x0000000000000000000000020000000000000002"))
adr = aa.AllocateCOAAddress(2)
expectedAddress = types.NewAddress(gethCommon.HexToAddress("0x000000000000000000000002ffddbb997755330e"))
require.Equal(t, expectedAddress, adr)
// check conforming to types
require.True(t, types.IsACOAAddress(adr))

// factory
factory := aa.COAFactoryAddress()
Expand Down
5 changes: 2 additions & 3 deletions fvm/evm/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ func getPrecompiles(
}

// DeployCOA deploys a cadence-owned-account and returns the address
func (h *ContractHandler) DeployCOA() types.Address {
target, err := h.addressAllocator.AllocateCOAAddress()
func (h *ContractHandler) DeployCOA(uuid uint64) types.Address {
target := h.addressAllocator.AllocateCOAAddress(uuid)
gaslimit := types.GasLimit(COAContractDeploymentRequiredGas)
handleError(err)
h.checkGasLimit(gaslimit)

factory := h.addressAllocator.COAFactoryAddress()
Expand Down
2 changes: 1 addition & 1 deletion fvm/evm/handler/handler_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) {
// setup several of accounts
// note that trie growth is the function of number of accounts
for i := 0; i < accountCount; i++ {
account := handler.AccountByAddress(handler.DeployCOA(), true)
account := handler.AccountByAddress(handler.DeployCOA(uint64(i+1)), true)
account.Deposit(types.NewFlowTokenVault(types.NewBalanceFromUFix64(100)))
accounts[i] = account
}
Expand Down
16 changes: 8 additions & 8 deletions fvm/evm/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func TestHandler_TransactionRun(t *testing.T) {
eoa := testutils.GetTestEOAAccount(t, testutils.EOATestAccount1KeyHex)

// deposit 1 Flow to the foa account
addr := handler.DeployCOA()
addr := handler.DeployCOA(1)
orgBalance := types.NewBalanceFromUFix64(types.OneFlowInUFix64)
vault := types.NewFlowTokenVault(orgBalance)
foa := handler.AccountByAddress(addr, true)
Expand All @@ -224,7 +224,7 @@ func TestHandler_TransactionRun(t *testing.T) {
)

// setup coinbase
foa2 := handler.DeployCOA()
foa2 := handler.DeployCOA(2)
account2 := handler.AccountByAddress(foa2, true)
require.Equal(t, types.NewBalanceFromUFix64(0), account2.Balance())

Expand Down Expand Up @@ -278,7 +278,7 @@ func TestHandler_COA(t *testing.T) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
handler := SetupHandler(t, backend, rootAddr)

foa := handler.AccountByAddress(handler.DeployCOA(), true)
foa := handler.AccountByAddress(handler.DeployCOA(1), true)
require.NotNil(t, foa)

zeroBalance := types.NewBalance(big.NewInt(0))
Expand Down Expand Up @@ -342,12 +342,12 @@ func TestHandler_COA(t *testing.T) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
h := SetupHandler(t, backend, rootAddr)

coa := h.DeployCOA()
coa := h.DeployCOA(1)
acc := h.AccountByAddress(coa, true)
require.NotEmpty(t, acc.Code())

// make a second account with some money
coa2 := h.DeployCOA()
coa2 := h.DeployCOA(2)
acc2 := h.AccountByAddress(coa2, true)
acc2.Deposit(types.NewFlowTokenVault(types.MakeABalanceInFlow(100)))

Expand Down Expand Up @@ -501,7 +501,7 @@ func TestHandler_COA(t *testing.T) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
handler := SetupHandler(t, backend, rootAddr)

foa := handler.AccountByAddress(handler.DeployCOA(), true)
foa := handler.AccountByAddress(handler.DeployCOA(1), true)
require.NotNil(t, foa)

// deposit 10000 flow
Expand Down Expand Up @@ -543,7 +543,7 @@ func TestHandler_COA(t *testing.T) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
h := SetupHandler(t, backend, rootAddr)

foa := h.AccountByAddress(h.DeployCOA(), true)
foa := h.AccountByAddress(h.DeployCOA(1), true)
require.NotNil(t, foa)

vault := types.NewFlowTokenVault(types.MakeABalanceInFlow(10000))
Expand All @@ -569,7 +569,7 @@ func TestHandler_COA(t *testing.T) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
handler := SetupHandler(t, backend, rootAddr)

foa := handler.AccountByAddress(handler.DeployCOA(), true)
foa := handler.AccountByAddress(handler.DeployCOA(1), true)
require.NotNil(t, foa)

vault := types.NewFlowTokenVault(types.MakeABalanceInFlow(100))
Expand Down
16 changes: 11 additions & 5 deletions fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ contract EVM {
resource BridgedAccount {

access(self)
let addressBytes: [UInt8; 20]
var addressBytes: [UInt8; 20]

init(addressBytes: [UInt8; 20]) {
init() {
self.addressBytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}

access(contract)
fun setAddress(addressBytes: [UInt8; 20]) {
self.addressBytes = addressBytes
}

Expand Down Expand Up @@ -127,9 +132,10 @@ contract EVM {
/// Creates a new bridged account
access(all)
fun createBridgedAccount(): @BridgedAccount {
return <-create BridgedAccount(
addressBytes: InternalEVM.createBridgedAccount()
)
let acc <-create BridgedAccount()
let addr = InternalEVM.createBridgedAccount(uuid: acc.uuid)
acc.setAddress(addressBytes: addr)
return <-acc
}

/// Runs an a RLP-encoded EVM transaction, deducts the gas fees,
Expand Down
12 changes: 11 additions & 1 deletion fvm/evm/stdlib/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,12 @@ func newInternalEVMTypeCallFunction(
const internalEVMTypeCreateBridgedAccountFunctionName = "createBridgedAccount"

var internalEVMTypeCreateBridgedAccountFunctionType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Label: "uuid",
TypeAnnotation: sema.NewTypeAnnotation(sema.UInt64Type),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(evmAddressBytesType),
}

Expand All @@ -1152,7 +1158,11 @@ func newInternalEVMTypeCreateBridgedAccountFunction(
internalEVMTypeCreateBridgedAccountFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
inter := invocation.Interpreter
address := handler.DeployCOA()
uuid, ok := invocation.Arguments[0].(interpreter.UInt64Value)
if !ok {
panic(errors.NewUnreachableError())
}
address := handler.DeployCOA(uint64(uuid))
return EVMAddressToAddressBytesArrayValue(inter, address)
},
)
Expand Down
Loading

0 comments on commit 5445289

Please sign in to comment.