Skip to content

Commit

Permalink
use first applied block in epoch for new miner active set (#4997)
Browse files Browse the repository at this point in the history
## Motivation
Closes #4912 

## Changes
- for new miner use the first applied block for ref ballot active set
  • Loading branch information
countvonzero committed Sep 13, 2023
1 parent a1412f7 commit 5391392
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 38 deletions.
2 changes: 1 addition & 1 deletion hare/eligibility/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ func (o *Oracle) computeActiveSet(ctx context.Context, targetEpoch types.EpochID
return activeSet, nil
}

activeSet, err := miner.ActiveSetFromEpochFirstBlock(o.cdb, targetEpoch)
activeSet, err := miner.ActiveSetFromEpochFirstCertifiedBlock(o.cdb, targetEpoch)
if err != nil && !errors.Is(err, sql.ErrNotFound) {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions miner/oracle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
"github.com/spacemeshos/go-spacemesh/sql/atxs"
"github.com/spacemeshos/go-spacemesh/sql/ballots"
"github.com/spacemeshos/go-spacemesh/sql/blocks"
"github.com/spacemeshos/go-spacemesh/sql/certificates"
"github.com/spacemeshos/go-spacemesh/sql/identities"
"github.com/spacemeshos/go-spacemesh/sql/layers"
"github.com/spacemeshos/go-spacemesh/system/mocks"
"github.com/spacemeshos/go-spacemesh/tortoise"
)
Expand Down Expand Up @@ -427,7 +427,7 @@ func TestOracle_NewNode(t *testing.T) {
}
block.Initialize()
require.NoError(t, blocks.Add(o.cdb, block))
require.NoError(t, certificates.Add(o.cdb, lid.GetEpoch().FirstLayer(), &types.Certificate{BlockID: block.ID()}))
require.NoError(t, layers.SetApplied(o.cdb, lid, block.ID()))

epoch := types.EpochID(2)
var ownAtx types.ATXID
Expand Down
14 changes: 13 additions & 1 deletion miner/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@ import (
"github.com/spacemeshos/go-spacemesh/sql/ballots"
"github.com/spacemeshos/go-spacemesh/sql/blocks"
"github.com/spacemeshos/go-spacemesh/sql/certificates"
"github.com/spacemeshos/go-spacemesh/sql/layers"
)

func ActiveSetFromEpochFirstBlock(db sql.Executor, epoch types.EpochID) ([]types.ATXID, error) {
func ActiveSetFromEpochFirstCertifiedBlock(db sql.Executor, epoch types.EpochID) ([]types.ATXID, error) {
bid, err := certificates.FirstInEpoch(db, epoch)
if err != nil {
return nil, err
}
return activeSetFromBlock(db, bid)
}

func ActiveSetFromEpochFirstBlock(db sql.Executor, epoch types.EpochID) ([]types.ATXID, error) {
bid, err := layers.FirstAppliedInEpoch(db, epoch)
if err != nil {
return nil, err
}
return activeSetFromBlock(db, bid)
}

func activeSetFromBlock(db sql.Executor, bid types.BlockID) ([]types.ATXID, error) {
block, err := blocks.Get(db, bid)
if err != nil {
return nil, fmt.Errorf("actives get block: %w", err)
Expand Down
111 changes: 77 additions & 34 deletions miner/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,87 @@ import (
"github.com/spacemeshos/go-spacemesh/sql"
"github.com/spacemeshos/go-spacemesh/sql/blocks"
"github.com/spacemeshos/go-spacemesh/sql/certificates"
"github.com/spacemeshos/go-spacemesh/sql/layers"
)

func TestActiveSetFromEpochFirstBlock(t *testing.T) {
epoch := types.EpochID(3)
cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(t))
for _, tc := range []struct {
desc string
miner, hare bool
err error
}{
{
desc: "actives for miner",
miner: true,
},
{
desc: "no actives for miner",
miner: true,
err: sql.ErrNotFound,
},
{
desc: "actives for hare",
hare: true,
},
{
desc: "no actives for hare",
hare: true,
err: sql.ErrNotFound,
},
} {
t.Run(tc.desc, func(t *testing.T) {
epoch := types.EpochID(3)
cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(t))

got, err := ActiveSetFromEpochFirstBlock(cdb, epoch)
require.ErrorIs(t, err, sql.ErrNotFound)
require.Nil(t, got)
got, err := ActiveSetFromEpochFirstCertifiedBlock(cdb, epoch)
require.ErrorIs(t, err, sql.ErrNotFound)
require.Nil(t, got)

var expected []types.ATXID
for i := uint32(0); i < layersPerEpoch; i++ {
lid := epoch.FirstLayer() + types.LayerID(i)
all := types.RandomActiveSet(10)
blts := createBallots(t, cdb, lid, 5, all)
block := &types.Block{
InnerBlock: types.InnerBlock{
LayerIndex: lid,
},
}
for _, b := range blts {
block.Rewards = append(block.Rewards, types.AnyReward{AtxID: b.AtxID})
all = append(all, b.AtxID)
}
block.Initialize()
require.NoError(t, blocks.Add(cdb, block))
require.NoError(t, certificates.Add(cdb, lid, &types.Certificate{BlockID: block.ID()}))
if i == 0 {
expected = all
}
for _, id := range all {
signer, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, cdb, id, (epoch - 1).FirstLayer(), signer, time.Now())
}
}
var expected []types.ATXID
for i := uint32(0); i < layersPerEpoch; i++ {
lid := epoch.FirstLayer() + types.LayerID(i)
all := types.RandomActiveSet(10)
blts := createBallots(t, cdb, lid, 5, all)
block := &types.Block{
InnerBlock: types.InnerBlock{
LayerIndex: lid,
},
}
for _, b := range blts {
block.Rewards = append(block.Rewards, types.AnyReward{AtxID: b.AtxID})
all = append(all, b.AtxID)
}
block.Initialize()
require.NoError(t, blocks.Add(cdb, block))
if tc.err == nil {
if tc.miner {
require.NoError(t, layers.SetApplied(cdb, lid, block.ID()))
}
if tc.hare {
require.NoError(t, certificates.Add(cdb, lid, &types.Certificate{BlockID: block.ID()}))
}
}
if i == 0 {
expected = all
}
for _, id := range all {
signer, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, cdb, id, (epoch - 1).FirstLayer(), signer, time.Now())
}
}

got, err = ActiveSetFromEpochFirstBlock(cdb, epoch)
require.NoError(t, err)
require.ElementsMatch(t, expected, got)
if tc.miner {
got, err = ActiveSetFromEpochFirstBlock(cdb, epoch)
} else if tc.hare {
got, err = ActiveSetFromEpochFirstCertifiedBlock(cdb, epoch)
}
if tc.err != nil {
require.ErrorIs(t, err, tc.err)
} else {
require.NoError(t, err)
require.ElementsMatch(t, expected, got)
}
})
}
}
24 changes: 24 additions & 0 deletions sql/layers/layers.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,30 @@ func GetApplied(db sql.Executor, lid types.LayerID) (rst types.BlockID, err erro
return rst, err
}

func FirstAppliedInEpoch(db sql.Executor, epoch types.EpochID) (types.BlockID, error) {
var (
result types.BlockID
err error
rows int
)
if rows, err = db.Exec(`
select applied_block from layers
where id between ?1 and ?2 and applied_block is not null and applied_block != ?3
order by id asc limit 1;`, func(stmt *sql.Statement) {
stmt.BindInt64(1, int64(epoch.FirstLayer()))
stmt.BindInt64(2, int64((epoch+1).FirstLayer()-1))
stmt.BindBytes(3, types.EmptyBlockID[:])
}, func(stmt *sql.Statement) bool {
stmt.ColumnBytes(0, result[:])
return true
}); err != nil {
return types.EmptyBlockID, fmt.Errorf("FirstAppliedInEpoch %s: %w", epoch, err)
} else if rows == 0 {
return types.EmptyBlockID, fmt.Errorf("FirstAppliedInEpoch %s: %w", epoch, sql.ErrNotFound)
}
return result, nil
}

// GetLastApplied for the applied block for layer.
func GetLastApplied(db sql.Executor) (types.LayerID, error) {
var lid types.LayerID
Expand Down
50 changes: 50 additions & 0 deletions sql/layers/layers_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package layers

import (
"os"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -9,6 +10,15 @@ import (
"github.com/spacemeshos/go-spacemesh/sql"
)

const layersPerEpoch = 4

func TestMain(m *testing.M) {
types.SetLayersPerEpoch(layersPerEpoch)

res := m.Run()
os.Exit(res)
}

func TestWeakCoin(t *testing.T) {
db := sql.InMemory()
lid := types.LayerID(10)
Expand Down Expand Up @@ -56,6 +66,46 @@ func TestAppliedBlock(t *testing.T) {
require.ErrorIs(t, err, sql.ErrNotFound)
}

func TestFirstAppliedInEpoch(t *testing.T) {
db := sql.InMemory()
blks := map[types.LayerID]types.BlockID{
types.EpochID(1).FirstLayer(): {1},
types.EpochID(2).FirstLayer(): types.EmptyBlockID,
types.EpochID(2).FirstLayer() + 1: {2},
types.EpochID(3).FirstLayer() + 1: {3},
}
for _, epoch := range []types.EpochID{0, 1, 2, 3} {
// cause layer to be inserted
for j := 0; j < layersPerEpoch; j++ {
lid := epoch.FirstLayer() + types.LayerID(j)
require.NoError(t, SetWeakCoin(db, lid, false))
}
}
for lid, bid := range blks {
require.NoError(t, SetApplied(db, lid, bid))
}

got, err := FirstAppliedInEpoch(db, types.EpochID(0))
require.ErrorIs(t, err, sql.ErrNotFound)
require.Equal(t, types.EmptyBlockID, got)

got, err = FirstAppliedInEpoch(db, types.EpochID(1))
require.NoError(t, err)
require.Equal(t, types.BlockID{1}, got)

got, err = FirstAppliedInEpoch(db, types.EpochID(2))
require.NoError(t, err)
require.Equal(t, types.BlockID{2}, got)

got, err = FirstAppliedInEpoch(db, types.EpochID(3))
require.NoError(t, err)
require.Equal(t, types.BlockID{3}, got)

got, err = FirstAppliedInEpoch(db, types.EpochID(4))
require.ErrorIs(t, err, sql.ErrNotFound)
require.Equal(t, types.EmptyBlockID, got)
}

func TestUnsetAppliedFrom(t *testing.T) {
db := sql.InMemory()
lid := types.LayerID(10)
Expand Down

0 comments on commit 5391392

Please sign in to comment.