Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6a3ebe1
Re-apply warp precompile interface changes
aaronbuchwald Feb 22, 2023
9474829
Address nits
aaronbuchwald Feb 22, 2023
0d2c92b
Merge branch 'master' into precompile-pre-post-handling
aaronbuchwald Feb 23, 2023
29277c5
Separate predicate storage slot preparation into separate function in…
aaronbuchwald Feb 23, 2023
83036b7
fix lint
aaronbuchwald Feb 23, 2023
7e10491
improve miner enforcePredicates comment
aaronbuchwald Feb 23, 2023
dcfd120
Merge branch 'master' into precompile-pre-post-handling
aaronbuchwald Feb 23, 2023
584bcaa
Add HashSliceToBytes test case for empty slice
aaronbuchwald Feb 23, 2023
73aee4b
Address comments
aaronbuchwald Feb 27, 2023
2eff72e
Address comments WIP
aaronbuchwald Feb 27, 2023
e3c0899
Pre+post handling diff for shared mem precompile
Feb 27, 2023
cd79963
Separate proposer and general precompile predicates
aaronbuchwald Mar 7, 2023
be31bea
Update ShouldVerifyWithContext to return true iff proposer predicate …
aaronbuchwald Mar 7, 2023
67b7f7c
Add checkPredicates unit test
aaronbuchwald Mar 7, 2023
0d4ad9f
Merge branch 'master' into precompile-pre-post-handling
aaronbuchwald Mar 7, 2023
cde76df
Merge branch 'precompile-pre-post-handling' into precompile-pre-post-…
aaronbuchwald Mar 7, 2023
ffdda86
Update .gitignore
aaronbuchwald Mar 7, 2023
e6d6d78
goimports
aaronbuchwald Mar 7, 2023
794122d
update
aaronbuchwald Mar 7, 2023
eb2bb88
goimports config
aaronbuchwald Mar 7, 2023
2edac77
Address PR review comments and improve comments
aaronbuchwald Mar 7, 2023
15bcbe8
Merge branch 'master' into precompile-pre-post-handling
aaronbuchwald Mar 15, 2023
485ed59
Fix typo
aaronbuchwald Mar 15, 2023
5fa5bb5
Address PR comments
aaronbuchwald Mar 16, 2023
67116e0
Add rules into PrepareAccessList
aaronbuchwald Mar 16, 2023
972e486
Only copy bytes in preparePredicates if predicate precompile is active
aaronbuchwald Mar 16, 2023
9d5246c
Merge branch 'master' into precompile-pre-post-handling
aaronbuchwald Mar 16, 2023
336f0cc
Address PR comments
aaronbuchwald Mar 16, 2023
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
Prev Previous commit
Next Next commit
Pre+post handling diff for shared mem precompile
  • Loading branch information
Darioush Jalali committed Feb 27, 2023
commit e3c0899d8ef6aaa79c05a8da6f0678fdf2d578d3
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ cmd/simulator/.simulator/*
dist/

# generator rpc file for e2e tests
contract-examples/dynamic_rpc.json
contract-examples/dynamic_rpc.json
node_modules
1 change: 1 addition & 0 deletions accounts/abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
// Returns the topics for the event including the event signature (if non-anonymous event) and
// hashes derived from indexed arguments and the packed data of non-indexed args according to
// the event ABI specification.
// The order of arguments must match the order of the event definition.
// https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#indexed-event-encoding.
// Note: PackEvent does not support array (fixed or dynamic-size) or struct types.
func (abi ABI) PackEvent(name string, args ...interface{}) ([]common.Hash, []byte, error) {
Expand Down
24 changes: 20 additions & 4 deletions plugin/evm/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,35 @@ func (b *Block) Accept(context.Context) error {
if err := vm.blockChain.Accept(b.ethBlock); err != nil {
return fmt.Errorf("chain could not accept %s: %w", b.ID(), err)
}

// Call Accept for relevant precompile logs. This should apply DB operations to the VM's versionDB
// to be committed atomically with marking this block as accepted.
if err := b.handlePrecompileAccept(); err != nil {
sharedMemoryWriter := NewSharedMemoryWriter()
if err := b.handlePrecompileAccept(sharedMemoryWriter); err != nil {
return err
}
if err := vm.acceptedBlockDB.Put(lastAcceptedKey, b.id[:]); err != nil {
return fmt.Errorf("failed to put %s as the last accepted block: %w", b.ID(), err)
}

return vm.db.Commit()
// Get pending operations on the vm's versionDB so we can apply them atomically
// with the shared memory requests.
vdbBatch, err := vm.db.CommitBatch()
if err != nil {
return fmt.Errorf("failed to get commit batch: %w", err)
}

// Apply any shared memory requests that accumulated from processing the logs
// of the accepted block (generated by precompiles) atomically with other pending
// changes to the vm's versionDB.
return vm.ctx.SharedMemory.Apply(sharedMemoryWriter.requests, vdbBatch)
}

// handlePrecompileAccept calls Accept on any logs generated with an active precompile address that implements
// contract.Accepter
// This function assumes that the Accept function will ONLY operate on state maintained in the VM's versiondb.
// This ensures that any DB operations are performed atomically with marking the block as accepted.
func (b *Block) handlePrecompileAccept() error {
func (b *Block) handlePrecompileAccept(sharedMemoryWriter *sharedMemoryWriter) error {
rules := b.vm.chainConfig.AvalancheRules(b.ethBlock.Number(), b.ethBlock.Timestamp())
receipts := rawdb.ReadReceipts(b.vm.chaindb, b.ethBlock.Hash(), b.ethBlock.NumberU64(), b.vm.chainConfig)
if receipts == nil {
Expand All @@ -102,7 +114,11 @@ func (b *Block) handlePrecompileAccept() error {
continue
}

if err := accepter.Accept(log.TxHash, txIndex, log.Topics, log.Data); err != nil {
acceptCtx := &contract.AcceptContext{
SnowCtx: b.vm.ctx,
SharedMemory: sharedMemoryWriter,
}
if err := accepter.Accept(acceptCtx, log.TxHash, txIndex, log.Topics, log.Data); err != nil {
return err
}
}
Expand Down
37 changes: 37 additions & 0 deletions plugin/evm/shared_memory_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"github.com/ava-labs/avalanchego/chains/atomic"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/subnet-evm/precompile/contract"
)

var _ contract.SharedMemoryWriter = &sharedMemoryWriter{}

type sharedMemoryWriter struct {
requests map[ids.ID]*atomic.Requests
}

func NewSharedMemoryWriter() *sharedMemoryWriter {
return &sharedMemoryWriter{
requests: make(map[ids.ID]*atomic.Requests),
}
}

func (s *sharedMemoryWriter) AddSharedMemoryRequests(chainID ids.ID, requests *atomic.Requests) {
mergeAtomicOpsToMap(s.requests, chainID, requests)
}

// mergeAtomicOps merges atomic ops for [chainID] represented by [requests]
// to the [output] map provided.
func mergeAtomicOpsToMap(output map[ids.ID]*atomic.Requests, chainID ids.ID, requests *atomic.Requests) {
if request, exists := output[chainID]; exists {
request.PutRequests = append(request.PutRequests, requests.PutRequests...)
request.RemoveRequests = append(request.RemoveRequests, requests.RemoveRequests...)
} else {
output[chainID] = requests
}
}
29 changes: 19 additions & 10 deletions plugin/evm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import (
"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/api/keystore"
"github.com/ava-labs/avalanchego/chains/atomic"
"github.com/ava-labs/avalanchego/database/manager"
"github.com/ava-labs/avalanchego/database/prefixdb"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/snow/choices"
Expand Down Expand Up @@ -157,12 +159,14 @@ func (sn *snLookup) SubnetID(chainID ids.ID) (ids.ID, error) {
}

// If [genesisJSON] is empty, defaults to using [genesisJSONLatest]
func setupGenesis(t *testing.T,
func setupGenesis(
t *testing.T,
genesisJSON string,
) (*snow.Context,
manager.Manager,
[]byte,
chan engCommon.Message,
*atomic.Memory,
) {
if len(genesisJSON) == 0 {
genesisJSON = genesisJSONLatest
Expand All @@ -176,6 +180,10 @@ func setupGenesis(t *testing.T,
Patch: 5,
})

// initialize the atomic memory
atomicMemory := atomic.NewMemory(prefixdb.New([]byte{0}, baseDBManager.Current().Database))
ctx.SharedMemory = atomicMemory.NewSharedMemory(ctx.ChainID)

// NB: this lock is intentionally left locked when this function returns.
// The caller of this function is responsible for unlocking.
ctx.Lock.Lock()
Expand All @@ -192,7 +200,7 @@ func setupGenesis(t *testing.T,

issuer := make(chan engCommon.Message, 1)
prefixedDBManager := baseDBManager.NewPrefixDBManager([]byte{1})
return ctx, prefixedDBManager, genesisBytes, issuer
return ctx, prefixedDBManager, genesisBytes, issuer, atomicMemory
}

// GenesisVM creates a VM instance with the genesis test bytes and returns
Expand All @@ -209,7 +217,7 @@ func GenesisVM(t *testing.T,
*engCommon.SenderTest,
) {
vm := &VM{}
ctx, dbManager, genesisBytes, issuer := setupGenesis(t, genesisJSON)
ctx, dbManager, genesisBytes, issuer, _ := setupGenesis(t, genesisJSON)
appSender := &engCommon.SenderTest{T: t}
appSender.CantSendAppGossip = true
appSender.SendAppGossipF = func(context.Context, []byte) error { return nil }
Expand Down Expand Up @@ -418,7 +426,7 @@ func TestSubnetEVMUpgradeRequiredAtGenesis(t *testing.T) {
}

for _, test := range genesisTests {
ctx, dbManager, genesisBytes, issuer := setupGenesis(t, test.genesisJSON)
ctx, dbManager, genesisBytes, issuer, _ := setupGenesis(t, test.genesisJSON)
vm := &VM{}
err := vm.Initialize(
context.Background(),
Expand Down Expand Up @@ -568,11 +576,12 @@ func TestBuildEthTxBlock(t *testing.T) {
// then calling SetPreference on block B (when it becomes preferred)
// and the head of a longer chain (block D) does not corrupt the
// canonical chain.
// A
// / \
// B C
// |
// D
//
// A
// / \
// B C
// |
// D
func TestSetPreferenceRace(t *testing.T) {
// Create two VMs which will agree on block A and then
// build the two distinct preferred chains above
Expand Down Expand Up @@ -2036,7 +2045,7 @@ func TestConfigureLogLevel(t *testing.T) {
for _, test := range configTests {
t.Run(test.name, func(t *testing.T) {
vm := &VM{}
ctx, dbManager, genesisBytes, issuer := setupGenesis(t, test.genesisJSON)
ctx, dbManager, genesisBytes, issuer, _ := setupGenesis(t, test.genesisJSON)
appSender := &engCommon.SenderTest{T: t}
appSender.CantSendAppGossip = true
appSender.SendAppGossipF = func(context.Context, []byte) error { return nil }
Expand Down
9 changes: 7 additions & 2 deletions plugin/evm/vm_upgrade_bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,14 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) {
}

// restart the vm
ctx := NewContext()
// Hack: registering metrics uses global variables, so we need to disable metrics here so that we
// can initialize the VM twice.
metrics.Enabled = false
defer func() {
metrics.Enabled = true
}()
if err := vm.Initialize(
context.Background(), ctx, dbManager, []byte(genesisJSONSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*common.Fx{}, appSender,
context.Background(), vm.ctx, dbManager, []byte(genesisJSONSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*common.Fx{}, appSender,
); err != nil {
t.Fatal(err)
}
Expand Down
13 changes: 12 additions & 1 deletion precompile/contract/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package contract
import (
"math/big"

"github.com/ava-labs/avalanchego/chains/atomic"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
"github.com/ava-labs/subnet-evm/commontype"
Expand Down Expand Up @@ -35,12 +37,21 @@ type Predicater interface {
VerifyPredicate(predicateContext *PredicateContext, storageSlots []byte) error
}

type SharedMemoryWriter interface {
AddSharedMemoryRequests(chainID ids.ID, requests *atomic.Requests)
}

type AcceptContext struct {
SnowCtx *snow.Context
SharedMemory SharedMemoryWriter
}

// Accepter is an optional interface for StatefulPrecompiledContracts to implement.
// If implemented, Accept will be called for every log with the address of the precompile when the block is accepted.
// WARNING: this is not intended to be used for custom precompiles. Backwards compatibility with custom precompiles that
// use the Accepter interface will not be supported.
type Accepter interface {
Accept(txHash common.Hash, logIndex int, topics []common.Hash, logData []byte) error
Accept(acceptCtx *AcceptContext, txHash common.Hash, logIndex int, topics []common.Hash, logData []byte) error
}

// ChainContext defines an interface that provides information to a stateful precompile
Expand Down