Skip to content

Commit

Permalink
vm, api: add smesherID to rewards (#5199)
Browse files Browse the repository at this point in the history
## Motivation

See #4863

Based on #4850

Closes #4529
Closes #4850 
Closes #4863
Closes #4964
Closes #5183

## Changes
- Refactors `rewards` table. Adds smesherID column, migrates data. Does not update old data (smesherID will be NULL for old data, unless resynced).
- Updates database queries.
- Returns smesherID in all API queries



Co-authored-by: Piers Powlesland <[email protected]>
Co-authored-by: piersy <[email protected]>
Co-authored-by: Dmitry Shulyak <[email protected]>
  • Loading branch information
3 people committed Dec 2, 2023
1 parent 3ae2bf3 commit d424b78
Show file tree
Hide file tree
Showing 23 changed files with 496 additions and 151 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ See [RELEASE](./RELEASE.md) for workflow instructions.

### Improvements

* [#5199](https://github.com/spacemeshos/go-spacemesh/pull/5199) Adds smesherID to rewards table. Historically rewards
were keyed by (coinbase, layer). Now the primary key has changed to (smesherID, layer), which allows querying rewards
by any subset of layer, smesherID, and coinbase. While this change does add smesherID to existing API endpoints
(`GlobalStateService.{AccountDataQuery,AccountDataStream,GlobalStateStream}`), it does not yet expose an endpoint to
query rewards by smesherID. Additionally, it does not re-index old data. Rewards will contain smesherID going forward,
but to refresh data for all rewards, a node will have to delete its database and resync from genesis.

## 1.3.0

### Upgrade information
Expand Down
20 changes: 7 additions & 13 deletions api/grpcserver/globalstate_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func (s GlobalStateService) AccountDataQuery(
// if filterTxReceipt {}

if filterReward {
dbRewards, err := s.mesh.GetRewards(addr)
dbRewards, err := s.mesh.GetRewardsByCoinbase(addr)
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting rewards data")
}
Expand All @@ -153,10 +153,8 @@ func (s GlobalStateService) AccountDataQuery(
Layer: &pb.LayerNumber{Number: r.Layer.Uint32()},
Total: &pb.Amount{Value: r.TotalReward},
LayerReward: &pb.Amount{Value: r.LayerReward},
// Leave this out for now as this is changing
// See https://github.com/spacemeshos/go-spacemesh/issues/2275
// LayerComputed: 0,
Coinbase: &pb.AccountId{Address: addr.String()},
Coinbase: &pb.AccountId{Address: addr.String()},
Smesher: &pb.SmesherId{Id: r.SmesherID[:]},
},
}})
}
Expand Down Expand Up @@ -291,10 +289,8 @@ func (s GlobalStateService) AccountDataStream(
Layer: &pb.LayerNumber{Number: reward.Layer.Uint32()},
Total: &pb.Amount{Value: reward.Total},
LayerReward: &pb.Amount{Value: reward.LayerReward},
// Leave this out for now as this is changing
// See https://github.com/spacemeshos/go-spacemesh/issues/2275
// LayerComputed: 0,
Coinbase: &pb.AccountId{Address: addr.String()},
Coinbase: &pb.AccountId{Address: addr.String()},
Smesher: &pb.SmesherId{Id: reward.SmesherID[:]},
},
}}}
if err := stream.Send(resp); err != nil {
Expand Down Expand Up @@ -424,10 +420,8 @@ func (s GlobalStateService) GlobalStateStream(
Layer: &pb.LayerNumber{Number: reward.Layer.Uint32()},
Total: &pb.Amount{Value: reward.Total},
LayerReward: &pb.Amount{Value: reward.LayerReward},
// Leave this out for now as this is changing
// See https://github.com/spacemeshos/go-spacemesh/issues/2275
// LayerComputed: 0,
Coinbase: &pb.AccountId{Address: reward.Coinbase.String()},
Coinbase: &pb.AccountId{Address: reward.Coinbase.String()},
Smesher: &pb.SmesherId{Id: reward.SmesherID[:]},
},
}}}
if err := stream.Send(resp); err != nil {
Expand Down
10 changes: 6 additions & 4 deletions api/grpcserver/globalstate_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestGlobalStateService(t *testing.T) {
t.Parallel()
c, ctx := setupGlobalStateService(t)

c.meshAPI.EXPECT().GetRewards(addr1).Return([]*types.Reward{
c.meshAPI.EXPECT().GetRewardsByCoinbase(addr1).Return([]*types.Reward{
{
Layer: layerFirst,
TotalReward: rewardAmount,
Expand Down Expand Up @@ -130,7 +130,7 @@ func TestGlobalStateService(t *testing.T) {
t.Parallel()
c, ctx := setupGlobalStateService(t)

c.meshAPI.EXPECT().GetRewards(addr1).Return([]*types.Reward{
c.meshAPI.EXPECT().GetRewardsByCoinbase(addr1).Return([]*types.Reward{
{
Layer: layerFirst,
TotalReward: rewardAmount,
Expand Down Expand Up @@ -159,12 +159,13 @@ func TestGlobalStateService(t *testing.T) {
t.Parallel()
c, ctx := setupGlobalStateService(t)

c.meshAPI.EXPECT().GetRewards(addr1).Return([]*types.Reward{
c.meshAPI.EXPECT().GetRewardsByCoinbase(addr1).Return([]*types.Reward{
{
Layer: layerFirst,
TotalReward: rewardAmount,
LayerReward: rewardAmount,
Coinbase: addr1,
SmesherID: rewardSmesherID,
},
}, nil)
c.conStateAPI.EXPECT().GetBalance(addr1).Return(accountBalance, nil)
Expand All @@ -188,12 +189,13 @@ func TestGlobalStateService(t *testing.T) {
t.Parallel()
c, ctx := setupGlobalStateService(t)

c.meshAPI.EXPECT().GetRewards(addr1).Return([]*types.Reward{
c.meshAPI.EXPECT().GetRewardsByCoinbase(addr1).Return([]*types.Reward{
{
Layer: layerFirst,
TotalReward: rewardAmount,
LayerReward: rewardAmount,
Coinbase: addr1,
SmesherID: rewardSmesherID,
},
}, nil)
c.conStateAPI.EXPECT().GetBalance(addr1).Return(accountBalance, nil)
Expand Down
56 changes: 37 additions & 19 deletions api/grpcserver/grpcserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,24 @@ var (
postGenesisEpoch = types.EpochID(2)
genesisID = types.Hash20{}

addr1 types.Address
addr2 types.Address
prevAtxID = types.ATXID(types.HexToHash32("44444"))
chlng = types.HexToHash32("55555")
poetRef = []byte("66666")
nipost = newNIPostWithChallenge(&chlng, poetRef)
challenge = newChallenge(1, prevAtxID, prevAtxID, postGenesisEpoch)
globalAtx *types.VerifiedActivationTx
globalAtx2 *types.VerifiedActivationTx
globalTx *types.Transaction
globalTx2 *types.Transaction
ballot1 = genLayerBallot(types.LayerID(11))
block1 = genLayerBlock(types.LayerID(11), nil)
block2 = genLayerBlock(types.LayerID(11), nil)
block3 = genLayerBlock(types.LayerID(11), nil)
meshAPIMock = &MeshAPIMock{}
conStateAPI = &ConStateAPIMock{
addr1 types.Address
addr2 types.Address
rewardSmesherID = types.RandomNodeID()
prevAtxID = types.ATXID(types.HexToHash32("44444"))
chlng = types.HexToHash32("55555")
poetRef = []byte("66666")
nipost = newNIPostWithChallenge(&chlng, poetRef)
challenge = newChallenge(1, prevAtxID, prevAtxID, postGenesisEpoch)
globalAtx *types.VerifiedActivationTx
globalAtx2 *types.VerifiedActivationTx
globalTx *types.Transaction
globalTx2 *types.Transaction
ballot1 = genLayerBallot(types.LayerID(11))
block1 = genLayerBlock(types.LayerID(11), nil)
block2 = genLayerBlock(types.LayerID(11), nil)
block3 = genLayerBlock(types.LayerID(11), nil)
meshAPIMock = &MeshAPIMock{}
conStateAPI = &ConStateAPIMock{
returnTx: make(map[types.TransactionID]*types.Transaction),
layerApplied: make(map[types.TransactionID]*types.LayerID),
balances: make(map[types.Address]*big.Int),
Expand Down Expand Up @@ -260,13 +261,26 @@ func (m *MeshAPIMock) ProcessedLayer() types.LayerID {
return layerVerified
}

func (m *MeshAPIMock) GetRewards(types.Address) (rewards []*types.Reward, err error) {
func (m *MeshAPIMock) GetRewardsByCoinbase(types.Address) (rewards []*types.Reward, err error) {
return []*types.Reward{
{
Layer: layerFirst,
TotalReward: rewardAmount,
LayerReward: rewardAmount,
Coinbase: addr1,
SmesherID: rewardSmesherID,
},
}, nil
}

func (m *MeshAPIMock) GetRewardsBySmesherId(types.NodeID) (rewards []*types.Reward, err error) {
return []*types.Reward{
{
Layer: layerFirst,
TotalReward: rewardAmount,
LayerReward: rewardAmount,
Coinbase: addr1,
SmesherID: rewardSmesherID,
},
}, nil
}
Expand Down Expand Up @@ -1728,6 +1742,7 @@ func TestAccountDataStream_comprehensive(t *testing.T) {
Total: rewardAmount,
LayerReward: rewardAmount * 2,
Coinbase: addr1,
SmesherID: rewardSmesherID,
})

res, err := stream.Recv()
Expand Down Expand Up @@ -1783,6 +1798,7 @@ func TestGlobalStateStream_comprehensive(t *testing.T) {
Total: rewardAmount,
LayerReward: rewardAmount * 2,
Coinbase: addr1,
SmesherID: rewardSmesherID,
})
res, err := stream.Recv()
require.NoError(t, err, "got error from stream")
Expand Down Expand Up @@ -1894,7 +1910,7 @@ func checkAccountDataQueryItemReward(t *testing.T, dataItem any) {
require.Equal(t, uint64(rewardAmount), x.Reward.Total.Value)
require.Equal(t, uint64(rewardAmount), x.Reward.LayerReward.Value)
require.Equal(t, addr1.String(), x.Reward.Coinbase.Address)
require.Nil(t, x.Reward.Smesher)
require.Equal(t, rewardSmesherID.Bytes(), x.Reward.Smesher.Id)
}

func checkAccountMeshDataItemTx(t *testing.T, dataItem any) {
Expand Down Expand Up @@ -1925,6 +1941,7 @@ func checkAccountDataItemReward(t *testing.T, dataItem any) {
require.Equal(t, layerFirst.Uint32(), x.Reward.Layer.Number)
require.Equal(t, uint64(rewardAmount*2), x.Reward.LayerReward.Value)
require.Equal(t, addr1.String(), x.Reward.Coinbase.Address)
require.Equal(t, rewardSmesherID.Bytes(), x.Reward.Smesher.Id)
}

func checkAccountDataItemAccount(t *testing.T, dataItem any) {
Expand All @@ -1946,6 +1963,7 @@ func checkGlobalStateDataReward(t *testing.T, dataItem any) {
require.Equal(t, layerFirst.Uint32(), x.Reward.Layer.Number)
require.Equal(t, uint64(rewardAmount*2), x.Reward.LayerReward.Value)
require.Equal(t, addr1.String(), x.Reward.Coinbase.Address)
require.Equal(t, rewardSmesherID.Bytes(), x.Reward.Smesher.Id)
}

func checkGlobalStateDataAccountWrapper(t *testing.T, dataItem any) {
Expand Down
3 changes: 2 additions & 1 deletion api/grpcserver/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ type genesisTimeAPI interface {
type meshAPI interface {
GetATXs(context.Context, []types.ATXID) (map[types.ATXID]*types.VerifiedActivationTx, []types.ATXID)
GetLayer(types.LayerID) (*types.Layer, error)
GetRewards(types.Address) ([]*types.Reward, error)
GetRewardsByCoinbase(types.Address) ([]*types.Reward, error)
GetRewardsBySmesherId(id types.NodeID) ([]*types.Reward, error)
LatestLayer() types.LayerID
LatestLayerInState() types.LayerID
ProcessedLayer() types.LayerID
Expand Down
63 changes: 51 additions & 12 deletions api/grpcserver/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 3 additions & 17 deletions cmd/bootstrapper/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
pb "github.com/spacemeshos/api/release/go/spacemesh/v1"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"go.uber.org/zap/zaptest"

"github.com/spacemeshos/go-spacemesh/api/grpcserver"
Expand Down Expand Up @@ -44,22 +45,6 @@ var bitcoinResponse1 string
//go:embed bitcoinResponse2.json
var bitcoinResponse2 string

type MeshAPIMock struct{}

func (m *MeshAPIMock) LatestLayer() types.LayerID { panic("not implemented") }
func (m *MeshAPIMock) LatestLayerInState() types.LayerID { panic("not implemented") }
func (m *MeshAPIMock) ProcessedLayer() types.LayerID { panic("not implemented") }
func (m *MeshAPIMock) GetRewards(types.Address) ([]*types.Reward, error) { panic("not implemented") }
func (m *MeshAPIMock) GetLayer(types.LayerID) (*types.Layer, error) { panic("not implemented") }

func (m *MeshAPIMock) GetATXs(
context.Context,
[]types.ATXID,
) (map[types.ATXID]*types.VerifiedActivationTx, []types.ATXID) {
panic("not implemented")
}
func (m *MeshAPIMock) MeshHash(types.LayerID) (types.Hash32, error) { panic("not implemented") }

func createAtxs(tb testing.TB, db sql.Executor, epoch types.EpochID, atxids []types.ATXID) {
for _, id := range atxids {
atx := &types.ActivationTx{InnerActivationTx: types.InnerActivationTx{
Expand All @@ -82,7 +67,8 @@ func launchServer(tb testing.TB, cdb *datastore.CachedDB) (grpcserver.Config, fu
cfg := grpcserver.DefaultTestConfig()
grpcService := grpcserver.New("127.0.0.1:0", zaptest.NewLogger(tb).Named("grpc"), cfg)
jsonService := grpcserver.NewJSONHTTPServer("127.0.0.1:0", zaptest.NewLogger(tb).Named("grpc.JSON"))
s := grpcserver.NewMeshService(cdb, &MeshAPIMock{}, nil, nil, 0, types.Hash20{}, 0, 0, 0)
s := grpcserver.NewMeshService(cdb, grpcserver.NewMockmeshAPI(gomock.NewController(tb)), nil, nil,
0, types.Hash20{}, 0, 0, 0)

pb.RegisterMeshServiceServer(grpcService.GrpcServer, s)
// start gRPC and json servers
Expand Down
7 changes: 4 additions & 3 deletions common/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ type AnyReward struct {
Weight RatNum
}

// CoinbaseReward contains the reward information by coinbase, used as an interface to VM.
// CoinbaseReward contains the reward information by coinbase and smesher, used as an interface to VM.
type CoinbaseReward struct {
Coinbase Address
Weight RatNum
SmesherID NodeID
Coinbase Address
Weight RatNum
}

// Initialize calculates and sets the Block's cached blockID.
Expand Down
1 change: 1 addition & 0 deletions common/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ type Reward struct {
TotalReward uint64
LayerReward uint64
Coinbase Address
SmesherID NodeID
}

// NewRawTx computes id from raw bytes and returns the object.
Expand Down
Loading

0 comments on commit d424b78

Please sign in to comment.