Skip to content

Commit

Permalink
Merge branch 'uptime-tracking-api' into sign-validator-uptime-warp-msg
Browse files Browse the repository at this point in the history
  • Loading branch information
ceyonur authored Nov 13, 2024
2 parents ed65695 + a8abbc3 commit 1067ebd
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 90 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.8
require (
github.com/VictoriaMetrics/fastcache v1.12.1
github.com/antithesishq/antithesis-sdk-go v0.3.8
github.com/ava-labs/avalanchego v1.12.0-initial-poc.8.0.20241109084237-90fa892673d1
github.com/ava-labs/avalanchego v1.11.13-0.20241113171850-4c199890fadb
github.com/cespare/cp v0.1.0
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
github.com/davecgh/go-spew v1.1.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/ava-labs/avalanchego v1.12.0-initial-poc.8.0.20241109084237-90fa892673d1 h1:U+hV28K+PfNP4n69bGUrgNbS446dtWwD3zhGL4HZVPc=
github.com/ava-labs/avalanchego v1.12.0-initial-poc.8.0.20241109084237-90fa892673d1/go.mod h1:86tO6F1FT8emclUwdQ2WCwAtAerqjm5A4IbV6XxNUyM=
github.com/ava-labs/avalanchego v1.11.13-0.20241113171850-4c199890fadb h1:ujIv6zAKQpZtlalLGSt9cx2AREJmYcv0wMmXGRAH6/g=
github.com/ava-labs/avalanchego v1.11.13-0.20241113171850-4c199890fadb/go.mod h1:86tO6F1FT8emclUwdQ2WCwAtAerqjm5A4IbV6XxNUyM=
github.com/ava-labs/coreth v0.13.8 h1:f14X3KgwHl9LwzfxlN6S4bbn5VA2rhEsNnHaRLSTo/8=
github.com/ava-labs/coreth v0.13.8/go.mod h1:t3BSv/eQv0AlDPMfEDCMMoD/jq1RkUsbFzQAFg5qBcE=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
Expand Down
28 changes: 6 additions & 22 deletions plugin/evm/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,9 @@ func (b *Block) Accept(context.Context) error {

// Call Accept for relevant precompile logs. Note we do this prior to
// calling Accept on the blockChain so any side effects (eg warp signatures)
// take place before the accepted log is emitted to subscribers. Use of the
// sharedMemoryWriter ensures shared memory requests generated by
// precompiles are committed atomically with the vm's lastAcceptedKey.
// take place before the accepted log is emitted to subscribers.
rules := b.vm.chainConfig.Rules(b.ethBlock.Number(), b.ethBlock.Timestamp())
sharedMemoryWriter := NewSharedMemoryWriter()
if err := b.handlePrecompileAccept(rules, sharedMemoryWriter); err != nil {
if err := b.handlePrecompileAccept(rules); err != nil {
return err
}
if err := vm.blockChain.Accept(b.ethBlock); err != nil {
Expand All @@ -77,24 +74,12 @@ func (b *Block) Accept(context.Context) error {
return fmt.Errorf("failed to put %s as the last accepted block: %w", b.ID(), err)
}

// Get pending operations on the vm's versionDB so we can apply them atomically
// with the shared memory requests.
vdbBatch, err := b.vm.db.CommitBatch()
if err != nil {
return fmt.Errorf("could not create commit batch processing block[%s]: %w", b.ID(), 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)
return b.vm.db.Commit()
}

// 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(rules params.Rules, sharedMemoryWriter *sharedMemoryWriter) error {
func (b *Block) handlePrecompileAccept(rules params.Rules) error {
// Short circuit early if there are no precompile accepters to execute
if len(rules.AccepterPrecompiles) == 0 {
return nil
Expand All @@ -108,9 +93,8 @@ func (b *Block) handlePrecompileAccept(rules params.Rules, sharedMemoryWriter *s
return fmt.Errorf("failed to fetch receipts for accepted block with non-empty root hash (%s) (Block: %s, Height: %d)", b.ethBlock.ReceiptHash(), b.ethBlock.Hash(), b.ethBlock.NumberU64())
}
acceptCtx := &precompileconfig.AcceptContext{
SnowCtx: b.vm.ctx,
SharedMemory: sharedMemoryWriter,
Warp: b.vm.warpBackend,
SnowCtx: b.vm.ctx,
Warp: b.vm.warpBackend,
}
for _, receipt := range receipts {
for logIdx, log := range receipt.Logs {
Expand Down
2 changes: 1 addition & 1 deletion plugin/evm/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,5 @@ func TestHandlePrecompileAccept(t *testing.T) {
precompileAddr: mockAccepter,
},
}
require.NoError(blk.handlePrecompileAccept(rules, nil))
require.NoError(blk.handlePrecompileAccept(rules))
}
37 changes: 0 additions & 37 deletions plugin/evm/shared_memory_writer.go

This file was deleted.

4 changes: 1 addition & 3 deletions plugin/evm/syncervm_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func (client *stateSyncerClient) Shutdown() error {
}

// finishSync is responsible for updating disk and memory pointers so the VM is prepared
// for bootstrapping. Executes any shared memory operations from the atomic trie to shared memory.
// for bootstrapping.
func (client *stateSyncerClient) finishSync() error {
stateBlock, err := client.state.GetBlock(context.TODO(), ids.ID(client.syncSummary.BlockHash))
if err != nil {
Expand Down Expand Up @@ -349,8 +349,6 @@ func (client *stateSyncerClient) finishSync() error {

// updateVMMarkers updates the following markers in the VM's database
// and commits them atomically:
// - updates atomic trie so it will have necessary metadata for the last committed root
// - updates atomic trie so it will resume applying operations to shared memory on initialize
// - updates lastAcceptedKey
// - removes state sync progress markers
func (client *stateSyncerClient) updateVMMarkers() error {
Expand Down
3 changes: 2 additions & 1 deletion plugin/evm/uptime/pausable_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ func TestPausableManager(t *testing.T) {

// Elapse Time
addTime(clk, time.Second)
// The node was paused before we started tracking
// Since we have not started tracking this node yet, its observed uptime should
// be incremented even though it is actually paused.
expectedUptime += 1 * time.Second

// Start tracking
Expand Down
14 changes: 6 additions & 8 deletions plugin/evm/validators/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,10 @@ func (v *validatorData) getStartTime() time.Time {
}

// constantsAreUnmodified returns true if the constants of this validator have
// not been modified compared to the other validator.
func (v *validatorData) constantsAreUnmodified(o interfaces.Validator) bool {
if v.validationID != o.ValidationID {
return true
}
return v.NodeID == o.NodeID &&
v.IsSoV == o.IsSoV &&
v.StartTime == o.StartTimestamp
// not been modified compared to the updated validator.
func (v *validatorData) constantsAreUnmodified(u interfaces.Validator) bool {
return v.validationID == u.ValidationID &&
v.NodeID == u.NodeID &&
v.IsSoV == u.IsSoV &&
v.StartTime == u.StartTimestamp
}
13 changes: 13 additions & 0 deletions plugin/evm/validators/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ func TestState(t *testing.T) {
vdr.NodeID = newNodeID
require.ErrorIs(state.UpdateValidator(vdr), ErrImmutableField)

// set a different start time should fail
newStartTime := vdr.StartTime().Add(time.Hour)
vdr.StartTimestamp = uint64(newStartTime.Unix())
require.ErrorIs(state.UpdateValidator(vdr), ErrImmutableField)

// set SoV should fail
vdr.IsSoV = false
require.ErrorIs(state.UpdateValidator(vdr), ErrImmutableField)

// set validation ID should result in not found
vdr.ValidationID = ids.GenerateTestID()
require.ErrorIs(state.UpdateValidator(vdr), database.ErrNotFound)

// delete uptime
require.NoError(state.DeleteValidator(vID))

Expand Down
7 changes: 4 additions & 3 deletions plugin/evm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,9 @@ func (vm *VM) onNormalOperationsStarted() error {
return fmt.Errorf("failed to update validators: %w", err)
}
vdrIDs := vm.validatorState.GetNodeIDs().List()
// then start tracking with updated validators
// Then start tracking with updated validators
// StartTracking initializes the uptime tracking with the known validators
// and update their uptime to account for the time we were being offline.
if err := vm.uptimeManager.StartTracking(vdrIDs); err != nil {
return fmt.Errorf("failed to start tracking uptime: %w", err)
}
Expand Down Expand Up @@ -1326,8 +1328,7 @@ func (vm *VM) initializeDBs(avaDB database.Database) error {
// skip standalone database initialization if we are running in unit tests
if vm.ctx.NetworkID != avalancheconstants.UnitTestID {
// first initialize the accepted block database to check if we need to use a standalone database
verDB := versiondb.New(avaDB)
acceptedDB := prefixdb.New(acceptedPrefix, verDB)
acceptedDB := prefixdb.New(acceptedPrefix, avaDB)
useStandAloneDB, err := vm.useStandaloneDatabase(acceptedDB)
if err != nil {
return err
Expand Down
62 changes: 62 additions & 0 deletions plugin/evm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/api/keystore"
Expand Down Expand Up @@ -3111,3 +3112,64 @@ func TestParentBeaconRootBlock(t *testing.T) {
})
}
}

func TestStandaloneDB(t *testing.T) {
vm := &VM{}
ctx := utils.TestSnowContext()
baseDB := memdb.New()
atomicMemory := atomic.NewMemory(prefixdb.New([]byte{0}, baseDB))
ctx.SharedMemory = atomicMemory.NewSharedMemory(ctx.ChainID)
issuer := make(chan commonEng.Message, 1)
sharedDB := prefixdb.New([]byte{1}, baseDB)
genesisBytes := buildGenesisTest(t, genesisJSONLatest)
// alter network ID to use standalone database
ctx.NetworkID = 123456
appSender := &enginetest.Sender{T: t}
appSender.CantSendAppGossip = true
appSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { return nil }
configJSON := `{"database-type": "memdb"}`

isDBEmpty := func(db database.Database) bool {
it := db.NewIterator()
defer it.Release()
return !it.Next()
}
// Ensure that the database is empty
require.True(t, isDBEmpty(baseDB))

err := vm.Initialize(
context.Background(),
ctx,
sharedDB,
genesisBytes,
nil,
[]byte(configJSON),
issuer,
[]*commonEng.Fx{},
appSender,
)
defer vm.Shutdown(context.Background())
require.NoError(t, err, "error initializing VM")
require.NoError(t, vm.SetState(context.Background(), snow.Bootstrapping))
require.NoError(t, vm.SetState(context.Background(), snow.NormalOp))

// Issue a block
acceptedBlockEvent := make(chan core.ChainEvent, 1)
vm.blockChain.SubscribeChainAcceptedEvent(acceptedBlockEvent)
tx0 := types.NewTransaction(uint64(0), testEthAddrs[0], big.NewInt(1), 21000, big.NewInt(testMinGasPrice), nil)
signedTx0, err := types.SignTx(tx0, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0])
require.NoError(t, err)
errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0})
require.NoError(t, errs[0])

// accept block
blk := issueAndAccept(t, issuer, vm)
newBlock := <-acceptedBlockEvent
require.Equal(t, newBlock.Block.Hash(), common.Hash(blk.ID()))

// Ensure that the shared database is empty
assert.True(t, isDBEmpty(baseDB))
// Ensure that the standalone database is not empty
assert.False(t, isDBEmpty(vm.db))
assert.False(t, isDBEmpty(vm.acceptedBlockDB))
}
13 changes: 2 additions & 11 deletions precompile/precompileconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
package precompileconfig

import (
"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/avalanchego/vms/platformvm/warp"
Expand Down Expand Up @@ -54,21 +52,14 @@ type Predicater interface {
VerifyPredicate(predicateContext *PredicateContext, predicateBytes []byte) error
}

// SharedMemoryWriter defines an interface to allow a precompile's Accepter to write operations
// into shared memory to be committed atomically on block accept.
type SharedMemoryWriter interface {
AddSharedMemoryRequests(chainID ids.ID, requests *atomic.Requests)
}

type WarpMessageWriter interface {
AddMessage(unsignedMessage *warp.UnsignedMessage) error
}

// AcceptContext defines the context passed in to a precompileconfig's Accepter
type AcceptContext struct {
SnowCtx *snow.Context
SharedMemory SharedMemoryWriter
Warp WarpMessageWriter
SnowCtx *snow.Context
Warp WarpMessageWriter
}

// Accepter is an optional interface for StatefulPrecompiledContracts to implement.
Expand Down
2 changes: 1 addition & 1 deletion scripts/versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# shellcheck disable=SC2034

# Don't export them as they're used in the context of other calls
AVALANCHE_VERSION=${AVALANCHE_VERSION:-'90fa8926'}
AVALANCHE_VERSION=${AVALANCHE_VERSION:-'4c199890'}
GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'}

# This won't be used, but it's here to make code syncs easier
Expand Down

0 comments on commit 1067ebd

Please sign in to comment.