Skip to content

Commit 881878d

Browse files
committed
feat(cchain): use state summary
1 parent 062e463 commit 881878d

9 files changed

Lines changed: 399 additions & 140 deletions

File tree

vms/saevm/cchain/BUILD.bazel

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ go_library(
2626
"genesis.go",
2727
"gossip.go",
2828
"hooks.go",
29-
"sync.go",
29+
"http.go",
30+
"state.go",
3031
"vm.go",
3132
"warp.go",
3233
],
@@ -78,6 +79,7 @@ go_library(
7879
"//vms/saevm/blocks",
7980
"//vms/saevm/cchain/dynamic",
8081
"//vms/saevm/cchain/state",
82+
"//vms/saevm/cchain/statesync",
8183
"//vms/saevm/cchain/tx",
8284
"//vms/saevm/cchain/txpool",
8385
"//vms/saevm/cchain/warp",
@@ -87,6 +89,7 @@ go_library(
8789
"//vms/saevm/sae",
8890
"//vms/saevm/sae/rpc",
8991
"//vms/saevm/saedb",
92+
"//vms/saevm/statesync",
9093
"//vms/saevm/types",
9194
"//x/blockdb",
9295
"@com_github_ava_labs_libevm//common",
@@ -114,6 +117,7 @@ go_test(
114117
"genesis_test.go",
115118
"gossip_test.go",
116119
"hooks_test.go",
120+
"state_test.go",
117121
"vm_test.go",
118122
"warp_test.go",
119123
],

vms/saevm/cchain/factory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ func (*Factory) New(log logging.Logger) (interface{}, error) {
3131
}
3232
return fullVM{
3333
adaptor.Convert(vm),
34-
adaptor.ConvertStateSync(&syncer{}),
34+
adaptor.ConvertStateSync(vm),
3535
}, nil
3636
}

vms/saevm/cchain/http.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package cchain
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net/http"
10+
"sync"
11+
12+
"github.com/ava-labs/avalanchego/graft/evm/utils/rpc"
13+
"github.com/ava-labs/avalanchego/vms/saevm/sae"
14+
)
15+
16+
const (
17+
avaxServiceName = "avax"
18+
avaxHTTPExtensionPath = "/" + avaxServiceName
19+
)
20+
21+
var handlerPaths = append(sae.HandlerPaths, avaxHTTPExtensionPath)
22+
23+
// CreateHandlers returns the HTTP handlers exposed by the underlying SAE VM
24+
// augmented with the avax service. None of the handlers are usable until after
25+
// the [VM] set as bootstrapping/normal operation.
26+
func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
27+
for _, path := range handlerPaths {
28+
vm.handlers[path] = &lazyHandler{}
29+
}
30+
return vm.handlers.get(), nil
31+
}
32+
33+
func (vm *VM) updateHandlers(ctx context.Context) error {
34+
m, err := vm.VM.CreateHandlers(ctx)
35+
if err != nil {
36+
return fmt.Errorf("creating SAE handlers: %w", err)
37+
}
38+
39+
service, err := newService(vm.ctx, vm.gossipSet, vm.pushGossiper, vm.state)
40+
if err != nil {
41+
return fmt.Errorf("creating avax service: %w", err)
42+
}
43+
handler, err := rpc.NewHandler(avaxServiceName, service)
44+
if err != nil {
45+
return fmt.Errorf("creating avax RPC handler: %w", err)
46+
}
47+
48+
m[avaxHTTPExtensionPath] = handler
49+
50+
return vm.handlers.setHandlers(m)
51+
}
52+
53+
type handlerMap map[string]*lazyHandler
54+
55+
func (m handlerMap) get() map[string]http.Handler {
56+
iface := make(map[string]http.Handler, len(m))
57+
for path, lazy := range m {
58+
iface[path] = lazy
59+
}
60+
return iface
61+
}
62+
63+
func (m handlerMap) setHandlers(actual map[string]http.Handler) error {
64+
for path, h := range actual {
65+
if lazy, ok := m[path]; ok {
66+
lazy.set(h)
67+
}
68+
}
69+
70+
for path := range m {
71+
if _, ok := actual[path]; !ok {
72+
return fmt.Errorf("missing handler path %q", path)
73+
}
74+
}
75+
return nil
76+
}
77+
78+
var _ http.Handler = (*lazyHandler)(nil)
79+
80+
type lazyHandler struct {
81+
mu sync.RWMutex
82+
h http.Handler
83+
}
84+
85+
func (l *lazyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
86+
l.mu.RLock()
87+
h := l.h
88+
l.mu.RUnlock()
89+
90+
if h == nil {
91+
http.NotFound(w, r)
92+
return
93+
}
94+
95+
h.ServeHTTP(w, r)
96+
}
97+
98+
func (l *lazyHandler) set(h http.Handler) {
99+
l.mu.Lock()
100+
l.h = h
101+
l.mu.Unlock()
102+
}

vms/saevm/cchain/state.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package cchain
5+
6+
import (
7+
"context"
8+
9+
"github.com/ava-labs/avalanchego/ids"
10+
"github.com/ava-labs/avalanchego/snow"
11+
"github.com/ava-labs/avalanchego/vms/saevm/blocks"
12+
"github.com/ava-labs/avalanchego/vms/saevm/cchain/statesync"
13+
"github.com/ava-labs/avalanchego/vms/saevm/sae"
14+
15+
snowcommon "github.com/ava-labs/avalanchego/snow/engine/common"
16+
)
17+
18+
func (vm *VM) SetState(ctx context.Context, state snow.State) error {
19+
if state >= snow.Bootstrapping {
20+
var err error
21+
vm.onBootstrappingOnce.Do(func() {
22+
err = vm.onBootstrapping(ctx)
23+
})
24+
if err != nil {
25+
return err
26+
}
27+
28+
if err := vm.VM.SetState(ctx, state); err != nil {
29+
return err
30+
}
31+
}
32+
33+
vm.mode.Set(state)
34+
return nil
35+
}
36+
37+
var (
38+
_ StateDependent = (*sae.VM)(nil)
39+
_ StateDependent = (*statesync.SummaryHandler)(nil)
40+
)
41+
42+
type StateDependent interface {
43+
GetBlock(context.Context, ids.ID) (*blocks.Block, error)
44+
GetBlockIDAtHeight(context.Context, uint64) (ids.ID, error)
45+
LastAccepted(context.Context) (ids.ID, error)
46+
}
47+
48+
func (vm *VM) activeHandler() StateDependent {
49+
if vm.mode.Get() >= snow.Bootstrapping {
50+
return vm.VM
51+
}
52+
return vm.SummaryHandler
53+
}
54+
55+
func (vm *VM) GetBlock(ctx context.Context, id ids.ID) (*blocks.Block, error) {
56+
return vm.activeHandler().GetBlock(ctx, id)
57+
}
58+
59+
func (vm *VM) GetBlockIDAtHeight(ctx context.Context, height uint64) (ids.ID, error) {
60+
return vm.activeHandler().GetBlockIDAtHeight(ctx, height)
61+
}
62+
63+
func (vm *VM) LastAccepted(ctx context.Context) (ids.ID, error) {
64+
return vm.activeHandler().LastAccepted(ctx)
65+
}
66+
67+
func (vm *VM) WaitForEvent(ctx context.Context) (snowcommon.Message, error) {
68+
if vm.mode.Get() == snow.StateSyncing {
69+
return vm.SummaryHandler.WaitForEvent(ctx)
70+
}
71+
72+
// TODO(StephenButtolph): Do not busy loop with [snowcommon.PendingTxs]. The
73+
// txpools are cleared after block execution, so we may still have
74+
// transactions in the txpool while blocks containing those transactions are
75+
// processing.
76+
77+
// TODO(StephenButtolph): Wait until the minimum block delay has passed.
78+
79+
ctx, cancel := context.WithCancel(ctx)
80+
type result struct {
81+
msg snowcommon.Message
82+
err error
83+
}
84+
results := make(chan result, 2)
85+
go func() {
86+
defer cancel()
87+
msg, err := vm.VM.WaitForEvent(ctx)
88+
results <- result{msg, err}
89+
}()
90+
go func() {
91+
defer cancel()
92+
err := vm.txpool.AwaitTxs(ctx)
93+
results <- result{snowcommon.PendingTxs, err}
94+
}()
95+
96+
r := <-results
97+
return r.msg, r.err
98+
}

vms/saevm/cchain/state_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package cchain
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
12+
"github.com/ava-labs/avalanchego/database/memdb"
13+
"github.com/ava-labs/avalanchego/snow"
14+
"github.com/ava-labs/avalanchego/vms/saevm/blocks"
15+
"github.com/ava-labs/avalanchego/vms/saevm/cchain/tx/txtest"
16+
)
17+
18+
func TestConsensusGettersAfterRestart(t *testing.T) {
19+
key := txtest.NewKey(t)
20+
alloc := withMaxAllocFor(key.EthAddress())
21+
db := memdb.New()
22+
23+
ctx, node := newSUT(t, alloc, withDB(db))
24+
w := newWallet(key, node.ctx, node.Client)
25+
26+
genesis, err := node.GetBlock(ctx, node.lastAccepted(ctx, t))
27+
require.NoErrorf(t, err, "%T.GetBlock()", node.VM)
28+
const numBlocks = 3
29+
want := make([]*blocks.Block, 0, numBlocks+1)
30+
want = append(want, genesis)
31+
for range numBlocks {
32+
blk := node.issueAndExecute(ctx, t, w.newMinimalTx(t))
33+
want = append(want, blk)
34+
}
35+
require.NoErrorf(t, node.Shutdown(ctx), "%T.Shutdown()", node.VM)
36+
37+
for _, state := range []snow.State{
38+
snow.StateSyncing,
39+
snow.Bootstrapping,
40+
snow.NormalOp,
41+
} {
42+
t.Run(state.String(), func(t *testing.T) {
43+
ctx, s := newSUT(t, alloc, withDB(db), withState(state))
44+
45+
wantLastAccepted := want[len(want)-1].ID()
46+
gotLastAccepted, err := s.LastAccepted(ctx)
47+
require.NoErrorf(t, err, "%T.LastAccepted()", s)
48+
assert.Equalf(t, wantLastAccepted, gotLastAccepted, "%T.LastAccepted()", s)
49+
50+
for _, b := range want {
51+
gotID, err := s.GetBlockIDAtHeight(ctx, b.Height())
52+
require.NoErrorf(t, err, "%T.GetBlockIDAtHeight(%d)", s, b.Height())
53+
assert.Equalf(t, b.ID(), gotID, "%T.GetBlockIDAtHeight(%d)", s, b.Height())
54+
55+
gotBlock, err := s.GetBlock(ctx, b.ID())
56+
require.NoErrorf(t, err, "%T.GetBlock(%s)", s, b.ID())
57+
assert.Equalf(t, b.ID(), gotBlock.ID(), "%T.GetBlock(%s).ID()", s, b.ID())
58+
assert.Equalf(t, b.Height(), gotBlock.Height(), "%T.GetBlock(%s).Height()", s, b.ID())
59+
}
60+
})
61+
}
62+
}
63+
64+
func TestGetGenesisNoState(t *testing.T) {
65+
ctx, sut := newSUT(t, withState(snow.StateSyncing))
66+
hash, err := sut.LastAccepted(ctx)
67+
require.NoErrorf(t, err, "%T.LastAccepted()", sut)
68+
require.NotZero(t, hash)
69+
70+
gotID, err := sut.GetBlockIDAtHeight(ctx, 0)
71+
require.NoErrorf(t, err, "%T.GetBlockIDAtHeight(%d)", sut, 0)
72+
assert.Equalf(t, hash, gotID, "%T.GetBlockIDAtHeight(%d)", sut, 0)
73+
74+
gotBlock, err := sut.GetBlock(ctx, hash)
75+
require.NoErrorf(t, err, "%T.GetBlock(%s)", sut, hash)
76+
assert.Equalf(t, hash, gotBlock.ID(), "%T.GetBlock(%s).ID()", sut, hash)
77+
assert.Equalf(t, uint64(0), gotBlock.Height(), "%T.GetBlock(%s).Height()", sut, hash)
78+
}

vms/saevm/cchain/sync.go

Lines changed: 0 additions & 53 deletions
This file was deleted.

0 commit comments

Comments
 (0)