--- /dev/null
+package sync
+
+import (
+ "github.com/aliyun/aliyun-oss-go-sdk/oss"
+
+ "github.com/bytom/vapor/toolbar/apinode"
+ "github.com/bytom/vapor/toolbar/osssync/config"
+ "github.com/bytom/vapor/toolbar/osssync/util"
+)
+
+// BlockKeeper the struct of the BlockKeeper
+type BlockKeeper struct {
+ Node *apinode.Node
+ OssClient *oss.Client
+ OssBucket *oss.Bucket
+ FileUtil *util.FileUtil
+}
+
+// NewBlockKeeper return one new instance of BlockKeeper
+func NewBlockKeeper() (*BlockKeeper, error) {
+ cfg := &config.Config{}
+ err := config.LoadConfig(&cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ node := apinode.NewNode(cfg.VaporURL)
+
+ ossClient, err := oss.New(cfg.Oss.Endpoint, cfg.Oss.AccessKeyID, cfg.Oss.AccessKeySecret)
+ if err != nil {
+ return nil, err
+ }
+
+ ossBucket, err := ossClient.Bucket("bytom-seed")
+ if err != nil {
+ return nil, err
+ }
+
+ fileUtil := util.NewFileUtil("./blocks")
+
+ return &BlockKeeper{
+ Node: node,
+ OssClient: ossClient,
+ OssBucket: ossBucket,
+ FileUtil: fileUtil,
+ }, nil
+}
--- /dev/null
+package sync
+
+import "github.com/bytom/vapor/toolbar/osssync/util"
+
+// Interval determines the number of blocks in a Gzip file in the Interval of blockHeight
+// StartBlockHeight is the start of the Interval
+// EndBlockHeight: the end of the Interval
+// GzSize is the number of blocks store in a Gzip file
+type Interval struct {
+ StartBlockHeight uint64
+ EndBlockHeight uint64
+ GzSize uint64
+}
+
+// NewInterval creates a new Interval from info.json
+func NewInterval(start, end, gzSize uint64) *Interval {
+ return &Interval{
+ StartBlockHeight: start,
+ EndBlockHeight: end,
+ GzSize: gzSize,
+ }
+}
+
+// Info is a struct for info.json
+type Info struct {
+ LatestBlockHeight uint64
+ Interval []*Interval
+}
+
+// NewInfo creates a new Info for info.json
+func NewInfo(end, gzSize uint64) *Info {
+ newInvl := NewInterval(0, end, gzSize)
+ var arr []*Interval
+ arr = append(arr, newInvl)
+ return &Info{0, arr}
+}
+
+// GetInfoJson Download info.json
+func (b *BlockKeeper) GetInfoJson() (*Info, error) {
+ data, err := b.GetObjToData("info.json")
+ if err != nil {
+ return nil, err
+ }
+
+ info := new(Info)
+ err = util.Json2Struct(data, &info)
+ return info, err
+}
+
+// Upload info.json
+func (b *BlockKeeper) PutInfoJson(infoData *Info) error {
+ jsonData, err := util.Struct2Json(infoData)
+ if err != nil {
+ return err
+ }
+
+ // Upload
+ return b.PutObjByteArr("info.json", jsonData)
+}
+
+// SetLatestBlockHeight set new latest blockHeight on OSS
+func (b *BlockKeeper) SetLatestBlockHeight(newLatestBlockHeight uint64) error {
+ info, err := b.GetInfoJson()
+ if err != nil {
+ return err
+ }
+
+ info.LatestBlockHeight = newLatestBlockHeight
+ return b.PutInfoJson(info)
+}
+
+// AddInterval adds an interval to the end of info.json
+func (b *BlockKeeper) AddInterval(end, gzSize uint64) error {
+ isJsonExist, err := b.OssBucket.IsObjectExist("info.json")
+ if err != nil {
+ return err
+ }
+
+ var info *Info
+ if isJsonExist {
+ // Download info.json
+ info, err = b.GetInfoJson()
+ if err != nil {
+ return err
+ }
+
+ // Add Interval
+ prevInvl := info.Interval[len(info.Interval)-1]
+ newInvl := NewInterval(prevInvl.EndBlockHeight+1, end, gzSize)
+ info.Interval = append(info.Interval, newInvl)
+ } else {
+ info = NewInfo(end, gzSize)
+ }
+ return b.PutInfoJson(info)
+}
--- /dev/null
+package sync
+
+import "github.com/bytom/vapor/protocol/bc/types"
+
+// GetBlockArray return the RawBlockArray by BlockHeight from start to start+length-1
+func (b *BlockKeeper) GetBlockArray(start, length uint64) ([]*types.Block, error) {
+ blockHeight := start
+ data := []*types.Block{}
+ for i := uint64(0); i < length; i++ {
+ resp, err := b.Node.GetBlockByHeight(blockHeight)
+ if err != nil {
+ return nil, err
+ }
+
+ data = append(data, resp)
+ blockHeight++
+ }
+ return data, nil
+}
--- /dev/null
+package sync
+
+import (
+ "bytes"
+ "io/ioutil"
+
+ "github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// PutObjByteArr upload Byte Array object
+func (b *BlockKeeper) PutObjByteArr(objectName string, objectValue []byte) error {
+ objectAcl := oss.ObjectACL(oss.ACLPublicRead)
+ return b.OssBucket.PutObject(objectName, bytes.NewReader(objectValue), objectAcl)
+}
+
+// GetObjToData download object to stream
+func (b *BlockKeeper) GetObjToData(objectName string) ([]byte, error) {
+ body, err := b.OssBucket.GetObject(objectName)
+ if err != nil {
+ return nil, err
+ }
+
+ defer body.Close()
+
+ data, err := ioutil.ReadAll(body)
+ if err != nil {
+ return nil, err
+ }
+
+ return data, err
+}
--- /dev/null
+package sync
+
+import (
+ "github.com/bytom/vapor/protocol"
+ "github.com/bytom/vapor/protocol/bc/types"
+)
+
+// GetLatestDownloadBlockHeight returns the current height of the node wait for download.
+func GetLatestDownloadBlockHeight(c *protocol.Chain) uint64 {
+ return c.BestBlockHeight()
+}
+
+// Sync
+func Sync(c *protocol.Chain, blocks []*types.Block) error {
+ for i := 0; i < len(blocks); i++ {
+ _, err := c.ProcessBlock(blocks[i])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
--- /dev/null
+package util
+
+import "os"
+
+// FileUtil is a struct of File utility
+type FileUtil struct {
+ LocalDir string
+}
+
+// IsExists if file or directory exist
+func IsExists(path string) bool {
+ _, err := os.Stat(path)
+ if err != nil && !os.IsExist(err) {
+ return false
+ }
+ return true
+}
+
+// IfNoFileToCreate if the file is not exist, create the file
+func IfNoFileToCreate(fileName string) (file *os.File) {
+ var f *os.File
+ var err error
+ if !IsExists(fileName) {
+ f, err = os.Create(fileName)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ }
+ return f
+}
+
+// PathExists return if path exists
+func PathExists(path string) (bool, error) {
+ _, err := os.Stat(path)
+ if err == nil {
+ return true, nil
+ }
+
+ return false, err
+}
+
+// BlockDirInitial initializes the block directory
+func (f *FileUtil) BlockDirInitial() error {
+ ifPathExist, err := PathExists(f.LocalDir)
+ if err != nil {
+ return err
+ }
+
+ if ifPathExist {
+ err = os.RemoveAll(f.LocalDir)
+ if err != nil {
+ return err
+ }
+ }
+
+ err = os.Mkdir(f.LocalDir, 0755)
+ return err
+}
// GzipCompress compress file to Gzip
func (f *FileUtil) GzipCompress(fileName string) error {
- filePath := f.localDir + "/" + fileName + ".json.gz"
+ filePath := f.LocalDir + "/" + fileName + ".json.gz"
fw, err := os.Create(filePath)
if err != nil {
return err
gw := gzip.NewWriter(fw)
defer gw.Close()
- filePath = f.localDir + "/" + fileName + ".json"
+ filePath = f.LocalDir + "/" + fileName + ".json"
fr, err := os.Open(filePath)
if err != nil {
return err
// GzipUncompress uncompress Gzip file
func (f *FileUtil) GzipUncompress(fileName string) error {
- filedirname := f.localDir + "/" + fileName + ".json.gz"
+ filedirname := f.LocalDir + "/" + fileName + ".json.gz"
fr, err := os.Open(filedirname)
if err != nil {
return err
buf := make([]byte, READ_SIZE)
n, err := gr.Read(buf)
- filedirname = f.localDir + "/" + gr.Header.Name
+ filedirname = f.LocalDir + "/" + gr.Header.Name
fw, err := os.Create(filedirname)
if err != nil {
return err
"os"
)
-type FileUtil struct {
- localDir string
-}
-
// NewFileUtil creates new file util
func NewFileUtil(localDir string) *FileUtil {
return &FileUtil{localDir}
// SaveBlockFile saves block file
func (f *FileUtil) SaveBlockFile(filename string, data interface{}) (bool, error) {
- filename = f.localDir + "/" + filename + ".json"
+ filename = f.LocalDir + "/" + filename + ".json"
saveData, err := json.Marshal(data)
if err != nil {
return false, err
// GetJson read json file
func (f *FileUtil) GetJson(filename string) (json.RawMessage, error) {
- filename = f.localDir + "/" + filename + ".json"
+ filename = f.LocalDir + "/" + filename + ".json"
return ioutil.ReadFile(filename)
}
// RemoveLocal deletes file
func (f *FileUtil) RemoveLocal(filename string) error {
- return os.Remove(f.localDir + "/" + filename)
+ return os.Remove(f.LocalDir + "/" + filename)
}
// Json2Struct transform json to struct
func Struct2Json(theStruct interface{}) (json.RawMessage, error) {
return json.Marshal(theStruct)
}
-
-// IsExists if file or directory exist
-func IsExists(path string) bool {
- _, err := os.Stat(path)
- if err != nil && !os.IsExist(err) {
- return false
- }
- return true
-}
-
-// IfNoFileToCreate if the file is not exist, create the file
-func IfNoFileToCreate(fileName string) (file *os.File) {
- var f *os.File
- var err error
- if !IsExists(fileName) {
- f, err = os.Create(fileName)
- if err != nil {
- return
- }
-
- defer f.Close()
- }
- return f
-}