From 39d19871b005d4a06934f6190a1308015826369d Mon Sep 17 00:00:00 2001 From: anton Date: Tue, 5 Mar 2019 17:45:54 +0200 Subject: [PATCH] added reward application before layer is processed by tortoise --- app/main.go | 2 +- config/config.go | 3 ++ mesh/mesh.go | 10 +++++- mesh/mesh_test.go | 2 +- mesh/reward.go | 12 ++++++- mesh/reward_test.go | 76 +++++++++++++++++++++++++++++++++++++-------- sync/syncer.go | 6 ++-- sync/syncer_test.go | 14 +++++++-- 8 files changed, 103 insertions(+), 22 deletions(-) diff --git a/app/main.go b/app/main.go index 830c1af904..185cda892a 100644 --- a/app/main.go +++ b/app/main.go @@ -325,7 +325,7 @@ func (app *SpacemeshApp) initServices(instanceName string, swarm server.Service, ld := time.Duration(app.Config.LayerDurationSec) * time.Second clock := timesync.NewTicker(timesync.RealClock{}, ld, gTime) trtl := consensus.NewAlgorithm(consensus.NewNinjaTortoise(layerSize, lg)) - msh := mesh.NewMesh(db, db, db, trtl, processor, lg) //todo: what to do with the logger? + msh := mesh.NewMesh(db, db, db, app.Config.REWARD, trtl, processor, lg) //todo: what to do with the logger? conf := sync.Configuration{SyncInterval: 1 * time.Second, Concurrency: 4, LayerSize: int(layerSize), RequestTimeout: 100 * time.Millisecond} syncer := sync.NewSync(swarm, msh, blockOracle, conf, clock.Subscribe(), lg) diff --git a/config/config.go b/config/config.go index f746111d1b..bc1e7e8be8 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,7 @@ import ( "github.com/spacemeshos/go-spacemesh/filesystem" hareConfig "github.com/spacemeshos/go-spacemesh/hare/config" "github.com/spacemeshos/go-spacemesh/log" + "github.com/spacemeshos/go-spacemesh/mesh" p2pConfig "github.com/spacemeshos/go-spacemesh/p2p/config" "github.com/spacemeshos/go-spacemesh/state" timeConfig "github.com/spacemeshos/go-spacemesh/timesync/config" @@ -42,6 +43,7 @@ type Config struct { HARE hareConfig.Config `mapstructure:"hare"` TIME timeConfig.TimeConfig `mapstructure:"time"` GAS state.GasParams `mapstructure:"gas"` + REWARD mesh.RewardParams } // BaseConfig defines the default configuration options for spacemesh app @@ -78,6 +80,7 @@ func DefaultConfig() Config { HARE: hareConfig.DefaultConfig(), TIME: timeConfig.DefaultConfig(), GAS: state.DefaultConfig(), + REWARD: mesh.DefaultRewardParams(), } } diff --git a/mesh/mesh.go b/mesh/mesh.go index ef26abe26d..836c93f12d 100644 --- a/mesh/mesh.go +++ b/mesh/mesh.go @@ -31,6 +31,7 @@ type StateUpdater interface { type Mesh struct { log.Log *meshDB + rewardConfig RewardParams verifiedLayer uint32 latestLayer uint32 lastSeenLayer uint32 @@ -43,7 +44,7 @@ type Mesh struct { done chan struct{} } -func NewMesh(layers, blocks, validity database.DB, mesh MeshValidator, state StateUpdater, logger log.Log) *Mesh { +func NewMesh(layers, blocks, validity database.DB, rewardConfig RewardParams, mesh MeshValidator, state StateUpdater, logger log.Log) *Mesh { //todo add boot from disk ll := &Mesh{ Log: logger, @@ -51,6 +52,7 @@ func NewMesh(layers, blocks, validity database.DB, mesh MeshValidator, state Sta state: state, done: make(chan struct{}), meshDB: NewMeshDB(layers, blocks, validity, logger), + rewardConfig: rewardConfig, } return ll @@ -92,6 +94,12 @@ func (m *Mesh) SetLatestLayer(idx uint32) { func (m *Mesh) ValidateLayer(layer *Layer) { m.Info("Validate layer %d", layer.Index()) + + //todo: with the introduction of rewards we need to refactor this loop out of since it now serves multiple purposes + if layer.index >= m.rewardConfig.RewardMaturity { + m.AccumulateRewards(layer.index - m.rewardConfig.RewardMaturity, m.rewardConfig) + } + oldPbase, newPbase := m.tortoise.HandleIncomingLayer(layer) atomic.StoreUint32(&m.verifiedLayer, uint32(layer.Index())) if newPbase > oldPbase { diff --git a/mesh/mesh_test.go b/mesh/mesh_test.go index 09ec20d798..1567f5aa4b 100644 --- a/mesh/mesh_test.go +++ b/mesh/mesh_test.go @@ -35,7 +35,7 @@ func getMesh(id string) *Mesh { bdb := database.NewMemDatabase() ldb := database.NewMemDatabase() cdb := database.NewMemDatabase() - layers := NewMesh(ldb, bdb, cdb, &MeshValidatorMock{}, &MockState{}, log.New(id, "", "")) + layers := NewMesh(ldb, bdb, cdb, ConfigTst(), &MeshValidatorMock{}, &MockState{}, log.New(id, "", "")) return layers } diff --git a/mesh/reward.go b/mesh/reward.go index 6c13a64db3..4559b3c824 100644 --- a/mesh/reward.go +++ b/mesh/reward.go @@ -12,9 +12,19 @@ type RewardParams struct { BaseReward *big.Int PenaltyPercent *big.Int TxQuota uint32 + RewardMaturity LayerID +} + +func DefaultRewardParams() RewardParams{ + return RewardParams{ + big.NewInt(10), + big.NewInt(5000), + big.NewInt(15), + 15, + 5, + } } -//type Transactions []*state.Transaction func CalculateLayerReward(id LayerID, params RewardParams) *big.Int { //todo: add inflation rules here diff --git a/mesh/reward_test.go b/mesh/reward_test.go index 74b407fa17..729dca6db2 100644 --- a/mesh/reward_test.go +++ b/mesh/reward_test.go @@ -34,13 +34,23 @@ func (s *MockMapState) ApplyRewards(layer state.LayerID, miners map[string]struc } +func ConfigTst() RewardParams{ + return RewardParams{ + big.NewInt(10), + big.NewInt(5000), + big.NewInt(15), + 15, + 5, + } +} + func getMeshWithMapState(id string, s StateUpdater) *Mesh { //time := time.Now() bdb := database.NewMemDatabase() ldb := database.NewMemDatabase() cdb := database.NewMemDatabase() - layers := NewMesh(ldb, bdb, cdb, &MeshValidatorMock{}, s, log.New(id, "", "")) + layers := NewMesh(ldb, bdb, cdb, ConfigTst(), &MeshValidatorMock{}, s,log.New(id, "", "")) return layers } @@ -105,12 +115,7 @@ func TestMesh_AccumulateRewards_happyFlow(t *testing.T) { layers.AddBlock(block3) layers.AddBlock(block4) - params := RewardParams{ - big.NewInt(10), - big.NewInt(5000), - big.NewInt(20), - 5, - } + params := NewTestRewardParams() layers.AccumulateRewards(1, params) remainder := (totalRewards * params.SimpleTxCost.Int64()) % 4 @@ -119,6 +124,16 @@ func TestMesh_AccumulateRewards_happyFlow(t *testing.T) { } +func NewTestRewardParams() RewardParams { + return RewardParams{ + big.NewInt(10), + big.NewInt(5000), + big.NewInt(20), + 15, + 10, + } +} + func TestMesh_AccumulateRewards_underQuota(t *testing.T) { s := &MockMapState{Rewards: make(map[string]*big.Int)} layers := getMeshWithMapState("t1", s) @@ -148,12 +163,7 @@ func TestMesh_AccumulateRewards_underQuota(t *testing.T) { layers.AddBlock(block3) layers.AddBlock(block4) - params := RewardParams{ - big.NewInt(10), - big.NewInt(5000), - big.NewInt(20), - 15, - } + params := NewTestRewardParams() layers.AccumulateRewards(1, params) remainder := (totalRewards * params.SimpleTxCost.Int64()) % 4 @@ -165,6 +175,46 @@ func TestMesh_AccumulateRewards_underQuota(t *testing.T) { } +func createLayer(mesh *Mesh,id LayerID, numOfBlocks, maxTransactions int) (totalRewards int64){ + for i := 0; i< numOfBlocks; i++ { + block1 := NewBlock(true, []byte("data1"), time.Now(), id) + block1.MinerID = strconv.Itoa(i) + totalRewards += addTransactions(block1, rand.Intn(maxTransactions)) + mesh.addBlock(block1) + } + return totalRewards +} + +func TestMesh_integration(t *testing.T){ + numofLayers := 10 + numofBlocks := 10 + maxTxs := 20 + + s := &MockMapState{Rewards: make(map[string]*big.Int)} + layers := getMeshWithMapState("t1", s) + defer layers.Close() + + var rewards int64 + for i := 0; i < numofLayers; i++{ + reward := createLayer(layers, LayerID(i), numofBlocks, maxTxs) + if rewards == 0 { + rewards += reward + } + } + + oldTotal := s.Total + l4, err := layers.getLayer(4) + assert.NoError(t,err) + l5, err := layers.getLayer(5) + assert.NoError(t, err) + //test negative case + layers.ValidateLayer(l4) + assert.Equal(t,oldTotal,s.Total) + + layers.ValidateLayer(l5) + assert.Equal(t, rewards * ConfigTst().SimpleTxCost.Int64() + ConfigTst().BaseReward.Int64(), s.Total) +} + func TestMesh_MergeDoubles(t *testing.T) { s := &MockMapState{Rewards: make(map[string]*big.Int)} layers := getMeshWithMapState("t1", s) diff --git a/sync/syncer.go b/sync/syncer.go index f6383045ea..b82a8a2444 100644 --- a/sync/syncer.go +++ b/sync/syncer.go @@ -78,7 +78,7 @@ func (s *Syncer) Start() { //fires a sync every sm.syncInterval or on force space from outside func (s *Syncer) run() { - foo := func() { + syncRoutine := func() { if atomic.CompareAndSwapUint32(&s.SyncLock, IDLE, RUNNING) { s.Synchronise() atomic.StoreUint32(&s.SyncLock, IDLE) @@ -90,11 +90,11 @@ func (s *Syncer) run() { s.Debug("run stoped") return case <-s.forceSync: - go foo() + go syncRoutine() case layer := <-s.clock: atomic.StoreUint32(&s.currentLayer, uint32(layer)) s.Info("sync got tick for layer %v", layer) - go foo() + go syncRoutine() } } } diff --git a/sync/syncer_test.go b/sync/syncer_test.go index d75ced8aab..5e6dc42823 100644 --- a/sync/syncer_test.go +++ b/sync/syncer_test.go @@ -81,13 +81,23 @@ func (s *stateMock) ApplyTransactions(id state.LayerID, tx state.Transactions) ( return 0, nil } +func ConfigTst() mesh.RewardParams{ + return mesh.RewardParams{ + big.NewInt(10), + big.NewInt(5000), + big.NewInt(15), + 15, + 5, + } +} + func getMeshWithLevelDB(id string) *mesh.Mesh { //time := time.Now() bdb := database.NewLevelDbStore("blocks_test_"+id, nil, nil) ldb := database.NewLevelDbStore("layers_test_"+id, nil, nil) cv := database.NewLevelDbStore("contextually_valid_test_"+id, nil, nil) //odb := database.NewLevelDbStore("orphans_test_"+id+"_"+time.String(), nil, nil) - layers := mesh.NewMesh(ldb, bdb, cv, &MeshValidatorMock{}, &stateMock{}, log.New(id, "", "")) + layers := mesh.NewMesh(ldb, bdb, cv,ConfigTst(), &MeshValidatorMock{}, &stateMock{}, log.New(id, "", "")) return layers } @@ -106,7 +116,7 @@ func getMeshWithMemoryDB(id string) *mesh.Mesh { ldb := database.NewMemDatabase() cv := database.NewMemDatabase() //odb := database.NewMemDatabase() - layers := mesh.NewMesh(ldb, bdb, cv, &MeshValidatorMock{}, &stateMock{}, log.New(id, "", "")) + layers := mesh.NewMesh(ldb, bdb, cv, ConfigTst(), &MeshValidatorMock{}, &stateMock{}, log.New(id, "", "")) return layers }