forked from hush/lightwalletd
George Tankersley
6 years ago
2 changed files with 186 additions and 11 deletions
@ -1,16 +1,149 @@ |
|||
package storage |
|||
|
|||
import "database/sql" |
|||
import ( |
|||
"database/sql" |
|||
"fmt" |
|||
|
|||
func createBlockTable(conn *sql.DB) error { |
|||
tableCreation := ` |
|||
protobuf "github.com/golang/protobuf/proto" |
|||
"github.com/gtank/ctxd/proto" |
|||
"github.com/pkg/errors" |
|||
) |
|||
|
|||
var ( |
|||
ErrBadRange = errors.New("no blocks in specified range") |
|||
) |
|||
|
|||
func createTables(conn *sql.DB) error { |
|||
stateTable := ` |
|||
CREATE TABLE IF NOT EXISTS state ( |
|||
current_height INTEGER, |
|||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, |
|||
FOREIGN KEY (current_height) REFERENCES blocks (height) |
|||
); |
|||
` |
|||
_, err := conn.Exec(stateTable) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
blockTable := ` |
|||
CREATE TABLE IF NOT EXISTS blocks ( |
|||
height INTEGER PRIMARY KEY, |
|||
hash TEXT, |
|||
has_sapling_tx BOOL, |
|||
encoding_version INTEGER, |
|||
compact_encoding BLOB |
|||
); |
|||
` |
|||
_, err := conn.Exec(tableCreation) |
|||
_, err = conn.Exec(blockTable) |
|||
return err |
|||
} |
|||
|
|||
func CurrentHeight(conn *sql.DB) (int, error) { |
|||
var height int |
|||
query := "SELECT current_height FROM state WHERE rowid = 1" |
|||
err := conn.QueryRow(query).Scan(&height) |
|||
return height, err |
|||
} |
|||
|
|||
func SetCurrentHeight(conn *sql.DB, height int) error { |
|||
update := "UPDATE state SET current_height=?, timestamp=CURRENT_TIMESTAMP WHERE rowid = 1" |
|||
result, err := conn.Exec(update, height) |
|||
if err != nil { |
|||
return errors.Wrap(err, "updating state row") |
|||
} |
|||
rowCount, err := result.RowsAffected() |
|||
if err != nil { |
|||
return errors.Wrap(err, "checking if state row exists") |
|||
} |
|||
if rowCount == 0 { |
|||
// row does not yet exist
|
|||
insert := "INSERT OR IGNORE INTO state (rowid, current_height) VALUES (1, ?)" |
|||
result, err = conn.Exec(insert, height) |
|||
if err != nil { |
|||
return errors.Wrap(err, "on state row insert") |
|||
} |
|||
rowCount, err = result.RowsAffected() |
|||
if err != nil { |
|||
return errors.Wrap(err, "checking if state row exists") |
|||
} |
|||
if rowCount != 1 { |
|||
return errors.New("totally failed to update current height state") |
|||
} |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func GetBlock(conn *sql.DB, height int) (*proto.CompactBlock, error) { |
|||
var blockBytes []byte // avoid a copy with *RawBytes
|
|||
query := "SELECT compact_encoding from blocks WHERE height = ?" |
|||
err := conn.QueryRow(query, height).Scan(&blockBytes) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
compactBlock := &proto.CompactBlock{} |
|||
err = protobuf.Unmarshal(blockBytes, compactBlock) |
|||
return compactBlock, err |
|||
} |
|||
|
|||
// [start, end]
|
|||
func GetBlockRange(conn *sql.DB, start, end int) ([]*proto.CompactBlock, error) { |
|||
// TODO sanity check range bounds
|
|||
query := "SELECT compact_encoding from blocks WHERE (height BETWEEN ? AND ?)" |
|||
result, err := conn.Query(query, start, end) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
defer result.Close() |
|||
|
|||
compactBlocks := make([]*proto.CompactBlock, 0, (end-start)+1) |
|||
for result.Next() { |
|||
var blockBytes []byte // avoid a copy with *RawBytes
|
|||
err = result.Scan(&blockBytes) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
newBlock := &proto.CompactBlock{} |
|||
err = protobuf.Unmarshal(blockBytes, newBlock) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
compactBlocks = append(compactBlocks, newBlock) |
|||
} |
|||
|
|||
err = result.Err() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
if len(compactBlocks) == 0 { |
|||
return nil, ErrBadRange |
|||
} |
|||
return compactBlocks, nil |
|||
} |
|||
|
|||
func GetBlockByHash(conn *sql.DB, hash string) (*proto.CompactBlock, error) { |
|||
var blockBytes []byte // avoid a copy with *RawBytes
|
|||
query := "SELECT compact_encoding from blocks WHERE hash = ?" |
|||
err := conn.QueryRow(query, hash).Scan(&blockBytes) |
|||
if err != nil { |
|||
return nil, errors.Wrap(err, fmt.Sprintf("getting block with hash %s", hash)) |
|||
} |
|||
compactBlock := &proto.CompactBlock{} |
|||
err = protobuf.Unmarshal(blockBytes, compactBlock) |
|||
return compactBlock, err |
|||
} |
|||
|
|||
func StoreBlock(conn *sql.DB, height int, hash string, sapling bool, version int, encoded []byte) error { |
|||
insertBlock := "INSERT INTO blocks (height, hash, has_sapling_tx, encoding_version, compact_encoding) values (?, ?, ?, ?, ?)" |
|||
_, err := conn.Exec(insertBlock, height, hash, sapling, version, encoded) |
|||
if err != nil { |
|||
return errors.Wrap(err, fmt.Sprintf("storing compact block %d", height)) |
|||
} |
|||
|
|||
currentHeight, err := CurrentHeight(conn) |
|||
if err != nil || height > currentHeight { |
|||
err = SetCurrentHeight(conn, height) |
|||
} |
|||
return err |
|||
} |
|||
|
Loading…
Reference in new issue