You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
2.8 KiB
119 lines
2.8 KiB
package explorer
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
"github.com/harmony-one/harmony/core/types"
|
|
"github.com/harmony-one/harmony/db"
|
|
)
|
|
|
|
// Constants for storage.
|
|
const (
|
|
BlockHeightKey = "bh"
|
|
BlockInfoPrefix = "bi"
|
|
BlockPrefix = "b"
|
|
TXPrefix = "tx"
|
|
)
|
|
|
|
// GetBlockInfoKey ...
|
|
func GetBlockInfoKey(id int) string {
|
|
return fmt.Sprintf("%s_%d", BlockInfoPrefix, id)
|
|
}
|
|
|
|
// GetBlockKey ...
|
|
func GetBlockKey(id int) string {
|
|
return fmt.Sprintf("%s_%d", BlockPrefix, id)
|
|
}
|
|
|
|
// GetTXKey ...
|
|
func GetTXKey(hash string) string {
|
|
return fmt.Sprintf("%s_%s", TXPrefix, hash)
|
|
}
|
|
|
|
var storage *Storage
|
|
var once sync.Once
|
|
|
|
// Storage dump the block info into leveldb.
|
|
type Storage struct {
|
|
db *db.LDBDatabase
|
|
}
|
|
|
|
// GetStorageInstance returns attack model by using singleton pattern.
|
|
func GetStorageInstance(ip, port string, remove bool) *Storage {
|
|
once.Do(func() {
|
|
storage = &Storage{}
|
|
storage.Init(ip, port, remove)
|
|
})
|
|
return storage
|
|
}
|
|
|
|
// Init initializes the block update.
|
|
func (storage *Storage) Init(ip, port string, remove bool) {
|
|
dbFileName := "/tmp/explorer_storage_" + ip + "_" + port
|
|
var err error
|
|
if remove {
|
|
var err = os.RemoveAll(dbFileName)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
}
|
|
}
|
|
if storage.db, err = db.NewLDBDatabase(dbFileName, 0, 0); err != nil {
|
|
fmt.Println(err.Error())
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// GetDB returns the LDBDatabase of the storage.
|
|
func (storage *Storage) GetDB() *db.LDBDatabase {
|
|
return storage.db
|
|
}
|
|
|
|
// Dump extracts information from block and index them into lvdb for explorer.
|
|
func (storage *Storage) Dump(accountBlock []byte, height uint32) {
|
|
fmt.Println("Dumping block ", height)
|
|
if accountBlock == nil {
|
|
return
|
|
}
|
|
// Update block height.
|
|
storage.db.Put([]byte(BlockHeightKey), []byte(strconv.Itoa(int(height))))
|
|
|
|
// Store block.
|
|
block := new(types.Block)
|
|
rlp.DecodeBytes(accountBlock, block)
|
|
storage.db.Put([]byte(GetBlockKey(int(height))), accountBlock)
|
|
|
|
// Store block info.
|
|
blockInfo := BlockInfo{
|
|
ID: block.Hash().Hex(),
|
|
Height: string(height),
|
|
Timestamp: string(block.Time().Int64()),
|
|
TXCount: string(block.Transactions().Len()),
|
|
Size: block.Size().String(),
|
|
}
|
|
|
|
if data, err := rlp.EncodeToBytes(blockInfo); err == nil {
|
|
key := GetBlockInfoKey(int(height))
|
|
fmt.Println("store blockinfo with key ", key)
|
|
fmt.Println("data to store ", data)
|
|
storage.db.Put([]byte(key), data)
|
|
} else {
|
|
fmt.Println("EncodeRLP blockInfo error")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Store txs
|
|
fmt.Println("# of txs ", len(block.Transactions()))
|
|
for _, tx := range block.Transactions() {
|
|
if data, err := rlp.EncodeToBytes(tx); err == nil {
|
|
key := GetTXKey(tx.Hash().Hex())
|
|
storage.db.Put([]byte(key), data)
|
|
} else {
|
|
fmt.Println("EncodeRLP transaction error")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
|