package main import ( "database/sql" "github.com/ipfs/go-cid" _ "github.com/mattn/go-sqlite3" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/types" ) type storage struct { db *sql.DB } func openStorage() (*storage, error) { db, err := sql.Open("sqlite3", "./chainwatch.db") if err != nil { return nil, err } st := &storage{db: db} return st, st.setup() } func (st *storage) setup() error { tx, err := st.db.Begin() if err != nil { return err } _, err = tx.Exec(` create table if not exists actors ( id text not null, code text not null, head text not null, nonce int not null, balance text not null, stateroot text constraint actors_blocks_stateroot_fk references blocks (parentStateRoot), constraint actors_pk primary key (id, nonce, balance, stateroot) ); create index if not exists actors_id_index on actors (id); create table if not exists id_address_map ( id text not null constraint id_address_map_actors_id_fk references actors (id), address text not null, constraint id_address_map_pk primary key (id, address) ); create index if not exists id_address_map_address_index on id_address_map (address); create index if not exists id_address_map_id_index on id_address_map (id); create table if not exists messages ( cid text not null constraint messages_pk primary key, "from" text not null constraint messages_id_address_map_from_fk references id_address_map (address), "to" text not null constraint messages_id_address_map_to_fk references id_address_map (address), nonce int not null, value text not null, gasprice int not null, gaslimit int not null, method int, params blob ); create unique index if not exists messages_cid_uindex on messages (cid); create table if not exists blocks ( cid text not null constraint blocks_pk primary key, parentWeight numeric not null, parentStateRoot text not null, height int not null, miner text not null constraint blocks_id_address_map_miner_fk references id_address_map (address), timestamp int not null ); create unique index if not exists blocks_cid_uindex on blocks (cid); create table if not exists block_messages ( block text not null constraint block_messages_blk_fk references blocks (cid), message text not null constraint block_messages_msg_fk references messages, constraint block_messages_pk primary key (block, message) ); create table if not exists miner_heads ( head text not null constraint miner_heads_actors_head_fk references actors (head), addr text not null constraint miner_heads_actors_id_fk references actors (id), stateroot text not null constraint miner_heads_blocks_stateroot_fk references blocks (parentStateRoot), sectorset text not null, provingset text not null, owner text not null, worker text not null, peerid text not null, sectorsize int not null, power text not null, active int, ppe int not null, slashed_at int not null, constraint miner_heads_id_address_map_owner_fk foreign key (owner) references id_address_map (address), constraint miner_heads_id_address_map_worker_fk foreign key (worker) references id_address_map (address), constraint miner_heads_pk primary key (head, addr) ); `) if err != nil { return err } return tx.Commit() } func (st *storage) hasBlock(bh *types.BlockHeader) bool { var exitsts bool err := st.db.QueryRow(`select exists (select 1 FROM blocks where cid=?)`, bh.Cid().String()).Scan(&exitsts) if err != nil { log.Error(err) return false } return exitsts } func (st *storage) storeActors(actors map[address.Address]map[types.Actor]cid.Cid) error { tx, err := st.db.Begin() if err != nil { return err } stmt, err := tx.Prepare(`insert into actors (id, code, head, nonce, balance, stateroot) values (?, ?, ?, ?, ?, ?) on conflict do nothing`) if err != nil { return err } defer stmt.Close() for addr, acts := range actors { for act, st := range acts { if _, err := stmt.Exec(addr.String(), act.Code.String(), act.Head.String(), act.Nonce, act.Balance.String(), st.String()); err != nil { return err } } } return tx.Commit() } func (st *storage) storeMiners(miners map[minerKey]*minerInfo) error { tx, err := st.db.Begin() if err != nil { return err } stmt, err := tx.Prepare(`insert into miner_heads (head, addr, stateroot, sectorset, provingset, owner, worker, peerid, sectorsize, power, active, ppe, slashed_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) on conflict do nothing`) if err != nil { return err } defer stmt.Close() for k, i := range miners { if _, err := stmt.Exec( k.act.Head.String(), k.addr.String(), k.stateroot.String(), i.state.Sectors.String(), i.state.ProvingSet.String(), i.info.Owner.String(), i.info.Worker.String(), i.info.PeerID.String(), i.info.SectorSize, i.state.Power.String(), i.state.Active, i.state.ProvingPeriodEnd, i.state.SlashedAt, ); err != nil { return err } } return tx.Commit() } func (st *storage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader) error { tx, err := st.db.Begin() if err != nil { return err } stmt, err := tx.Prepare(`insert into blocks (cid, parentWeight, parentStateRoot, height, miner, "timestamp") values (?, ?, ?, ?, ?, ?) on conflict do nothing`) if err != nil { return err } defer stmt.Close() for _, bh := range bhs { if _, err := stmt.Exec(bh.Cid().String(), bh.ParentWeight.String(), bh.ParentStateRoot.String(), bh.Height, bh.Miner.String(), bh.Timestamp); err != nil { return err } } return tx.Commit() } func (st *storage) storeMessages(msgs map[cid.Cid]*types.Message) error { tx, err := st.db.Begin() if err != nil { return err } stmt, err := tx.Prepare(`insert into messages (cid, "from", "to", nonce, "value", gasprice, gaslimit, method, params) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) on conflict do nothing`) if err != nil { return err } defer stmt.Close() for c, m := range msgs { if _, err := stmt.Exec( c.String(), m.From.String(), m.To.String(), m.Nonce, m.Value.String(), m.GasPrice.String(), m.GasLimit.String(), m.Method, m.Params, ); err != nil { return err } } return tx.Commit() } func (st *storage) storeAddressMap(addrs map[address.Address]address.Address) error { tx, err := st.db.Begin() if err != nil { return err } stmt, err := tx.Prepare(`insert into id_address_map (id, address) VALUES (?, ?) on conflict do nothing`) if err != nil { return err } defer stmt.Close() for a, i := range addrs { if i == address.Undef { continue } if _, err := stmt.Exec( i.String(), a.String(), ); err != nil { return err } } return tx.Commit() } func (st *storage) storeMsgInclusions(incls map[cid.Cid][]cid.Cid) error { tx, err := st.db.Begin() if err != nil { return err } stmt, err := tx.Prepare(`insert into block_messages (block, message) VALUES (?, ?)`) if err != nil { return err } defer stmt.Close() for b, msgs := range incls { for _, msg := range msgs { if _, err := stmt.Exec( b.String(), msg.String(), ); err != nil { return err } } } return tx.Commit() } func (st *storage) close() error { return st.db.Close() }