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

Create access list #22550

Merged
merged 22 commits into from
Apr 7, 2021
Merged

Conversation

MariusVanDerWijden
Copy link
Member

@MariusVanDerWijden MariusVanDerWijden commented Mar 22, 2021

replaces #22289

depends on #22333

TODO:

  • RPC call
  • Testing
> eth.createAccessList({"from": personal.listAccounts[0], "data": "0x608060806080608155fd"}, "pending")
WARN [04-01|14:30:05.452] Setting defaults failed                  error="execution reverted"
{
  accessList: [{
      address: "0x98850435dd398606953b034b92e5188a5435fd3d",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000081"]
  }],
  error: "execution reverted",
  gasUsed: "0x12eb8"
}
> eth.createAccessList({"from": personal.listAccounts[0], "data": "0x608060806080608155fd"})
WARN [04-01|14:30:14.191] Setting defaults failed                  error="execution reverted"
{
  accessList: [{
      address: "0x98850435dd398606953b034b92e5188a5435fd3d",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000081"]
  }],
  error: "execution reverted",
  gasUsed: "0x12eb8"
}

> eth.createAccessList({"from": personal.listAccounts[0], "data": "0x608060806080608155fe"})
WARN [04-01|14:30:51.090] Setting defaults failed                  error="invalid opcode: opcode 0xfe not defined"
{
  accessList: [{
      address: "0x98850435dd398606953b034b92e5188a5435fd3d",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000081"]
  }],
  error: "invalid opcode: opcode 0xfe not defined",
  gasUsed: "0xaf4e03"
}

core/vm/logger.go Outdated Show resolved Hide resolved
core/vm/logger.go Outdated Show resolved Hide resolved
@holiman
Copy link
Contributor

holiman commented Mar 24, 2021

> debug.createAccessList("pending", 0, {"from": personal.listAccounts[0], "data": "0x6080608060806081fe"})
WARN [03-24|11:22:17.781] Served debug_createAccessList            reqid=10 t=2.208827ms err="invalid opcode: opcode 0xfe not defined"
Error: invalid opcode: opcode 0xfe not defined

So, in the test, this is an invalid opcode. But it could have been a failure caused by OOG due to an item not being in the access list, which is exactly the case where the we need to help people figure out what slots they should have put there.
So for this feature, we should have it return the result even if the execution ends with a non-success.

@MariusVanDerWijden
Copy link
Member Author

MariusVanDerWijden commented Mar 24, 2021

So you would prefer something like this?

> debug.createAccessList("pending", 0, {"from": personal.listAccounts[0], "data": "0x608060806080608155fe"})
WARN [03-24|11:54:05.362] Setting defaults failed                  error="invalid opcode: opcode 0xfe not defined"
[{
    address: "0x4dceeba327b576eb0cfb03fe202e67cadc4cf221",
    storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000081"]
}]

The problem is that the sender will not know whether the createAccessList succeeded, or if they just created an access list for an always failing transaction

@holiman
Copy link
Contributor

holiman commented Mar 24, 2021

I think I'd prefer if the result told me both the accumulated accesslist and whether the tx succeeded.

@holiman
Copy link
Contributor

holiman commented Mar 24, 2021

It should also tell me what the gas usage was on successfull txs. So I can spot the difference if I send it in explicitlly or of it becomes auto-populated (1900 vs 2100 per account, IIRC)

@MariusVanDerWijden
Copy link
Member Author

Wdym with successful transactions? If we found a tx in the loop that was successful, even with an incomplete access list?

> debug.createAccessList("pending", 0, {"from": personal.listAccounts[0], "data": "0x608060806080608155fe"})
WARN [03-24|12:54:00.374] Setting defaults failed                  error="invalid opcode: opcode 0xfe not defined"
{
  Accesslist: [{
      address: "0x06e5eddb5d2bed0dfad5e7164053124ba3245d40",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000081"]
  }],
  Error: "invalid opcode: opcode 0xfe not defined",
  GasUsed: 25000000
}
> debug.createAccessList("pending", 0, {"from": personal.listAccounts[0], "data": "0x608060806080608155fd"})
WARN [03-24|12:54:50.248] Setting defaults failed                  error="execution reverted"
{
  Accesslist: [{
      address: "0x06e5eddb5d2bed0dfad5e7164053124ba3245d40",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000081"]
  }],
  Error: "execution reverted",
  GasUsed: 77496
}

@holiman
Copy link
Contributor

holiman commented Mar 24, 2021

Wdym with successful transactions?

I just meant that GasUsed: 25000000 is not important, although it's good to have it there. But primarily it's important to see the gas used in the cases where it does not eat all gas.

@holiman
Copy link
Contributor

holiman commented Mar 25, 2021

> acl
[{
    address: "0x4b6bf5e6a9efb1475495f6e40c107b3f9eb727e4",
    storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000080"]
}]
> debug.createAccessList("pending", 0, {"from": personal.listAccounts[0], "data": "0x6080608055", accessList: acl})
ERROR[03-25|09:01:34.647] RPC method debug_createAccessList crashed: runtime error: invalid memory address or nil pointer dereference
goroutine 344 [running]:
github.com/ethereum/go-ethereum/rpc.(*callback).call.func1(0xc002d4c4a0, 0x16, 0xc00030dd58)
	github.com/ethereum/go-ethereum/rpc/service.go:200 +0xba
panic(0x11e9ec0, 0x1c73540)
	runtime/panic.go:969 +0x1b9
github.com/ethereum/go-ethereum/eth.(*PublicDebugAPI).CreateAccessList(0xc0005f0250, 0xc001ba48c0, 0x0, 0x0, 0x0, 0xc001b8f020, 0x0, 0x0, 0x0)
	github.com/ethereum/go-ethereum/eth/api.go:575 +0x149
reflect.Value.call(0xc0000e7c00, 0xc000011608, 0x13, 0x1372b2e, 0x4, 0xc002d0ea80, 0x4, 0x5, 0xc002d0ea98, 0xc0007011d0, ...)
	reflect/value.go:476 +0x8c7
reflect.Value.Call(0xc0000e7c00, 0xc000011608, 0x13, 0xc002d0ea80, 0x4, 0x5, 0x16, 0x20, 0x0)
	reflect/value.go:337 +0xb9
github.com/ethereum/go-ethereum/rpc.(*callback).call(0xc0018810e0, 0x15a68a0, 0xc0006ca3c0, 0xc002d4c4a0, 0x16, 0xc0007011d0, 0x3, 0x3, 0x0, 0x0, ...)
	github.com/ethereum/go-ethereum/rpc/service.go:206 +0x2c5
github.com/ethereum/go-ethereum/rpc.(*handler).runMethod(0xc0000d06c0, 0x15a68a0, 0xc0006ca3c0, 0xc00016ad20, 0xc0018810e0, 0xc0007011d0, 0x3, 0x3, 0x3)
	github.com/ethereum/go-ethereum/rpc/handler.go:389 +0x8a
github.com/ethereum/go-ethereum/rpc.(*handler).handleCall(0xc0000d06c0, 0xc000172840, 0xc00016ad20, 0x203000)
	github.com/ethereum/go-ethereum/rpc/handler.go:337 +0x294
github.com/ethereum/go-ethereum/rpc.(*handler).handleCallMsg(0xc0000d06c0, 0xc000172840, 0xc00016ad20, 0x1595801)
	github.com/ethereum/go-ethereum/rpc/handler.go:298 +0x1be
github.com/ethereum/go-ethereum/rpc.(*handler).handleMsg.func1(0xc000172840)
	github.com/ethereum/go-ethereum/rpc/handler.go:139 +0x46
github.com/ethereum/go-ethereum/rpc.(*handler).startCallProc.func1(0xc0000d06c0, 0xc002d59e60)
	github.com/ethereum/go-ethereum/rpc/handler.go:226 +0xd2
created by github.com/ethereum/go-ethereum/rpc.(*handler).startCallProc
	github.com/ethereum/go-ethereum/rpc/handler.go:222 +0x66
 

@holiman
Copy link
Contributor

holiman commented Mar 25, 2021

I pushed some fixes, to handle the panic and also make the output gasUsed same format as the input gas (hexutil.Uint64).
This seems odd though:

> debug.createAccessList("pending", 0, {"from": personal.listAccounts[0], "data": "0x6080608055",gas: "0x12e4a", accessList: [{address: "0x0000000000000000000000000000000000121212", storageKeys:["0x0000000000000000000000000000000000000000000000000000000000000090"]}]})
{
  accessList: [{
      address: "0x4b6bf5e6a9efb1475495f6e40c107b3f9eb727e4",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000080"]
  }],
  gasUsed: "0x12e4a"
}

Why doesn't it tell me that 0x...1212 is in the access list? I suspect that you don't set the input accesslist properly

@holiman
Copy link
Contributor

holiman commented Mar 25, 2021

Hm, you do set it, but it later becomes forgotten:

TRACE[03-25|09:29:19.288] Creating Access list                     accesslist="&[{Address:0x0000000000000000000000000000000000121212 StorageKeys:[0x0000000000000000000000000000000000000000000000000000000000000090]}]"
TRACE[03-25|09:29:19.289] Capturing State                          op=PUSH1
TRACE[03-25|09:29:19.289] Capturing State                          op=PUSH1
TRACE[03-25|09:29:19.289] Capturing State                          op=SSTORE
TRACE[03-25|09:29:19.289] Creating Access list                     accesslist="&[{Address:0x4b6bF5e6A9Efb1475495f6E40c107b3f9eb727E4 StorageKeys:[0x0000000000000000000000000000000000000000000000000000000000000080]}]"

@holiman
Copy link
Contributor

holiman commented Mar 25, 2021

Yeah, so the tracer doesn't know about the existing one. You'll have to merge the output from the tracer and the input accesslist in every iteration. Or init the tracer with the input data.

@MariusVanDerWijden
Copy link
Member Author

> debug.createAccessList("pending", 0, {"from": personal.listAccounts[0], "data": "0x6080608055",gas: "0x12e4a", accessList: [{address: "0x0000000000000000000000000000000000121212", storageKeys:["0x0000000000000000000000000000000000000000000000000000000000000090"]}]})
{
  accessList: [{
      address: "0xc6da66e09fece7e62df43fbd9adda4d248433e41",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000080"]
  }, {
      address: "0x0000000000000000000000000000000000121212",
      storageKeys: ["0x0000000000000000000000000000000000000000000000000000000000000090"]
  }],
  error: "out of gas",
  gasUsed: "0x12e4a"
}

@holiman
Copy link
Contributor

holiman commented Mar 25, 2021

There's quite a lot of switching back and forth between whether it's a pointer or not. Seems like maybe the code could be simpler by not having it be a pointer:

diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go
index b7ed80ef5e..49b7622bce 100644
--- a/core/vm/access_list_tracer.go
+++ b/core/vm/access_list_tracer.go
@@ -85,7 +85,7 @@ func (a *accessList) Copy() *accessList {
 	return cp
 }
 
-func (a *accessList) ToAccessList() *types.AccessList {
+func (a *accessList) ToAccessList() types.AccessList {
 	acl := make([]types.AccessTuple, 0, len(a.addresses))
 	for addr, idx := range a.addresses {
 		var tuple types.AccessTuple
@@ -101,21 +101,19 @@ func (a *accessList) ToAccessList() *types.AccessList {
 		acl = append(acl, tuple)
 	}
 	cast := types.AccessList(acl)
-	return &cast
+	return cast
 }
 
 type AccessListTracer struct {
 	list *accessList
 }
 
-func NewAccessListTracer(acl *types.AccessList) *AccessListTracer {
+func NewAccessListTracer(acl types.AccessList) *AccessListTracer {
 	list := newAccessList()
-	if acl != nil {
-		for _, al := range *acl {
-			list.AddAddress(al.Address)
-			for _, slot := range al.StorageKeys {
-				list.AddSlot(al.Address, slot)
-			}
+	for _, al := range acl {
+		list.AddAddress(al.Address)
+		for _, slot := range al.StorageKeys {
+			list.AddSlot(al.Address, slot)
 		}
 	}
 	return &AccessListTracer{
@@ -150,11 +148,11 @@ func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost
 
 func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
 
-func (a *AccessListTracer) GetAccessList() *types.AccessList {
+func (a *AccessListTracer) GetAccessList() types.AccessList {
 	return a.list.ToAccessList()
 }
 
-func (a *AccessListTracer) GetUnpreparedAccessList(sender common.Address, dst *common.Address, precompiles []common.Address) *types.AccessList {
+func (a *AccessListTracer) GetUnpreparedAccessList(sender common.Address, dst *common.Address, precompiles []common.Address) types.AccessList {
 	copy := a.list.Copy()
 	copy.DeleteAddressIfNoSlotSet(sender)
 	if dst != nil {
diff --git a/eth/api.go b/eth/api.go
index 6b90d8eb03..8d332ea885 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -545,7 +545,7 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc
 }
 
 type AccessListResult struct {
-	Accesslist *types.AccessList `json:"accessList"`
+	Accesslist types.AccessList `json:"accessList"`
 	Error      string            `json:"error,omitempty"`
 	GasUsed    hexutil.Uint64    `json:"gasUsed"`
 }
diff --git a/eth/api_backend.go b/eth/api_backend.go
index a86d7266d1..859a381a98 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -351,7 +351,7 @@ func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Blo
 // AccessList creates an access list for the given transaction.
 // If the accesslist creation fails an error is returned.
 // If the transaction itself fails, an vmErr is returned.
-func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reexec uint64, args *ethapi.SendTxArgs) (acl *types.AccessList, gasUsed uint64, vmErr error, err error) {
+func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reexec uint64, args *ethapi.SendTxArgs) (acl types.AccessList, gasUsed uint64, vmErr error, err error) {
 	if block == nil {
 		block = b.CurrentBlock()
 	}
@@ -368,19 +368,18 @@ func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reex
 	}
 	var (
 		gas        uint64
-		accessList = args.AccessList
+		accessList  types.AccessList
 		msg        types.Message
 	)
+	if args.AccessList != nil{
+		accessList = *args.AccessList
+	}
 	for i := 0; i < 10; i++ {
 		log.Trace("Creating Access list", "accesslist", accessList)
 		// Copy the original db so we don't modify it
 		statedb := db.Copy()
 		// If we have an accesslist, use it
-		if accessList != nil {
-			msg = types.NewMessage(args.From, args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), *args.Data, *accessList, false)
-		} else {
-			msg = types.NewMessage(args.From, args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), *args.Data, nil, false)
-		}
+		msg = types.NewMessage(args.From, args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), *args.Data, accessList, false)
 		// Apply the transaction
 		context := core.NewEVMBlockContext(block.Header(), b.eth.blockchain, nil)
 		tracer := vm.NewAccessListTracer(accessList)

}

func (a *accessList) ToAccessList() *types.AccessList {
acl := make([]types.AccessTuple, 0, len(a.addresses))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
acl := make([]types.AccessTuple, 0, len(a.addresses))
acl := make(types.AccessList, 0, len(a.addresses))

And then no cast is needed, just return it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another idea would be to make state.accessList public. Although I do think it's kind of nice to have the tracer be standalone, even if it does introduce this copy-paste struct. @fjl what do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I thought about that too, but I think having two public structs called AccessList would probably confuse a lot of people, especially since it is only used as an internal datastructure

@holiman
Copy link
Contributor

holiman commented Mar 25, 2021

Oh, one more thing: If the to is nil, it's a create-tx. The newly created contract will wind up in the accesslist, but does not have to be added by the caller from the outside.
Maybe it's not something we need to care about. Not sure.

idx, addrPresent := al.addresses[address]
if !addrPresent || idx == -1 {
delete(al.addresses, address)
func (al accessList) deleteAddressIfNoSlotSet(address common.Address) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you still need this method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, deleted it now

@holiman
Copy link
Contributor

holiman commented Apr 7, 2021

> acl
[{
    address: "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192"
}]
> eth.createAccessList({"from": personal.listAccounts[0], "data": "0x6080608060806081", gas:"0xfffff", accessList: acl})
Error: invalid argument 0: missing required field 'storageKeys' for AccessTuple
	at web3.js:6347:37(47)
	at web3.js:5081:62(37)
	at <eval>:1:21(15)

Would be neat if storagekeys defaults to empty list if not specified...

@karalabe karalabe merged commit 9d10856 into ethereum:master Apr 7, 2021
atif-konasl pushed a commit to frozeman/pandora-execution-engine that referenced this pull request Oct 15, 2021
* core/vm: implement AccessListTracer

* eth: implement debug.createAccessList

* core/vm: fixed nil panics in accessListTracer

* eth: better error messages for createAccessList

* eth: some fixes on CreateAccessList

* eth: allow for provided accesslists

* eth: pass accesslist by value

* eth: remove created acocunt from accesslist

* core/vm: simplify access list tracer

* core/vm: unexport accessListTracer

* eth: return best guess if al iteration times out

* eth: return best guess if al iteration times out

* core: docstring, unexport methods

* eth: typo

* internal/ethapi: move createAccessList to eth package

* internal/ethapi: remove reexec from createAccessList

* internal/ethapi: break if al is equal to last run, not if gas is equal

* internal/web3ext: fixed arguments

* core/types: fixed equality check for accesslist

* core/types: no hardcoded vals

* core, internal: simplify access list generation, make it precise

* core/vm: fix typo

Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
@MariusVanDerWijden MariusVanDerWijden deleted the create_access_list branch November 30, 2021 15:25
gzliudan added a commit to gzliudan/XDPoSChain that referenced this pull request May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants