set up leveldb to store explorer info add storage adding dumping new block logic into db add fake data and structs for fake data explorer backend service with fake data clean something up set up leveldb to store explorer info add logic for blockinfo & refactor codepull/155/head
parent
4102079236
commit
912b51ea6f
@ -0,0 +1,43 @@ |
||||
package explorer |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
"sync" |
||||
|
||||
"github.com/harmony-one/harmony/core/types" |
||||
"github.com/harmony-one/harmony/db" |
||||
) |
||||
|
||||
var blockUpdate *BlockUpdate |
||||
var once sync.Once |
||||
|
||||
// BlockUpdate dump the block info into leveldb.
|
||||
type BlockUpdate struct { |
||||
db *db.LDBDatabase |
||||
} |
||||
|
||||
// GetInstance returns attack model by using singleton pattern.
|
||||
func GetInstance() *BlockUpdate { |
||||
once.Do(func() { |
||||
blockUpdate = &BlockUpdate{} |
||||
}) |
||||
return blockUpdate |
||||
} |
||||
|
||||
// Init initializes the block update.
|
||||
func (blockUpdate *BlockUpdate) Init(ip, port string) { |
||||
dbFileName := "/tmp/explorer_db_" + ip + "_" + port |
||||
var err = os.RemoveAll(dbFileName) |
||||
if err != nil { |
||||
fmt.Println(err.Error()) |
||||
} |
||||
if blockUpdate.db, err = db.NewLDBDatabase(dbFileName, 0, 0); err != nil { |
||||
fmt.Println(err.Error()) |
||||
os.Exit(1) |
||||
} |
||||
} |
||||
|
||||
// Dump extracts information from block and index them into lvdb for explorer.
|
||||
func (blockUpdate *BlockUpdate) Dump(block *types.Block, height int) { |
||||
} |
@ -1,158 +0,0 @@ |
||||
package explorer_test |
||||
|
||||
// http://www.golangprograms.com/golang-restful-api-using-grom-and-gorilla-mux.html
|
||||
// https://dev.to/codehakase/building-a-restful-api-with-go
|
||||
// https://thenewstack.io/make-a-restful-json-api-go/
|
||||
// https://medium.com/@kelvin_sp/building-and-testing-a-rest-api-in-golang-using-gorilla-mux-and-mysql-1f0518818ff6
|
||||
// var a App
|
||||
|
||||
// func TestMain(m *testing.M) {
|
||||
// a = App{}
|
||||
// a.Initialize("root", "", "rest_api_example")
|
||||
|
||||
// ensureTableExists()
|
||||
|
||||
// code := m.Run()
|
||||
|
||||
// clearTable()
|
||||
|
||||
// os.Exit(code)
|
||||
// }
|
||||
|
||||
// func ensureTableExists() {
|
||||
// if _, err := a.DB.Exec(tableCreationQuery); err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func clearTable() {
|
||||
// a.DB.Exec("DELETE FROM users")
|
||||
// a.DB.Exec("ALTER TABLE users AUTO_INCREMENT = 1")
|
||||
// }
|
||||
|
||||
// func executeRequest(req *http.Request) *httptest.ResponseRecorder {
|
||||
// rr := httptest.NewRecorder()
|
||||
// a.Router.ServeHTTP(rr, req)
|
||||
|
||||
// return rr
|
||||
// }
|
||||
|
||||
// func checkResponseCode(t *testing.T, expected, actual int) {
|
||||
// if expected != actual {
|
||||
// t.Errorf("Expected response code %d. Got %d\n", expected, actual)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestGetNonExistentUser(t *testing.T) {
|
||||
// clearTable()
|
||||
|
||||
// req, _ := http.NewRequest("GET", "/user/45", nil)
|
||||
// response := executeRequest(req)
|
||||
|
||||
// checkResponseCode(t, http.StatusNotFound, response.Code)
|
||||
|
||||
// var m map[string]string
|
||||
// json.Unmarshal(response.Body.Bytes(), &m)
|
||||
// if m["error"] != "User not found" {
|
||||
// t.Errorf("Expected the 'error' key of the response to be set to 'User not found'. Got '%s'", m["error"])
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestCreateUser(t *testing.T) {
|
||||
// clearTable()
|
||||
|
||||
// payload := []byte(`{"name":"test user","age":30}`)
|
||||
|
||||
// req, _ := http.NewRequest("POST", "/user", bytes.NewBuffer(payload))
|
||||
// response := executeRequest(req)
|
||||
|
||||
// checkResponseCode(t, http.StatusCreated, response.Code)
|
||||
|
||||
// var m map[string]interface{}
|
||||
// json.Unmarshal(response.Body.Bytes(), &m)
|
||||
|
||||
// if m["name"] != "test user" {
|
||||
// t.Errorf("Expected user name to be 'test user'. Got '%v'", m["name"])
|
||||
// }
|
||||
|
||||
// if m["age"] != 30.0 {
|
||||
// t.Errorf("Expected user age to be '30'. Got '%v'", m["age"])
|
||||
// }
|
||||
|
||||
// // the id is compared to 1.0 because JSON unmarshaling converts numbers to
|
||||
// // floats, when the target is a map[string]interface{}
|
||||
// if m["id"] != 1.0 {
|
||||
// t.Errorf("Expected product ID to be '1'. Got '%v'", m["id"])
|
||||
// }
|
||||
// }
|
||||
|
||||
// func addUsers(count int) {
|
||||
// if count < 1 {
|
||||
// count = 1
|
||||
// }
|
||||
|
||||
// for i := 0; i < count; i++ {
|
||||
// statement := fmt.Sprintf("INSERT INTO users(name, age) VALUES('%s', %d)", ("User " + strconv.Itoa(i+1)), ((i + 1) * 10))
|
||||
// a.DB.Exec(statement)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestGetUser(t *testing.T) {
|
||||
// clearTable()
|
||||
// addUsers(1)
|
||||
|
||||
// req, _ := http.NewRequest("GET", "/user/1", nil)
|
||||
// response := executeRequest(req)
|
||||
|
||||
// checkResponseCode(t, http.StatusOK, response.Code)
|
||||
// }
|
||||
|
||||
// func TestUpdateUser(t *testing.T) {
|
||||
// clearTable()
|
||||
// addUsers(1)
|
||||
|
||||
// req, _ := http.NewRequest("GET", "/user/1", nil)
|
||||
// response := executeRequest(req)
|
||||
// var originalUser map[string]interface{}
|
||||
// json.Unmarshal(response.Body.Bytes(), &originalUser)
|
||||
|
||||
// payload := []byte(`{"name":"test user - updated name","age":21}`)
|
||||
|
||||
// req, _ = http.NewRequest("PUT", "/user/1", bytes.NewBuffer(payload))
|
||||
// response = executeRequest(req)
|
||||
|
||||
// checkResponseCode(t, http.StatusOK, response.Code)
|
||||
|
||||
// var m map[string]interface{}
|
||||
// json.Unmarshal(response.Body.Bytes(), &m)
|
||||
|
||||
// if m["id"] != originalUser["id"] {
|
||||
// t.Errorf("Expected the id to remain the same (%v). Got %v", originalUser["id"], m["id"])
|
||||
// }
|
||||
|
||||
// if m["name"] == originalUser["name"] {
|
||||
// t.Errorf("Expected the name to change from '%v' to '%v'. Got '%v'", originalUser["name"], m["name"], m["name"])
|
||||
// }
|
||||
|
||||
// if m["age"] == originalUser["age"] {
|
||||
// t.Errorf("Expected the age to change from '%v' to '%v'. Got '%v'", originalUser["age"], m["age"], m["age"])
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestDeleteUser(t *testing.T) {
|
||||
// clearTable()
|
||||
// addUsers(1)
|
||||
|
||||
// req, _ := http.NewRequest("GET", "/user/1", nil)
|
||||
// response := executeRequest(req)
|
||||
// checkResponseCode(t, http.StatusOK, response.Code)
|
||||
|
||||
// req, _ = http.NewRequest("DELETE", "/user/1", nil)
|
||||
// response = executeRequest(req)
|
||||
|
||||
// checkResponseCode(t, http.StatusOK, response.Code)
|
||||
|
||||
// req, _ = http.NewRequest("GET", "/user/1", nil)
|
||||
// response = executeRequest(req)
|
||||
// checkResponseCode(t, http.StatusNotFound, response.Code)
|
||||
// }
|
@ -0,0 +1,118 @@ |
||||
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
|
||||
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) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue