diff --git a/go.mod b/go.mod index 820b987..5d54f27 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 - github.com/elastos/Elastos.ELA v0.7.0 + github.com/elastos/Elastos.ELA v0.6.1-0.20210630074712-c34eacefbc5a github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/stretchr/testify v1.4.0 diff --git a/interface/interface.go b/interface/interface.go index 245a53c..dd1d52a 100644 --- a/interface/interface.go +++ b/interface/interface.go @@ -24,11 +24,14 @@ type Config struct { // on the given height has been rollback OnRollback func(height uint32) - //FilterType is the filter type .(FTBloom, FTDPOS and so on ) + // FilterType is the filter type .(FTBloom, FTDPOS and so on ) FilterType uint8 - //node version + // Node version NodeVersion string + + // Upgrade proposal type + UpgradeProposalType uint16 } /* diff --git a/interface/spvservice.go b/interface/spvservice.go index dc3ab81..24bb367 100644 --- a/interface/spvservice.go +++ b/interface/spvservice.go @@ -30,6 +30,10 @@ const ( // notifyTimeout is the duration to timeout a notify to the listener, and // resend the notify to the listener. notifyTimeout = 10 * time.Second // 10 second + + // upgrade side chain proposal type + minUpgradeProposalType = 0x0200 + maxUpgradeProposalType = 0x02ff ) type ConsensusAlgorithm byte @@ -47,10 +51,12 @@ type spvservice struct { listeners map[common.Uint256]TransactionListener revertListener RevertListener blockListener BlockListener - //FilterType is the filter type .(FTBloom, FTDPOS and so on ) + // FilterType is the filter type .(FTBloom, FTDPOS and so on ) filterType uint8 // p2p Protocol version height use to change version msg content NewP2PProtocolVersionHeight uint64 + // the upgrade ProposalType + UpgradeProposalType uint16 } // NewSPVService creates a new SPV service instance. @@ -93,6 +99,7 @@ func NewSPVService(cfg *Config) (*spvservice, error) { listeners: make(map[common.Uint256]TransactionListener), filterType: cfg.FilterType, NewP2PProtocolVersionHeight: cfg.ChainParams.NewP2PProtocolVersionHeight, + UpgradeProposalType: cfg.UpgradeProposalType, } chainStore := database.NewChainDB(headerStore, service) @@ -329,6 +336,13 @@ func (s *spvservice) putTx(batch store.DataBatch, utx util.Transaction, p.RateOfCustomIDFee, p.Hash(tx.PayloadVersion), nakedBatch); err != nil { return false, err } + default: + if p.ProposalType > minUpgradeProposalType && p.ProposalType <= maxUpgradeProposalType { + if err := s.db.Upgrade().BatchPutControversialUpgrade( + p.Hash(tx.PayloadVersion), p.UpgradeCodeInfo, tx.PayloadVersion, nakedBatch); err != nil { + return false, err + } + } } case types.CustomIDResult: p, ok := tx.Payload.(*payload.CustomIDProposalResult) @@ -336,9 +350,21 @@ func (s *spvservice) putTx(batch store.DataBatch, utx util.Transaction, return false, errors.New("invalid custom ID result tx") } nakedBatch := batch.GetNakedBatch() - err := s.db.CID().BatchPutCustomIDProposalResults(p.ProposalResults, nakedBatch) - if err != nil { - return false, err + for _, r := range p.ProposalResults { + switch r.ProposalType { + case payload.ReceiveCustomID, payload.ReserveCustomID, payload.ChangeCustomIDFee: + err := s.db.CID().BatchPutCustomIDProposalResult(r, nakedBatch) + if err != nil { + return false, err + } + default: + if r.ProposalType > minUpgradeProposalType && r.ProposalType <= maxUpgradeProposalType { + err := s.db.Upgrade().BatchPutUpgradeProposalResult(r, nakedBatch) + if err != nil { + return false, err + } + } + } } } diff --git a/interface/store/addrs.go b/interface/store/addrs.go index e1f72aa..b6e1b40 100644 --- a/interface/store/addrs.go +++ b/interface/store/addrs.go @@ -83,6 +83,5 @@ func (a *addrs) Clear() error { } func (a *addrs) Close() error { - a.Lock() return nil } diff --git a/interface/store/arbiters.go b/interface/store/arbiters.go index 397c34f..41fe0ed 100644 --- a/interface/store/arbiters.go +++ b/interface/store/arbiters.go @@ -173,7 +173,6 @@ func (c *arbiters) GetByHeight(height uint32) (crcArbiters [][]byte, normalArbit } func (c *arbiters) Close() error { - c.Lock() return nil } @@ -181,11 +180,22 @@ func (c *arbiters) Clear() error { c.Lock() defer c.Unlock() it := c.db.NewIterator(dbutil.BytesPrefix(BKTArbiters), nil) - defer it.Release() for it.Next() { c.b.Delete(it.Key()) } + it.Release() c.b.Delete(BKTArbPosition) + c.b.Delete(BKTArbPositions) + it = c.db.NewIterator(dbutil.BytesPrefix(BKTArbitersData), nil) + for it.Next() { + c.b.Delete(it.Key()) + } + it.Release() + it = c.db.NewIterator(dbutil.BytesPrefix(BKTTransactionHeight), nil) + for it.Next() { + c.b.Delete(it.Key()) + } + it.Release() return c.db.Write(c.b, nil) } diff --git a/interface/store/arbiters_test.go b/interface/store/arbiters_test.go index 767e9ed..a5acbed 100644 --- a/interface/store/arbiters_test.go +++ b/interface/store/arbiters_test.go @@ -171,10 +171,6 @@ func TestArbiters(t *testing.T) { t.Errorf("crc arbiter can not be found") return } - - - - } func checkExist(target [][]byte, src [][]byte) bool { diff --git a/interface/store/customid.go b/interface/store/customid.go index ee65ca4..2ff8e37 100644 --- a/interface/store/customid.go +++ b/interface/store/customid.go @@ -14,11 +14,6 @@ import ( // Ensure customID implement CustomID interface. var _ CustomID = (*customID)(nil) -var BKTReservedCustomID = []byte("RS") -var BKTReceivedCustomID = []byte("RC") -var BKTChangeCustomIDFee = []byte("CF") -var BKTLastCustomIDFee = []byte("CH") - const DefaultFeeRate common.Fixed64 = 1e8 type customID struct { @@ -77,17 +72,6 @@ func (c *customID) PutControversialChangeCustomIDFee(rate common.Fixed64, propos return c.db.Write(batch, nil) } -func (c *customID) PutCustomIDProposalResults( - results []payload.ProposalResult) error { - c.Lock() - defer c.Unlock() - batch := new(leveldb.Batch) - if err := c.batchPutCustomIDProposalResults(results, batch); err != nil { - return err - } - return c.db.Write(batch, nil) -} - func (c *customID) BatchPutControversialReservedCustomIDs( reservedCustomIDs []string, proposalHash common.Uint256, batch *leveldb.Batch) error { c.Lock() @@ -135,104 +119,102 @@ func (c *customID) BatchDeleteControversialChangeCustomIDFee( batch.Delete(toKey(BKTChangeCustomIDFee, proposalHash.Bytes()...)) } -func (c *customID) BatchPutCustomIDProposalResults( - results []payload.ProposalResult, batch *leveldb.Batch) error { +func (c *customID) BatchPutCustomIDProposalResult( + result payload.ProposalResult, batch *leveldb.Batch) error { c.Lock() defer c.Unlock() - return c.batchPutCustomIDProposalResults(results, batch) + return c.batchPutCustomIDProposalResult(result, batch) } -func (c *customID) batchPutCustomIDProposalResults( - results []payload.ProposalResult, batch *leveldb.Batch) error { +func (c *customID) batchPutCustomIDProposalResult( + result payload.ProposalResult, batch *leveldb.Batch) error { // add new reserved custom ID into cache. - for _, r := range results { - switch r.ProposalType { - case payload.ReserveCustomID: - // initialize cache. - if len(c.reservedCustomIDs) == 0 { - existedCustomIDs, err := c.getReservedCustomIDsFromDB() - if err != nil { - return err - }else{ - c.reservedCustomIDs = existedCustomIDs - } - } - if r.Result == true { - // update cache. - reservedCustomIDs, err := c.getControversialReservedCustomIDsFromDB(r.ProposalHash) - if err != nil { - return err - } - for k, v := range reservedCustomIDs { - c.reservedCustomIDs[k] = v - } - // update db. - if err := c.batchPutReservedCustomIDs(batch); err != nil { - return err - } + switch result.ProposalType { + case payload.ReserveCustomID: + // initialize cache. + if len(c.reservedCustomIDs) == 0 { + existedCustomIDs, err := c.getReservedCustomIDsFromDB() + if err != nil { + return err } else { - // if you need to remove data from db, you need to consider rollback. - //c.removeControversialReservedCustomIDsFromDB(r.ProposalHash, batch) + c.reservedCustomIDs = existedCustomIDs } - - case payload.ReceiveCustomID: - // initialize cache. - if len(c.receivedCustomIDs) == 0 { - existedCustomIDs, err := c.getReceivedCustomIDsFromDB() - if err != nil { - return err - }else{ - c.receivedCustomIDs = existedCustomIDs - } + } + if result.Result == true { + // update cache. + reservedCustomIDs, err := c.getControversialReservedCustomIDsFromDB(result.ProposalHash) + if err != nil { + return err } - if r.Result == true { - // update cache. - receivedCustomIDs, err := c.getControversialReceivedCustomIDsFromDB(r.ProposalHash) - if err != nil { - return err - } - for k, v := range receivedCustomIDs { - c.receivedCustomIDs[k] = v - } - // update db. - if err := c.batchPutReceivedCustomIDs(batch); err != nil { - return err - } + for k, v := range reservedCustomIDs { + c.reservedCustomIDs[k] = v + } + // update db. + if err := c.batchPutReservedCustomIDs(batch); err != nil { + return err + } + } else { + // if you need to remove data from db, you need to consider rollback. + //c.removeControversialReservedCustomIDsFromDB(r.ProposalHash, batch) + } + + case payload.ReceiveCustomID: + // initialize cache. + if len(c.receivedCustomIDs) == 0 { + existedCustomIDs, err := c.getReceivedCustomIDsFromDB() + if err != nil { + return err } else { - // if you need to remove data from db, you need to consider rollback. - //c.removeControversialReceivedCustomIDsFromDB(r.ProposalHash, batch) + c.receivedCustomIDs = existedCustomIDs } + } + if result.Result == true { + // update cache. + receivedCustomIDs, err := c.getControversialReceivedCustomIDsFromDB(result.ProposalHash) + if err != nil { + return err + } + for k, v := range receivedCustomIDs { + c.receivedCustomIDs[k] = v + } + // update db. + if err := c.batchPutReceivedCustomIDs(batch); err != nil { + return err + } + } else { + // if you need to remove data from db, you need to consider rollback. + //c.removeControversialReceivedCustomIDsFromDB(r.ProposalHash, batch) + } - case payload.ChangeCustomIDFee: - // initialize cache. - if c.feeRate == 0 { - feeRate, _ := c.getCustomIDFeeRateFromDB() - // todo consider other errors - if feeRate == 0 { - feeRate = DefaultFeeRate - } - c.feeRate = feeRate + case payload.ChangeCustomIDFee: + // initialize cache. + if c.feeRate == 0 { + feeRate, _ := c.getCustomIDFeeRateFromDB() + // todo consider other errors + if feeRate == 0 { + feeRate = DefaultFeeRate } + c.feeRate = feeRate + } - if r.Result == true { - rate, err := c.getControversialCustomIDFeeRate(r.ProposalHash) - if err != nil { - return err - } - - // update db. - if err := c.batchPutLastCustomIDFee(batch, r.ProposalHash); err != nil { - return err - } - c.feeRate = rate - if err := c.batchPutChangeCustomIDFee(batch); err != nil { - return err - } - } else { - // if you need to remove data from db, you need to consider rollback. - //c.removeControversialCustomIDFeeRate(r.ProposalHash, batch) + if result.Result == true { + rate, err := c.getControversialCustomIDFeeRate(result.ProposalHash) + if err != nil { + return err + } + + // update db. + if err := c.batchPutLastCustomIDFee(batch, result.ProposalHash); err != nil { + return err + } + c.feeRate = rate + if err := c.batchPutChangeCustomIDFee(batch); err != nil { + return err } + } else { + // if you need to remove data from db, you need to consider rollback. + //c.removeControversialCustomIDFeeRate(r.ProposalHash, batch) } } return nil @@ -396,7 +378,7 @@ func (c *customID) getControversialReservedCustomIDsFromDB(proposalHash common.U return nil, err } r := bytes.NewReader(val) - count, err := common.ReadVarUint(r,0) + count, err := common.ReadVarUint(r, 0) if err != nil { return nil, err } @@ -533,7 +515,7 @@ func (c *customID) getCustomIDFeeRateFromDB() (common.Fixed64, error) { val, err := c.db.Get(BKTChangeCustomIDFee, nil) if err != nil { if err.Error() == leveldb.ErrNotFound.Error() { - return 0,nil + return 0, nil } return 0, err } @@ -565,7 +547,6 @@ func (c *customID) removeControversialCustomIDFeeRate( } func (c *customID) Close() error { - c.Lock() return nil } @@ -575,22 +556,29 @@ func (c *customID) Clear() error { batch := new(leveldb.Batch) it := c.db.NewIterator(util.BytesPrefix(BKTReservedCustomID), nil) - defer it.Release() for it.Next() { batch.Delete(it.Key()) } + it.Release() it = c.db.NewIterator(util.BytesPrefix(BKTReceivedCustomID), nil) - defer it.Release() for it.Next() { batch.Delete(it.Key()) } + it.Release() it = c.db.NewIterator(util.BytesPrefix(BKTChangeCustomIDFee), nil) - defer it.Release() for it.Next() { batch.Delete(it.Key()) } + it.Release() + + it = c.db.NewIterator(util.BytesPrefix(BKTLastCustomIDFee), nil) + for it.Next() { + batch.Delete(it.Key()) + } + it.Release() + return c.db.Write(c.b, nil) } diff --git a/interface/store/databatch.go b/interface/store/databatch.go index 80c334c..23ec4d2 100644 --- a/interface/store/databatch.go +++ b/interface/store/databatch.go @@ -100,8 +100,8 @@ func (b *dataBatch) DeleteCustomID(tx *types.Transaction) error { b.customID.BatchDeleteControversialChangeCustomIDFee( p.Hash(tx.PayloadVersion), b.Batch) } - case types.CustomIDResult: - p, ok := tx.Payload.(*payload.CustomIDProposalResult) + case types.ProposalResult: + p, ok := tx.Payload.(*payload.RecordProposalResult) if !ok { return errors.New("invalid custom ID result tx") } diff --git a/interface/store/datastore.go b/interface/store/datastore.go index 7c6f51e..d5c3bec 100644 --- a/interface/store/datastore.go +++ b/interface/store/datastore.go @@ -19,6 +19,7 @@ type dataStore struct { que *que ars *arbiters cid *customID + upg *upgrade } func NewDataStore(dataDir string, originArbiters [][]byte, arbitersCount int) (*dataStore, error) { @@ -43,6 +44,7 @@ func NewDataStore(dataDir string, originArbiters [][]byte, arbitersCount int) (* que: NewQue(db), ars: NewArbiters(db, originArbiters, arbitersCount), cid: NewCustomID(db), + upg: NewUpgrade(db), }, nil } @@ -70,6 +72,10 @@ func (d *dataStore) CID() CustomID { return d.cid } +func (d *dataStore) Upgrade() Upgrade { + return d.upg +} + func (d *dataStore) Batch() DataBatch { return &dataBatch{ DB: d.db, diff --git a/interface/store/interface.go b/interface/store/interface.go index 1b09213..a82f942 100644 --- a/interface/store/interface.go +++ b/interface/store/interface.go @@ -25,6 +25,7 @@ type DataStore interface { Que() Que Arbiters() Arbiters CID() CustomID + Upgrade() Upgrade Batch() DataBatch } @@ -154,10 +155,18 @@ type CustomID interface { BatchDeleteControversialChangeCustomIDFee( proposalHash common.Uint256, batch *leveldb.Batch) - PutCustomIDProposalResults(results []payload.ProposalResult) error - BatchPutCustomIDProposalResults(results []payload.ProposalResult, batch *leveldb.Batch) error + BatchPutCustomIDProposalResult(result payload.ProposalResult, batch *leveldb.Batch) error GetReservedCustomIDs() (map[string]struct{}, error) GetReceivedCustomIDs() (map[string]common.Uint168, error) GetCustomIDFeeRate() (common.Fixed64, error) } + +type Upgrade interface { + database.DB + + BatchPutControversialUpgrade(proposalHash common.Uint256, info *payload.UpgradeCodeInfo, version byte, batch *leveldb.Batch) error + BatchPutUpgradeProposalResult(result payload.ProposalResult, batch *leveldb.Batch) error + + GetByHeight(height uint32) (info *payload.UpgradeCodeInfo, err error) +} diff --git a/interface/store/prefix.go b/interface/store/prefix.go index 55bac54..cb8a537 100644 --- a/interface/store/prefix.go +++ b/interface/store/prefix.go @@ -32,4 +32,15 @@ var ( BKTRevertPosition = []byte("revertp") BKTRevertPositions = []byte("revertps") BKTConsensus = []byte("consensus") + + // custom ID + BKTReservedCustomID = []byte("RS") + BKTReceivedCustomID = []byte("RC") + BKTChangeCustomIDFee = []byte("CF") + BKTLastCustomIDFee = []byte("CH") + + // upgrade code + BKTUpgradeControversial = []byte("upgradectl") + BKTUpgradeCode = []byte("upgradecode") + BKTUpgradePositions = []byte("upgradepts") ) diff --git a/interface/store/que.go b/interface/store/que.go index ba718af..cfc46b9 100644 --- a/interface/store/que.go +++ b/interface/store/que.go @@ -105,6 +105,5 @@ func (q *que) Clear() error { } func (q *que) Close() error { - q.Lock() return nil } diff --git a/interface/store/upgrade.go b/interface/store/upgrade.go new file mode 100644 index 0000000..fb07e90 --- /dev/null +++ b/interface/store/upgrade.go @@ -0,0 +1,209 @@ +package store + +import ( + "bytes" + "errors" + "github.com/elastos/Elastos.ELA/core/types/payload" + dbutil "github.com/syndtr/goleveldb/leveldb/util" + "sync" + + "github.com/elastos/Elastos.ELA/common" + + "github.com/syndtr/goleveldb/leveldb" +) + +// Ensure upgrade implement Upgrade interface. +var _ Upgrade = (*upgrade)(nil) + +type upgrade struct { + batch + sync.RWMutex + db *leveldb.DB + b *leveldb.Batch + posCache []uint32 +} + +func (u *upgrade) Commit() error { + return u.db.Write(u.b, nil) +} + +func (u *upgrade) Rollback() error { + u.b.Reset() + return nil +} + +func (u *upgrade) Clear() error { + u.Lock() + defer u.Unlock() + it := u.db.NewIterator(dbutil.BytesPrefix(BKTUpgradeControversial), nil) + for it.Next() { + u.b.Delete(it.Key()) + } + it.Release() + it = u.db.NewIterator(dbutil.BytesPrefix(BKTUpgradeCode), nil) + for it.Next() { + u.b.Delete(it.Key()) + } + it.Release() + + u.b.Delete(BKTUpgradePositions) + + return u.db.Write(u.b, nil) +} + +func (u *upgrade) Close() error { + return nil +} + +func NewUpgrade(db *leveldb.DB) *upgrade { + return &upgrade{ + db: db, + b: new(leveldb.Batch), + } +} + +func (u *upgrade) BatchPutControversialUpgrade(proposalHash common.Uint256, info *payload.UpgradeCodeInfo, + version byte, batch *leveldb.Batch) error { + // store reserved custom ID. + w := new(bytes.Buffer) + if err := w.WriteByte(version); err != nil { + return err + } + if err := info.Serialize(w, version); err != nil { + return err + } + batch.Put(toKey(BKTUpgradeControversial, proposalHash.Bytes()...), w.Bytes()) + return nil +} + +func (u *upgrade) getCurrentPositions() []uint32 { + pos, err := u.db.Get(BKTUpgradePositions, nil) + if err == nil { + return bytesToUint32Array(pos) + } + return nil +} + +func (u *upgrade) batchDeleteControversialUpgrade(proposalHash common.Uint256, batch *leveldb.Batch) error { + batch.Delete(toKey(BKTUpgradeControversial, proposalHash.Bytes()...)) + return nil +} + +func (u *upgrade) batchPutUpgradeResult(proposalHash common.Uint256, batch *leveldb.Batch) error { + info, data, err := u.getControversialUpgradeInfoByProposalHash(proposalHash) + if err != nil { + return err + } + + if !info.ForceUpgrade { + return nil + } + + // update current positions + posCache := u.getCurrentPositions() + newPosCache := make([]uint32, 0) + for _, p := range posCache { + if p < info.WorkingHeight { + newPosCache = append(newPosCache, p) + } + } + newPosCache = append(newPosCache, info.WorkingHeight) + u.posCache = newPosCache + batch.Put(BKTUpgradePositions, uint32ArrayToBytes(u.posCache)) + + index := getIndex(info.WorkingHeight) + batch.Put(toKey(BKTUpgradeCode, index...), data) + return nil +} + +func (u *upgrade) BatchPutUpgradeProposalResult( + result payload.ProposalResult, batch *leveldb.Batch) error { + u.Lock() + defer u.Unlock() + + return u.batchPutUpgradeProposalResults(result, batch) +} + +func (u *upgrade) batchPutUpgradeProposalResults( + result payload.ProposalResult, batch *leveldb.Batch) error { + if result.Result == true { + err := u.batchPutUpgradeResult(result.ProposalHash, batch) + if err != nil { + return err + } + } + + // remove controversial upgrade information + if err := u.batchDeleteControversialUpgrade(result.ProposalHash, batch); err != nil { + return err + } + return nil +} + +func (u *upgrade) GetByHeight(height uint32) (info *payload.UpgradeCodeInfo, err error) { + u.RLock() + defer u.RUnlock() + var pos []uint32 + if len(u.posCache) == 0 { + pos = u.getCurrentPositions() + u.posCache = pos + } else { + pos = u.posCache + } + currentHeight, err := findHeight(pos, height) + if err != nil { + return nil, err + } + + return u.getUpgradeInfoByHeight(currentHeight) +} + +func (u *upgrade) getUpgradeInfoByHeight(height uint32) (*payload.UpgradeCodeInfo, error) { + index := getIndex(height) + data, err := u.db.Get(toKey(BKTUpgradeCode, index...), nil) + if err != nil { + return nil, err + } + r := bytes.NewBuffer(data) + versionBytes, err := common.ReadBytes(r, 1) + if err != nil { + return nil, err + } + info := &payload.UpgradeCodeInfo{} + if err = info.Deserialize(r, versionBytes[0]); err != nil { + return nil, err + } + return info, nil +} + +func findHeight(pos []uint32, height uint32) (uint32, error) { + if len(pos) == 0 { + return 0, errors.New("current positions is nil") + } + + for i := len(pos) - 1; i >= 0; i-- { + if height >= pos[i] { + return pos[i], nil + } + } + + return 0, errors.New("invalid height") +} + +func (u *upgrade) getControversialUpgradeInfoByProposalHash(proposalHash common.Uint256) (*payload.UpgradeCodeInfo, []byte, error) { + data, err := u.db.Get(toKey(BKTUpgradeControversial, proposalHash.Bytes()...), nil) + if err != nil { + return nil, nil, err + } + r := bytes.NewBuffer(data) + versionBytes, err := common.ReadBytes(r, 1) + if err != nil { + return nil, nil, err + } + info := &payload.UpgradeCodeInfo{} + if err = info.Deserialize(r, versionBytes[0]); err != nil { + return nil, nil, err + } + + return info, data, nil +} diff --git a/interface/store/upgrade_test.go b/interface/store/upgrade_test.go new file mode 100644 index 0000000..9237921 --- /dev/null +++ b/interface/store/upgrade_test.go @@ -0,0 +1,135 @@ +package store + +import ( + "os" + "path/filepath" + "testing" + + "github.com/elastos/Elastos.ELA/common" + "github.com/elastos/Elastos.ELA/core/types/payload" + + "github.com/stretchr/testify/assert" + "github.com/syndtr/goleveldb/leveldb" +) + +func TestUpgrade(t *testing.T) { + dataDir := "upgrade_test" + os.RemoveAll(dataDir) + + db, err := leveldb.OpenFile(filepath.Join(dataDir, "store"), nil) + if err != nil { + println(err.Error()) + } + upgradeDB := NewUpgrade(db) + + batch := upgradeDB.b + + _, err = upgradeDB.GetByHeight(100) + assert.EqualError(t, err, "current positions is nil") + + // batch put controversial upgrade + ph := common.Uint256{1} + up := &payload.UpgradeCodeInfo{ + WorkingHeight: 100, + NodeVersion: "0.7.0", + NodeDownLoadUrl: "", + NodeBinHash: &common.Uint256{2}, + ForceUpgrade: true, + } + err = upgradeDB.BatchPutControversialUpgrade(ph, up, 0x00, batch) + assert.NoError(t, err) + err = upgradeDB.Commit() + assert.NoError(t, err) + + // get upgrade code information from db, upgrade proposal is controversial. + _, err = upgradeDB.GetByHeight(100) + assert.EqualError(t, err, "current positions is nil") + + // batch put proposal result: true + result := payload.ProposalResult{ + ProposalHash: ph, + ProposalType: 0x0201, + Result: true, + } + err = upgradeDB.BatchPutUpgradeProposalResult(result, batch) + assert.NoError(t, err) + err = upgradeDB.Commit() + assert.NoError(t, err) + + // get upgrade code information from db + info, err := upgradeDB.GetByHeight(100) + assert.NoError(t, err) + assert.Equal(t, *info, *up) + + // second test, batch put upgrade proposal again + // batch put controversial upgrade + ph2 := common.Uint256{3} + up2 := &payload.UpgradeCodeInfo{ + WorkingHeight: 1000, + NodeVersion: "0.8.0", + NodeDownLoadUrl: "", + NodeBinHash: &common.Uint256{4}, + ForceUpgrade: true, + } + err = upgradeDB.BatchPutControversialUpgrade(ph2, up2, 0x00, batch) + assert.NoError(t, err) + err = upgradeDB.Commit() + assert.NoError(t, err) + + // get upgrade code information from db, upgrade proposal is controversial. + info, err = upgradeDB.GetByHeight(1000) + assert.NoError(t, err) + assert.Equal(t, *info, *up) + + // batch put proposal result: true + result = payload.ProposalResult{ + ProposalHash: ph2, + ProposalType: 0x0201, + Result: true, + } + err = upgradeDB.BatchPutUpgradeProposalResult(result, batch) + assert.NoError(t, err) + err = upgradeDB.Commit() + assert.NoError(t, err) + + // get upgrade code information from db + info, err = upgradeDB.GetByHeight(1000) + assert.NoError(t, err) + assert.Equal(t, *info, *up2) + + // third test, batch put upgrade proposal again with result: false + // batch put controversial upgrade + ph3 := common.Uint256{5} + up3 := &payload.UpgradeCodeInfo{ + WorkingHeight: 10000, + NodeVersion: "0.9.0", + NodeDownLoadUrl: "", + NodeBinHash: &common.Uint256{6}, + ForceUpgrade: true, + } + err = upgradeDB.BatchPutControversialUpgrade(ph3, up3, 0x00, batch) + assert.NoError(t, err) + err = upgradeDB.Commit() + assert.NoError(t, err) + + // get upgrade code information from db, upgrade proposal is controversial. + info, err = upgradeDB.GetByHeight(10000) + assert.NoError(t, err) + assert.Equal(t, *info, *up2) + + // batch put proposal result: false + result = payload.ProposalResult{ + ProposalHash: ph2, + ProposalType: 0x0201, + Result: false, + } + err = upgradeDB.BatchPutUpgradeProposalResult(result, batch) + assert.NoError(t, err) + err = upgradeDB.Commit() + assert.NoError(t, err) + + // get upgrade code information from db + info, err = upgradeDB.GetByHeight(10000) + assert.NoError(t, err) + assert.Equal(t, *info, *up2) +}