diff --git a/common/schema b/common/schema index 64584bb27..d0dee1f0f 160000 --- a/common/schema +++ b/common/schema @@ -1 +1 @@ -Subproject commit 64584bb277b9861cc0411a834f09cc1977fbae15 +Subproject commit d0dee1f0f2f1403c348014ffebcf1c79885ac3c7 diff --git a/core/service/blockSpinePublickeyService.go b/core/service/blockSpinePublickeyService.go index 6a2edf6c5..fe27f0651 100644 --- a/core/service/blockSpinePublickeyService.go +++ b/core/service/blockSpinePublickeyService.go @@ -67,14 +67,14 @@ func (bsf *BlockSpinePublicKeyService) BuildSpinePublicKeysFromNodeRegistry( } spinePublicKeys = make([]*model.SpinePublicKey, 0) for _, nr := range nodeRegistrations { - bsfpk := &model.SpinePublicKey{ + spinePublicKey := &model.SpinePublicKey{ NodePublicKey: nr.NodePublicKey, PublicKeyAction: util.GetAddRemoveSpineKeyAction(nr.RegistrationStatus), - MainBlockHeight: nr.Height, + MainBlockHeight: nr.Height, // (node registration) transaction's height Height: spineHeight, Latest: true, } - spinePublicKeys = append(spinePublicKeys, bsfpk) + spinePublicKeys = append(spinePublicKeys, spinePublicKey) } return spinePublicKeys, nil } diff --git a/core/smith/strategy/blocksmithStrategySpine.go b/core/smith/strategy/blocksmithStrategySpine.go index 8bf1694e0..7c9e0492b 100644 --- a/core/smith/strategy/blocksmithStrategySpine.go +++ b/core/smith/strategy/blocksmithStrategySpine.go @@ -25,6 +25,7 @@ type ( LastSortedBlockID int64 SortedBlocksmithsLock sync.RWMutex SortedBlocksmithsMap map[string]*int64 + SpineBlockQuery query.BlockQueryInterface } ) @@ -32,12 +33,14 @@ func NewBlocksmithStrategySpine( queryExecutor query.ExecutorInterface, spinePublicKeyQuery query.SpinePublicKeyQueryInterface, logger *log.Logger, + spineBlockQuery query.BlockQueryInterface, ) *BlocksmithStrategySpine { return &BlocksmithStrategySpine{ QueryExecutor: queryExecutor, SpinePublicKeyQuery: spinePublicKeyQuery, Logger: logger, SortedBlocksmithsMap: make(map[string]*int64), + SpineBlockQuery: spineBlockQuery, } } @@ -107,9 +110,31 @@ func (bss *BlocksmithStrategySpine) SortBlocksmiths(block *model.Block, withLock if block.ID == bss.LastSortedBlockID && block.ID != constant.SpinechainGenesisBlockID { return } + + var ( + prevHeight = block.Height + prevBlock model.Block + err error + ) + + // always calculate sorted blocksmiths from previous block, otherwise when downloading the spine blocks it could happen + // that the node is unable to validate a block if it is smithed by a newly registered node that has his public key included in the same + // block the node is trying to validate (in that scenario the node's public key isn't in the db yet because the block hasn't been + // pushed yet) + if block.Height > 0 { + prevHeight = block.Height - 1 + } + blockAtHeightQ := bss.SpineBlockQuery.GetBlockByHeight(prevHeight) + blockAtHeightRow, _ := bss.QueryExecutor.ExecuteSelectRow(blockAtHeightQ, false) + err = bss.SpineBlockQuery.Scan(&prevBlock, blockAtHeightRow) + if err != nil { + bss.Logger.Errorf("SortBlocksmith (Spine):GetBlockByHeight fail: %s", err) + return + } + // fetch valid blocksmiths var blocksmiths []*model.Blocksmith - nextBlocksmiths, err := bss.GetBlocksmiths(block) + nextBlocksmiths, err := bss.GetBlocksmiths(&prevBlock) if err != nil { bss.Logger.Errorf("SortBlocksmith (Spine):GetBlocksmiths fail: %s", err) return diff --git a/core/smith/strategy/blocksmithStrategySpine_test.go b/core/smith/strategy/blocksmithStrategySpine_test.go index f3a40d521..193c33b8d 100644 --- a/core/smith/strategy/blocksmithStrategySpine_test.go +++ b/core/smith/strategy/blocksmithStrategySpine_test.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "github.com/zoobc/zoobc-core/common/chaintype" "math/big" "reflect" "regexp" @@ -40,6 +41,30 @@ func (*mockQueryGetBlocksmithsSpineFail) ExecuteSelect( return nil, errors.New("mockError") } +func (*mockQueryGetBlocksmithsSpineFail) ExecuteSelectRow(qStr string, tx bool, args ...interface{}) (*sql.Row, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + mock.ExpectQuery(regexp.QuoteMeta(qStr)). + WillReturnRows(sqlmock.NewRows(query.NewBlockQuery(&chaintype.SpineChain{}).Fields).AddRow( + mockBlock.GetID(), + mockBlock.GetBlockHash(), + mockBlock.GetPreviousBlockHash(), + mockBlock.GetHeight(), + mockBlock.GetTimestamp(), + mockBlock.GetBlockSeed(), + mockBlock.GetBlockSignature(), + mockBlock.GetCumulativeDifficulty(), + mockBlock.GetPayloadLength(), + mockBlock.GetPayloadHash(), + mockBlock.GetBlocksmithPublicKey(), + mockBlock.GetTotalAmount(), + mockBlock.GetTotalFee(), + mockBlock.GetTotalCoinBase(), + mockBlock.GetVersion(), + )) + return db.QueryRow(qStr), nil +} + func (*mockQueryGetBlocksmithsSpineSuccessWithBlocksmith) ExecuteSelect( qStr string, tx bool, args ...interface{}, ) (*sql.Rows, error) { @@ -114,6 +139,30 @@ func (*mockQuerySortBlocksmithSpineSuccessWithBlocksmiths) ExecuteSelect( return rows, nil } +func (*mockQuerySortBlocksmithSpineSuccessWithBlocksmiths) ExecuteSelectRow(qStr string, tx bool, args ...interface{}) (*sql.Row, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + mock.ExpectQuery(regexp.QuoteMeta(qStr)). + WillReturnRows(sqlmock.NewRows(query.NewBlockQuery(&chaintype.SpineChain{}).Fields).AddRow( + mockBlock.GetID(), + mockBlock.GetBlockHash(), + mockBlock.GetPreviousBlockHash(), + mockBlock.GetHeight(), + mockBlock.GetTimestamp(), + mockBlock.GetBlockSeed(), + mockBlock.GetBlockSignature(), + mockBlock.GetCumulativeDifficulty(), + mockBlock.GetPayloadLength(), + mockBlock.GetPayloadHash(), + mockBlock.GetBlocksmithPublicKey(), + mockBlock.GetTotalAmount(), + mockBlock.GetTotalFee(), + mockBlock.GetTotalCoinBase(), + mockBlock.GetVersion(), + )) + return db.QueryRow(qStr), nil +} + func (*mockQueryGetBlocksmithsSpineSuccessNoBlocksmith) ExecuteSelect( qStr string, tx bool, args ...interface{}, ) (*sql.Rows, error) { @@ -131,6 +180,30 @@ func (*mockQueryGetBlocksmithsSpineSuccessNoBlocksmith) ExecuteSelect( return rows, nil } +func (*mockQueryGetBlocksmithsSpineSuccessNoBlocksmith) ExecuteSelectRow(qStr string, tx bool, args ...interface{}) (*sql.Row, error) { + db, mock, _ := sqlmock.New() + defer db.Close() + mock.ExpectQuery(regexp.QuoteMeta(qStr)). + WillReturnRows(sqlmock.NewRows(query.NewBlockQuery(&chaintype.SpineChain{}).Fields).AddRow( + mockBlock.GetID(), + mockBlock.GetBlockHash(), + mockBlock.GetPreviousBlockHash(), + mockBlock.GetHeight(), + mockBlock.GetTimestamp(), + mockBlock.GetBlockSeed(), + mockBlock.GetBlockSignature(), + mockBlock.GetCumulativeDifficulty(), + mockBlock.GetPayloadLength(), + mockBlock.GetPayloadHash(), + mockBlock.GetBlocksmithPublicKey(), + mockBlock.GetTotalAmount(), + mockBlock.GetTotalFee(), + mockBlock.GetTotalCoinBase(), + mockBlock.GetVersion(), + )) + return db.QueryRow(qStr), nil +} + func TestBlocksmithStrategySpine_GetSmithTime(t *testing.T) { type fields struct { QueryExecutor query.ExecutorInterface @@ -300,6 +373,7 @@ func TestBlocksmithStrategySpine_SortBlocksmiths(t *testing.T) { SortedBlocksmiths []*model.Blocksmith LastSortedBlockID int64 SortedBlocksmithsMap map[string]*int64 + SpineBlockQuery query.BlockQueryInterface } type args struct { block *model.Block @@ -318,6 +392,7 @@ func TestBlocksmithStrategySpine_SortBlocksmiths(t *testing.T) { SortedBlocksmiths: nil, SortedBlocksmithsMap: make(map[string]*int64), LastSortedBlockID: 1, + SpineBlockQuery: query.NewBlockQuery(&chaintype.SpineChain{}), }, args: args{ block: mockBlock, @@ -332,6 +407,7 @@ func TestBlocksmithStrategySpine_SortBlocksmiths(t *testing.T) { SortedBlocksmiths: nil, SortedBlocksmithsMap: make(map[string]*int64), LastSortedBlockID: 1, + SpineBlockQuery: query.NewBlockQuery(&chaintype.SpineChain{}), }, args: args{mockBlock}, }, @@ -345,6 +421,7 @@ func TestBlocksmithStrategySpine_SortBlocksmiths(t *testing.T) { SortedBlocksmiths: tt.fields.SortedBlocksmiths, LastSortedBlockID: tt.fields.LastSortedBlockID, SortedBlocksmithsMap: tt.fields.SortedBlocksmithsMap, + SpineBlockQuery: tt.fields.SpineBlockQuery, } bss.SortedBlocksmiths = make([]*model.Blocksmith, 0) bss.SortBlocksmiths(tt.args.block, true) @@ -371,6 +448,7 @@ func TestBlocksmithStrategySpine_GetSortedBlocksmithsMap(t *testing.T) { LastSortedBlockID int64 SortedBlocksmithsLock sync.RWMutex SortedBlocksmithsMap map[string]*int64 + SpineBlockQuery query.BlockQueryInterface } type args struct { block *model.Block @@ -392,6 +470,7 @@ func TestBlocksmithStrategySpine_GetSortedBlocksmithsMap(t *testing.T) { Logger: log.New(), SortedBlocksmiths: bssMockBlocksmiths, SortedBlocksmithsMap: mockBlocksmithMap, + SpineBlockQuery: query.NewBlockQuery(&chaintype.SpineChain{}), }, want: mockBlocksmithMap, }, @@ -406,6 +485,7 @@ func TestBlocksmithStrategySpine_GetSortedBlocksmithsMap(t *testing.T) { Logger: log.New(), SortedBlocksmiths: bssMockBlocksmiths, SortedBlocksmithsMap: mockBlocksmithMap, + SpineBlockQuery: query.NewBlockQuery(&chaintype.SpineChain{}), }, want: mockBlocksmithMap, }, @@ -420,6 +500,7 @@ func TestBlocksmithStrategySpine_GetSortedBlocksmithsMap(t *testing.T) { LastSortedBlockID: tt.fields.LastSortedBlockID, SortedBlocksmithsLock: tt.fields.SortedBlocksmithsLock, SortedBlocksmithsMap: tt.fields.SortedBlocksmithsMap, + SpineBlockQuery: tt.fields.SpineBlockQuery, } if got := bss.GetSortedBlocksmithsMap(tt.args.block); !reflect.DeepEqual(got, tt.want) { t.Errorf("BlocksmithStrategySpine.GetSortedBlocksmithsMap() = %v, want %v", got, tt.want) @@ -550,6 +631,7 @@ func TestNewBlocksmithStrategySpine(t *testing.T) { queryExecutor query.ExecutorInterface spinePublicKeyQuery query.SpinePublicKeyQueryInterface logger *log.Logger + spineBlockQuery query.BlockQueryInterface } tests := []struct { name string @@ -561,13 +643,13 @@ func TestNewBlocksmithStrategySpine(t *testing.T) { args: args{ logger: nil, }, - want: NewBlocksmithStrategySpine(nil, nil, nil), + want: NewBlocksmithStrategySpine(nil, nil, nil, nil), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := NewBlocksmithStrategySpine(tt.args.queryExecutor, tt.args.spinePublicKeyQuery, - tt.args.logger); !reflect.DeepEqual(got, tt.want) { + tt.args.logger, tt.args.spineBlockQuery); !reflect.DeepEqual(got, tt.want) { t.Errorf("NewBlocksmithStrategySpine() = %v, want %v", got, tt.want) } }) diff --git a/main.go b/main.go index 03fdb189a..77043f562 100644 --- a/main.go +++ b/main.go @@ -525,6 +525,7 @@ func startSpinechain() { queryExecutor, query.NewSpinePublicKeyQuery(), loggerCoreService, + query.NewBlockQuery(spinechain), ) spinechainBlockService = service.NewBlockSpineService( spinechain,