Merge pull request #1071 from filecoin-project/feat/chainwatch-pg
Use postgres in chainwatch
This commit is contained in:
commit
0011c321e2
@ -21,7 +21,7 @@ func subBlocks(ctx context.Context, api aapi.FullNode, st *storage) {
|
|||||||
bh.Cid(): bh,
|
bh.Cid(): bh,
|
||||||
}, false)
|
}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
//log.Errorf("%+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ var dotCmd = &cli.Command{
|
|||||||
res, err := st.db.Query(`select block, parent, b.miner, b.height, p.height from block_parents
|
res, err := st.db.Query(`select block, parent, b.miner, b.height, p.height from block_parents
|
||||||
inner join blocks b on block_parents.block = b.cid
|
inner join blocks b on block_parents.block = b.cid
|
||||||
inner join blocks p on block_parents.parent = p.cid
|
inner join blocks p on block_parents.parent = p.cid
|
||||||
where b.height > ? and b.height < ?`, minH, maxH)
|
where b.height > $1 and b.height < $2`, minH, maxH)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -34,6 +34,8 @@ where b.height > ? and b.height < ?`, minH, maxH)
|
|||||||
|
|
||||||
fmt.Println("digraph D {")
|
fmt.Println("digraph D {")
|
||||||
|
|
||||||
|
hl := st.hasList()
|
||||||
|
|
||||||
for res.Next() {
|
for res.Next() {
|
||||||
var block, parent, miner string
|
var block, parent, miner string
|
||||||
var height, ph uint64
|
var height, ph uint64
|
||||||
@ -46,7 +48,7 @@ where b.height > ? and b.height < ?`, minH, maxH)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
has := st.hasBlock(bc)
|
_, has := hl[bc]
|
||||||
|
|
||||||
col := crc32.Checksum([]byte(miner), crc32.MakeTable(crc32.Castagnoli))&0xc0c0c0c0 + 0x30303030
|
col := crc32.Checksum([]byte(miner), crc32.MakeTable(crc32.Castagnoli))&0xc0c0c0c0 + 0x30303030
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func main() {
|
|||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "db",
|
Name: "db",
|
||||||
EnvVars: []string{"LOTUS_DB"},
|
EnvVars: []string{"LOTUS_DB"},
|
||||||
Value: "./chainwatch.db",
|
Value: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -82,8 +82,6 @@ var runCmd = &cli.Command{
|
|||||||
defer st.close()
|
defer st.close()
|
||||||
|
|
||||||
runSyncer(ctx, api, st)
|
runSyncer(ctx, api, st)
|
||||||
go subMpool(ctx, api, st)
|
|
||||||
go subBlocks(ctx, api, st)
|
|
||||||
|
|
||||||
h, err := newHandler(api, st)
|
h, err := newHandler(api, st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
|
|
||||||
@ -15,24 +16,45 @@ func subMpool(ctx context.Context, api aapi.FullNode, st *storage) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for change := range sub {
|
for {
|
||||||
if change.Type != aapi.MpoolAdd {
|
var updates []aapi.MpoolUpdate
|
||||||
continue
|
|
||||||
|
select {
|
||||||
|
case update := <-sub:
|
||||||
|
updates = append(updates, update)
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("mpool message")
|
loop:
|
||||||
|
for {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
select {
|
||||||
|
case update := <-sub:
|
||||||
|
updates = append(updates, update)
|
||||||
|
default:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := st.storeMessages(map[cid.Cid]*types.Message{
|
msgs := map[cid.Cid]*types.Message{}
|
||||||
change.Message.Message.Cid(): &change.Message.Message,
|
for _, v := range updates {
|
||||||
})
|
if v.Type != aapi.MpoolAdd {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs[v.Message.Message.Cid()] = &v.Message.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Processing %d mpool updates", len(msgs))
|
||||||
|
|
||||||
|
err := st.storeMessages(msgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := st.storeMpoolInclusion(change.Message.Message.Cid()); err != nil {
|
if err := st.storeMpoolInclusions(updates); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
cmd/lotus-chainwatch/site/block.html
Normal file
61
cmd/lotus-chainwatch/site/block.html
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Lotus ChainWatch</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="main.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{$cid := param "cid"}}
|
||||||
|
|
||||||
|
<div class="Index">
|
||||||
|
<div class="Index-header">
|
||||||
|
<div>
|
||||||
|
<span>Lotus ChainWatch - Wallets</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Index-nodes">
|
||||||
|
<div class="Index-node">
|
||||||
|
<div>Miner: {{index (strings "blocks" "miner" "cid=$1" $cid) 0}}</div>
|
||||||
|
<div>Parents:</div>
|
||||||
|
<div>
|
||||||
|
{{range strings "block_parents" "parent" "block=$1" $cid}}
|
||||||
|
{{$parent := .}}
|
||||||
|
<a href="block.html?cid={{$parent}}">{{. | substr 54 62}}</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div>Messages:</div>
|
||||||
|
<table>
|
||||||
|
{{range strings "block_messages" "message" "block=$1" $cid}}
|
||||||
|
{{$msg := .}}
|
||||||
|
<tr>
|
||||||
|
<td><a href="message.html?cid={{$msg}}">{{$msg | substr 54 62}}</a></td>
|
||||||
|
<td>
|
||||||
|
{{$from := qstr "select \"from\" from messages where cid=$1" $msg}}
|
||||||
|
{{$nonce := qstr "select nonce from messages where cid=$1" $msg}}
|
||||||
|
<a href="key.html?w={{$from}}">{{$from}}</a> (N:{{$nonce}})
|
||||||
|
</td>
|
||||||
|
<td>-></td>
|
||||||
|
<td>
|
||||||
|
{{$to := qstr "select \"to\" from messages where cid=$1" $msg}}
|
||||||
|
<a href="key.html?w={{$to}}">{{$to}}</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Method:<b>{{qstr "select method from messages where cid=$1" $msg}}</b>
|
||||||
|
</td>
|
||||||
|
{{$rec := qstrs `select r.exit, r.gas_used from messages
|
||||||
|
inner join block_messages bm on messages.cid = bm.message
|
||||||
|
inner join blocks b on bm.block = b.cid
|
||||||
|
inner join block_parents bp on b.cid = bp.parent
|
||||||
|
inner join blocks chd on bp.block = chd.cid
|
||||||
|
inner join receipts r on messages.cid = r.msg and chd.parentStateRoot = r.state
|
||||||
|
where messages.cid=$1 and b.cid=$2` 2 $msg $cid}}
|
||||||
|
<td>exit:<b>{{index $rec 0}}</b></td>
|
||||||
|
<td>gasUsed:<b>{{index $rec 1}}</b></td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
43
cmd/lotus-chainwatch/site/blocks.html
Normal file
43
cmd/lotus-chainwatch/site/blocks.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Lotus ChainWatch</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="main.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{$start := param "start" | parseInt}}
|
||||||
|
|
||||||
|
<div class="Index">
|
||||||
|
<div class="Index-header">
|
||||||
|
<div>
|
||||||
|
<span>Lotus ChainWatch - Wallets</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Index-nodes">
|
||||||
|
<div class="Index-node">
|
||||||
|
<table>
|
||||||
|
{{range pageDown $start 50}}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{$h := .}}
|
||||||
|
{{$h}};
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<b>{{qstr `select count(distinct block_messages.message) from block_messages
|
||||||
|
inner join blocks b on block_messages.block = b.cid
|
||||||
|
where b.height = $1` $h}}</b> Msgs
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{range strings "blocks" "cid" "height = $1" $h}}
|
||||||
|
<a href="block.html?cid={{.}}">{{. | substr 54 62}}</a>
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
<a href="blocks.html?start={{sub $start 50}}">Next 50</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -26,6 +26,11 @@
|
|||||||
{{count "id_address_map" "id != address"}} <a href="keys.html">Keys</a>;
|
{{count "id_address_map" "id != address"}} <a href="keys.html">Keys</a>;
|
||||||
E% FIL in wallets; F% FIL in miners; M% in market; %G Other actors; %H FIL it treasury
|
E% FIL in wallets; F% FIL in miners; M% in market; %G Other actors; %H FIL it treasury
|
||||||
</div>
|
</div>
|
||||||
|
<div class="Index-node">
|
||||||
|
{{$maxH := queryNum "select max(height) from blocks inner join blocks_synced bs on blocks.cid = bs.cid"}}
|
||||||
|
|
||||||
|
{{count "blocks"}} <a href="blocks.html?start={{$maxH}}">Blocks</a>; Current Height: {{$maxH}};
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -15,18 +15,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="Index-nodes">
|
<div class="Index-nodes">
|
||||||
<div class="Index-node">
|
<div class="Index-node">
|
||||||
Balance: {{queryNum "select balance from actors inner join main.id_address_map m on m.address = ? where actors.id = m.id order by nonce desc limit 1" $wallet }}
|
Balance: {{queryNum "select balance from actors inner join id_address_map m on m.address = $1 where actors.id = m.id order by nonce desc limit 1" $wallet }}
|
||||||
</div>
|
</div>
|
||||||
<div class="Index-node">
|
<div class="Index-node">
|
||||||
Messages:
|
Messages:
|
||||||
<table>
|
<table>
|
||||||
<tr><td>Dir</td><td>Peer</td><td>Nonce</td><td>Value</td><td>Block</td><td>Mpool Wait</td></tr>
|
<tr><td>Dir</td><td>Peer</td><td>Nonce</td><td>Value</td><td>Block</td><td>Mpool Wait</td></tr>
|
||||||
{{ range messages "`from` = ? or `to` = ?" $wallet $wallet $wallet}}
|
{{ range messages "\"from\" = $1 or \"to\" = $1" $wallet}}
|
||||||
<tr>
|
<tr>
|
||||||
{{ if eq .From.String $wallet }}
|
{{ if eq .From.String $wallet }}
|
||||||
<td>To</td><td>{{.To.String}}</td>
|
<td>To</td><td><a href="key.html?w={{.To.String}}">{{.To.String}}</a></td>
|
||||||
{{else}}
|
{{else}}
|
||||||
<td>From</td><td>{{.From.String}}</td>
|
<td>From</td><td><a href="key.html?w={{.From.String}}">{{.From.String}}</a></td>
|
||||||
{{end}}
|
{{end}}
|
||||||
<td>{{.Nonce}}</td>
|
<td>{{.Nonce}}</td>
|
||||||
<td>{{.Value}}</td>
|
<td>{{.Value}}</td>
|
||||||
|
@ -14,7 +14,12 @@
|
|||||||
<div class="Index-nodes">
|
<div class="Index-nodes">
|
||||||
<div class="Index-node">
|
<div class="Index-node">
|
||||||
{{range strings "id_address_map" "address" "address != id"}}
|
{{range strings "id_address_map" "address" "address != id"}}
|
||||||
<div><a href="key.html?w={{.}}">{{.}}</a></div>
|
{{$addr := .}}
|
||||||
|
<div>
|
||||||
|
<a href="key.html?w={{$addr}}">{{$addr}}</a>
|
||||||
|
<span><b>{{qstr "select count(distinct cid) from messages where \"from\"=?" $addr}}</b> outmsgs;</span>
|
||||||
|
<span><b>{{qstr "select count(distinct cid) from messages where \"to\"=?" $addr}}</b> inmsgs</span>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,10 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
color: #aff;
|
||||||
|
}
|
||||||
|
|
||||||
.Index {
|
.Index {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
@ -2,11 +2,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/lib/pq"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
@ -19,11 +22,13 @@ type storage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func openStorage(dbSource string) (*storage, error) {
|
func openStorage(dbSource string) (*storage, error) {
|
||||||
db, err := sql.Open("sqlite3", dbSource)
|
db, err := sql.Open("postgres", dbSource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db.SetMaxOpenConns(1350)
|
||||||
|
|
||||||
st := &storage{db: db}
|
st := &storage{db: db}
|
||||||
|
|
||||||
return st, st.setup()
|
return st, st.setup()
|
||||||
@ -35,33 +40,77 @@ func (st *storage) setup() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = tx.Exec(`
|
_, err = tx.Exec(`
|
||||||
|
create table if not exists blocks_synced
|
||||||
|
(
|
||||||
|
cid text not null
|
||||||
|
constraint blocks_synced_pk
|
||||||
|
primary key,
|
||||||
|
add_ts int not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create unique index if not exists blocks_synced_cid_uindex
|
||||||
|
on blocks_synced (cid);
|
||||||
|
|
||||||
|
create table if not exists block_parents
|
||||||
|
(
|
||||||
|
block text not null,
|
||||||
|
parent text not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create unique index if not exists block_parents_block_parent_uindex
|
||||||
|
on block_parents (block, parent);
|
||||||
|
|
||||||
|
create table if not exists blocks
|
||||||
|
(
|
||||||
|
cid text not null
|
||||||
|
constraint blocks_pk
|
||||||
|
primary key,
|
||||||
|
parentWeight numeric not null,
|
||||||
|
parentStateRoot text not null,
|
||||||
|
height bigint not null,
|
||||||
|
miner text not null,
|
||||||
|
timestamp bigint not null,
|
||||||
|
vrfproof bytea,
|
||||||
|
tickets bigint not null,
|
||||||
|
eprof bytea,
|
||||||
|
prand bytea,
|
||||||
|
ep0partial bytea,
|
||||||
|
ep0sector bigint not null,
|
||||||
|
ep0challangei bigint not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create unique index if not exists block_cid_uindex
|
||||||
|
on blocks (cid);
|
||||||
|
|
||||||
|
create table if not exists id_address_map
|
||||||
|
(
|
||||||
|
id text not null,
|
||||||
|
address text not null,
|
||||||
|
constraint id_address_map_pk
|
||||||
|
primary key (id, address)
|
||||||
|
);
|
||||||
|
|
||||||
|
create unique index if not exists id_address_map_id_uindex
|
||||||
|
on id_address_map (id);
|
||||||
|
|
||||||
|
create unique index if not exists id_address_map_address_uindex
|
||||||
|
on id_address_map (address);
|
||||||
|
|
||||||
create table if not exists actors
|
create table if not exists actors
|
||||||
(
|
(
|
||||||
id text not null,
|
id text not null
|
||||||
|
constraint id_address_map_actors_id_fk
|
||||||
|
references id_address_map (id),
|
||||||
code text not null,
|
code text not null,
|
||||||
head text not null,
|
head text not null,
|
||||||
nonce int not null,
|
nonce int not null,
|
||||||
balance text not null,
|
balance text not null,
|
||||||
stateroot text
|
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
|
create index if not exists actors_id_index
|
||||||
on actors (id);
|
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
|
create index if not exists id_address_map_address_index
|
||||||
on id_address_map (address);
|
on id_address_map (address);
|
||||||
|
|
||||||
@ -73,84 +122,23 @@ create table if not exists messages
|
|||||||
cid text not null
|
cid text not null
|
||||||
constraint messages_pk
|
constraint messages_pk
|
||||||
primary key,
|
primary key,
|
||||||
"from" text not null
|
"from" text not null,
|
||||||
constraint messages_id_address_map_from_fk
|
"to" text not null,
|
||||||
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,
|
nonce int not null,
|
||||||
value text not null,
|
value text not null,
|
||||||
gasprice int not null,
|
gasprice int not null,
|
||||||
gaslimit int not null,
|
gaslimit int not null,
|
||||||
method int,
|
method int,
|
||||||
params blob
|
params bytea
|
||||||
);
|
);
|
||||||
|
|
||||||
create unique index if not exists messages_cid_uindex
|
create unique index if not exists messages_cid_uindex
|
||||||
on messages (cid);
|
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,
|
|
||||||
vrfproof blob,
|
|
||||||
tickets int not null,
|
|
||||||
eprof blob,
|
|
||||||
prand blob,
|
|
||||||
ep0partial blob,
|
|
||||||
ep0sector int not null,
|
|
||||||
ep0challangei int not null
|
|
||||||
);
|
|
||||||
|
|
||||||
create unique index if not exists block_cid_uindex
|
|
||||||
on blocks (cid);
|
|
||||||
|
|
||||||
create table if not exists blocks_synced
|
|
||||||
(
|
|
||||||
cid text not null
|
|
||||||
constraint blocks_synced_pk
|
|
||||||
primary key
|
|
||||||
constraint blocks_synced_blocks_cid_fk
|
|
||||||
references blocks,
|
|
||||||
add_ts int not null
|
|
||||||
);
|
|
||||||
|
|
||||||
create unique index if not exists blocks_synced_cid_uindex
|
|
||||||
on blocks_synced (cid);
|
|
||||||
|
|
||||||
create table if not exists block_parents
|
|
||||||
(
|
|
||||||
block text not null
|
|
||||||
constraint block_parents_blocks_cid_fk
|
|
||||||
references blocks,
|
|
||||||
parent text not null
|
|
||||||
constraint block_parents_blocks_cid_fk_2
|
|
||||||
references blocks
|
|
||||||
);
|
|
||||||
|
|
||||||
create unique index if not exists block_parents_block_parent_uindex
|
|
||||||
on block_parents (block, parent);
|
|
||||||
|
|
||||||
create unique index if not exists blocks_cid_uindex
|
|
||||||
on blocks (cid);
|
|
||||||
|
|
||||||
create table if not exists block_messages
|
create table if not exists block_messages
|
||||||
(
|
(
|
||||||
block text not null
|
block text not null,
|
||||||
constraint block_messages_blk_fk
|
message text not null,
|
||||||
references blocks (cid),
|
|
||||||
message text not null
|
|
||||||
constraint block_messages_msg_fk
|
|
||||||
references messages,
|
|
||||||
constraint block_messages_pk
|
constraint block_messages_pk
|
||||||
primary key (block, message)
|
primary key (block, message)
|
||||||
);
|
);
|
||||||
@ -170,16 +158,12 @@ create unique index if not exists mpool_messages_msg_uindex
|
|||||||
|
|
||||||
create table if not exists receipts
|
create table if not exists receipts
|
||||||
(
|
(
|
||||||
msg text not null
|
msg text not null,
|
||||||
constraint receipts_messages_cid_fk
|
state text not null,
|
||||||
references messages,
|
|
||||||
state text not null
|
|
||||||
constraint receipts_blocks_parentStateRoot_fk
|
|
||||||
references blocks (parentStateRoot),
|
|
||||||
idx int not null,
|
idx int not null,
|
||||||
exit int not null,
|
exit int not null,
|
||||||
gas_used int not null,
|
gas_used int not null,
|
||||||
return blob,
|
return bytea,
|
||||||
constraint receipts_pk
|
constraint receipts_pk
|
||||||
primary key (msg, state)
|
primary key (msg, state)
|
||||||
);
|
);
|
||||||
@ -187,34 +171,23 @@ create table if not exists receipts
|
|||||||
create index if not exists receipts_msg_state_index
|
create index if not exists receipts_msg_state_index
|
||||||
on receipts (msg, state);
|
on receipts (msg, state);
|
||||||
|
|
||||||
|
|
||||||
create table if not exists miner_heads
|
create table if not exists miner_heads
|
||||||
(
|
(
|
||||||
head text not null
|
head text not null,
|
||||||
constraint miner_heads_actors_head_fk
|
addr text not null,
|
||||||
references actors (head),
|
stateroot text not null,
|
||||||
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,
|
sectorset text not null,
|
||||||
setsize int not null,
|
setsize decimal not null,
|
||||||
provingset text not null,
|
provingset text not null,
|
||||||
provingsize int not null,
|
provingsize decimal not null,
|
||||||
owner text not null,
|
owner text not null,
|
||||||
worker text not null,
|
worker text not null,
|
||||||
peerid text not null,
|
peerid text not null,
|
||||||
sectorsize int not null,
|
sectorsize bigint not null,
|
||||||
power text not null,
|
power bigint not null,
|
||||||
active int,
|
active bool,
|
||||||
ppe int not null,
|
ppe bigint not null,
|
||||||
slashed_at int not null,
|
slashed_at bigint 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
|
constraint miner_heads_pk
|
||||||
primary key (head, addr)
|
primary key (head, addr)
|
||||||
);
|
);
|
||||||
@ -226,14 +199,31 @@ create table if not exists miner_heads
|
|||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *storage) hasBlock(bh cid.Cid) bool {
|
func (st *storage) hasList() map[cid.Cid]struct{} {
|
||||||
var exitsts bool
|
rws, err := st.db.Query(`select cid FROM blocks_synced`)
|
||||||
err := st.db.QueryRow(`select exists (select 1 FROM blocks_synced where cid=?)`, bh.String()).Scan(&exitsts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return false
|
return map[cid.Cid]struct{}{}
|
||||||
}
|
}
|
||||||
return exitsts
|
out := map[cid.Cid]struct{}{}
|
||||||
|
|
||||||
|
for rws.Next() {
|
||||||
|
var c string
|
||||||
|
if err := rws.Scan(&c); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ci, err := cid.Parse(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out[ci] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *storage) storeActors(actors map[address.Address]map[types.Actor]cid.Cid) error {
|
func (st *storage) storeActors(actors map[address.Address]map[types.Actor]cid.Cid) error {
|
||||||
@ -241,12 +231,20 @@ func (st *storage) storeActors(actors map[address.Address]map[types.Actor]cid.Ci
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if _, err := tx.Exec(`
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`insert into actors (id, code, head, nonce, balance, stateroot) values (?, ?, ?, ?, ?, ?) on conflict do nothing`)
|
create temp table a (like actors excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy a (id, code, head, nonce, balance, stateroot) from stdin `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
for addr, acts := range actors {
|
for addr, acts := range actors {
|
||||||
for act, st := range acts {
|
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 {
|
if _, err := stmt.Exec(addr.String(), act.Code.String(), act.Head.String(), act.Nonce, act.Balance.String(), st.String()); err != nil {
|
||||||
@ -255,6 +253,14 @@ func (st *storage) storeActors(actors map[address.Address]map[types.Actor]cid.Ci
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into actors select * from a on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("actor put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,20 +270,28 @@ func (st *storage) storeMiners(miners map[minerKey]*minerInfo) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`insert into miner_heads (head, addr, stateroot, sectorset, setsize, provingset, provingsize, owner, worker, peerid, sectorsize, power, active, ppe, slashed_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) on conflict do nothing`)
|
if _, err := tx.Exec(`
|
||||||
|
|
||||||
|
create temp table mh (like miner_heads excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy mh (head, addr, stateroot, sectorset, setsize, provingset, provingsize, owner, worker, peerid, sectorsize, power, active, ppe, slashed_at) from STDIN`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
for k, i := range miners {
|
for k, i := range miners {
|
||||||
if _, err := stmt.Exec(
|
if _, err := stmt.Exec(
|
||||||
k.act.Head.String(),
|
k.act.Head.String(),
|
||||||
k.addr.String(),
|
k.addr.String(),
|
||||||
k.stateroot.String(),
|
k.stateroot.String(),
|
||||||
i.state.Sectors.String(),
|
i.state.Sectors.String(),
|
||||||
i.ssize,
|
fmt.Sprint(i.ssize),
|
||||||
i.state.ProvingSet.String(),
|
i.state.ProvingSet.String(),
|
||||||
i.psize,
|
fmt.Sprint(i.psize),
|
||||||
i.info.Owner.String(),
|
i.info.Owner.String(),
|
||||||
i.info.Worker.String(),
|
i.info.Worker.String(),
|
||||||
i.info.PeerID.String(),
|
i.info.PeerID.String(),
|
||||||
@ -290,6 +304,13 @@ func (st *storage) storeMiners(miners map[minerKey]*minerInfo) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into miner_heads select * from mh on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("actor put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
@ -300,21 +321,77 @@ func (st *storage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) e
|
|||||||
|
|
||||||
tx, err := st.db.Begin()
|
tx, err := st.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return xerrors.Errorf("begin: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`insert into blocks (cid, parentWeight, parentStateRoot, height, miner, "timestamp", vrfproof, tickets, eprof, prand, ep0partial, ep0sector, ep0challangei) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) on conflict do nothing`)
|
if _, err := tx.Exec(`
|
||||||
|
|
||||||
|
create temp table tbp (like block_parents excluding constraints) on commit drop;
|
||||||
|
create temp table bs (like blocks_synced excluding constraints) on commit drop;
|
||||||
|
create temp table b (like blocks excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy tbp (block, parent) from STDIN`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
|
for _, bh := range bhs {
|
||||||
|
for _, parent := range bh.Parents {
|
||||||
|
if _, err := stmt.Exec(bh.Cid().String(), parent.String()); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into block_parents select * from tbp on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("parent put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sync {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy bs (cid, add_ts) from stdin `)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bh := range bhs {
|
||||||
|
if _, err := stmt.Exec(bh.Cid().String(), now); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into blocks_synced select * from bs on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("syncd put: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt2, err := tx.Prepare(`copy b (cid, parentWeight, parentStateRoot, height, miner, "timestamp", vrfproof, tickets, eprof, prand, ep0partial, ep0sector, ep0challangei) from stdin `)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, bh := range bhs {
|
for _, bh := range bhs {
|
||||||
l := len(bh.EPostProof.Candidates)
|
l := len(bh.EPostProof.Candidates)
|
||||||
if len(bh.EPostProof.Candidates) == 0 {
|
if len(bh.EPostProof.Candidates) == 0 {
|
||||||
bh.EPostProof.Candidates = append(bh.EPostProof.Candidates, types.EPostTicket{})
|
bh.EPostProof.Candidates = append(bh.EPostProof.Candidates, types.EPostTicket{})
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := stmt.Exec(bh.Cid().String(),
|
if _, err := stmt2.Exec(
|
||||||
|
bh.Cid().String(),
|
||||||
bh.ParentWeight.String(),
|
bh.ParentWeight.String(),
|
||||||
bh.ParentStateRoot.String(),
|
bh.ParentStateRoot.String(),
|
||||||
bh.Height,
|
bh.Height,
|
||||||
@ -326,41 +403,24 @@ func (st *storage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) e
|
|||||||
bh.EPostProof.PostRand,
|
bh.EPostProof.PostRand,
|
||||||
bh.EPostProof.Candidates[0].Partial,
|
bh.EPostProof.Candidates[0].Partial,
|
||||||
bh.EPostProof.Candidates[0].SectorID,
|
bh.EPostProof.Candidates[0].SectorID,
|
||||||
bh.EPostProof.Candidates[0].ChallengeIndex,
|
bh.EPostProof.Candidates[0].ChallengeIndex); err != nil {
|
||||||
); err != nil {
|
log.Error(err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt2, err := tx.Prepare(`insert into block_parents (block, parent) values (?, ?) on conflict do nothing`)
|
if err := stmt2.Close(); err != nil {
|
||||||
|
return xerrors.Errorf("s2 close: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into blocks select * from b on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("blk put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return xerrors.Errorf("commit: %w", err)
|
||||||
}
|
}
|
||||||
defer stmt2.Close()
|
return nil
|
||||||
for _, bh := range bhs {
|
|
||||||
for _, parent := range bh.Parents {
|
|
||||||
if _, err := stmt2.Exec(bh.Cid().String(), parent.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sync {
|
|
||||||
stmt, err := tx.Prepare(`insert into blocks_synced (cid, add_ts) values (?, ?) on conflict do nothing`)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
now := time.Now().Unix()
|
|
||||||
|
|
||||||
for _, bh := range bhs {
|
|
||||||
if _, err := stmt.Exec(bh.Cid().String(), now); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *storage) storeMessages(msgs map[cid.Cid]*types.Message) error {
|
func (st *storage) storeMessages(msgs map[cid.Cid]*types.Message) error {
|
||||||
@ -369,11 +429,19 @@ func (st *storage) storeMessages(msgs map[cid.Cid]*types.Message) error {
|
|||||||
return err
|
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 := tx.Exec(`
|
||||||
|
|
||||||
|
create temp table msgs (like messages excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy msgs (cid, "from", "to", nonce, "value", gasprice, gaslimit, method, params) from stdin `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
for c, m := range msgs {
|
for c, m := range msgs {
|
||||||
if _, err := stmt.Exec(
|
if _, err := stmt.Exec(
|
||||||
@ -390,6 +458,13 @@ func (st *storage) storeMessages(msgs map[cid.Cid]*types.Message) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into messages select * from msgs on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("actor put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
@ -400,11 +475,19 @@ func (st *storage) storeReceipts(recs map[mrec]*types.MessageReceipt) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`insert into receipts (msg, state, idx, exit, gas_used, return) VALUES (?, ?, ?, ?, ?, ?) on conflict do nothing`)
|
if _, err := tx.Exec(`
|
||||||
|
|
||||||
|
create temp table recs (like receipts excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy recs (msg, state, idx, exit, gas_used, return) from stdin `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
for c, m := range recs {
|
for c, m := range recs {
|
||||||
if _, err := stmt.Exec(
|
if _, err := stmt.Exec(
|
||||||
@ -418,6 +501,13 @@ func (st *storage) storeReceipts(recs map[mrec]*types.MessageReceipt) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into receipts select * from recs on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("actor put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
@ -428,11 +518,19 @@ func (st *storage) storeAddressMap(addrs map[address.Address]address.Address) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`insert into id_address_map (id, address) VALUES (?, ?) on conflict do nothing`)
|
if _, err := tx.Exec(`
|
||||||
|
|
||||||
|
create temp table iam (like id_address_map excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy iam (id, address) from STDIN `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
for a, i := range addrs {
|
for a, i := range addrs {
|
||||||
if i == address.Undef {
|
if i == address.Undef {
|
||||||
@ -445,6 +543,13 @@ func (st *storage) storeAddressMap(addrs map[address.Address]address.Address) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into id_address_map select * from iam on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("actor put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
@ -455,11 +560,19 @@ func (st *storage) storeMsgInclusions(incls map[cid.Cid][]cid.Cid) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`insert into block_messages (block, message) VALUES (?, ?) on conflict do nothing`)
|
if _, err := tx.Exec(`
|
||||||
|
|
||||||
|
create temp table mi (like block_messages excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy mi (block, message) from STDIN `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
for b, msgs := range incls {
|
for b, msgs := range incls {
|
||||||
for _, msg := range msgs {
|
for _, msg := range msgs {
|
||||||
@ -471,28 +584,58 @@ func (st *storage) storeMsgInclusions(incls map[cid.Cid][]cid.Cid) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into block_messages select * from mi on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("actor put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *storage) storeMpoolInclusion(msg cid.Cid) error {
|
func (st *storage) storeMpoolInclusions(msgs []api.MpoolUpdate) error {
|
||||||
tx, err := st.db.Begin()
|
tx, err := st.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`insert into mpool_messages (msg, add_ts) VALUES (?, ?) on conflict do nothing`)
|
if _, err := tx.Exec(`
|
||||||
|
|
||||||
|
create temp table mi (like mpool_messages excluding constraints) on commit drop;
|
||||||
|
|
||||||
|
|
||||||
|
`); err != nil {
|
||||||
|
return xerrors.Errorf("prep temp: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(`copy mi (msg, add_ts) from stdin `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
if _, err := stmt.Exec(
|
for _, msg := range msgs {
|
||||||
msg.String(),
|
if msg.Type != api.MpoolAdd {
|
||||||
time.Now().Unix(),
|
continue
|
||||||
); err != nil {
|
}
|
||||||
|
|
||||||
|
if _, err := stmt.Exec(
|
||||||
|
msg.Message.Message.Cid().String(),
|
||||||
|
time.Now().Unix(),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(`insert into mpool_messages select * from mi on conflict do nothing `); err != nil {
|
||||||
|
return xerrors.Errorf("actor put: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
"context"
|
"context"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
@ -16,6 +17,8 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxBatch = 3000
|
||||||
|
|
||||||
func runSyncer(ctx context.Context, api api.FullNode, st *storage) {
|
func runSyncer(ctx context.Context, api api.FullNode, st *storage) {
|
||||||
notifs, err := api.ChainNotify(ctx)
|
notifs, err := api.ChainNotify(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -32,6 +35,12 @@ func runSyncer(ctx context.Context, api api.FullNode, st *storage) {
|
|||||||
case store.HCRevert:
|
case store.HCRevert:
|
||||||
log.Warnf("revert todo")
|
log.Warnf("revert todo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if change.Type == store.HCCurrent {
|
||||||
|
go subMpool(ctx, api, st)
|
||||||
|
go subBlocks(ctx, api, st)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -56,9 +65,13 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
|
|||||||
actors := map[address.Address]map[types.Actor]cid.Cid{}
|
actors := map[address.Address]map[types.Actor]cid.Cid{}
|
||||||
var alk sync.Mutex
|
var alk sync.Mutex
|
||||||
|
|
||||||
|
log.Infof("Getting synced block list")
|
||||||
|
|
||||||
|
hazlist := st.hasList()
|
||||||
|
|
||||||
log.Infof("Getting headers / actors")
|
log.Infof("Getting headers / actors")
|
||||||
|
|
||||||
toSync := map[cid.Cid]*types.BlockHeader{}
|
allToSync := map[cid.Cid]*types.BlockHeader{}
|
||||||
toVisit := list.New()
|
toVisit := list.New()
|
||||||
|
|
||||||
for _, header := range ts.Blocks() {
|
for _, header := range ts.Blocks() {
|
||||||
@ -68,15 +81,16 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
|
|||||||
for toVisit.Len() > 0 {
|
for toVisit.Len() > 0 {
|
||||||
bh := toVisit.Remove(toVisit.Back()).(*types.BlockHeader)
|
bh := toVisit.Remove(toVisit.Back()).(*types.BlockHeader)
|
||||||
|
|
||||||
if _, seen := toSync[bh.Cid()]; seen || st.hasBlock(bh.Cid()) {
|
_, has := hazlist[bh.Cid()]
|
||||||
|
if _, seen := allToSync[bh.Cid()]; seen || has {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
toSync[bh.Cid()] = bh
|
allToSync[bh.Cid()] = bh
|
||||||
addresses[bh.Miner] = address.Undef
|
addresses[bh.Miner] = address.Undef
|
||||||
|
|
||||||
if len(toSync)%500 == 10 {
|
if len(allToSync)%500 == 10 {
|
||||||
log.Infof("todo: (%d) %s", len(toSync), bh.Cid())
|
log.Infof("todo: (%d) %s @%d", len(allToSync), bh.Cid(), bh.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bh.Parents) == 0 {
|
if len(bh.Parents) == 0 {
|
||||||
@ -94,186 +108,218 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Syncing %d blocks", len(toSync))
|
for len(allToSync) > 0 {
|
||||||
|
minH := uint64(math.MaxUint64)
|
||||||
|
|
||||||
log.Infof("Persisting actors")
|
for _, header := range allToSync {
|
||||||
|
if header.Height < minH {
|
||||||
paDone := 0
|
minH = header.Height
|
||||||
par(50, maparr(toSync), func(bh *types.BlockHeader) {
|
}
|
||||||
paDone++
|
|
||||||
if paDone%100 == 0 {
|
|
||||||
log.Infof("pa: %d %d%%", paDone, (paDone*100)/len(toSync))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bh.Parents) == 0 { // genesis case
|
toSync := map[cid.Cid]*types.BlockHeader{}
|
||||||
ts, err := types.NewTipSet([]*types.BlockHeader{bh})
|
for c, header := range allToSync {
|
||||||
aadrs, err := api.StateListActors(ctx, ts)
|
if header.Height < minH+maxBatch {
|
||||||
|
toSync[c] = header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for c := range toSync {
|
||||||
|
delete(allToSync, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Syncing %d blocks", len(toSync))
|
||||||
|
|
||||||
|
paDone := 0
|
||||||
|
par(50, maparr(toSync), func(bh *types.BlockHeader) {
|
||||||
|
paDone++
|
||||||
|
if paDone%100 == 0 {
|
||||||
|
log.Infof("pa: %d %d%%", paDone, (paDone*100)/len(toSync))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bh.Parents) == 0 { // genesis case
|
||||||
|
ts, err := types.NewTipSet([]*types.BlockHeader{bh})
|
||||||
|
aadrs, err := api.StateListActors(ctx, ts)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
par(50, aadrs, func(addr address.Address) {
|
||||||
|
act, err := api.StateGetActor(ctx, addr, ts)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alk.Lock()
|
||||||
|
_, ok := actors[addr]
|
||||||
|
if !ok {
|
||||||
|
actors[addr] = map[types.Actor]cid.Cid{}
|
||||||
|
}
|
||||||
|
actors[addr][*act] = bh.ParentStateRoot
|
||||||
|
addresses[addr] = address.Undef
|
||||||
|
alk.Unlock()
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pts, err := api.ChainGetTipSet(ctx, types.NewTipSetKey(bh.Parents...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
par(50, aadrs, func(addr address.Address) {
|
changes, err := api.StateChangedActors(ctx, pts.ParentState(), bh.ParentStateRoot)
|
||||||
act, err := api.StateGetActor(ctx, addr, ts)
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for a, act := range changes {
|
||||||
|
addr, err := address.NewFromString(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
alk.Lock()
|
alk.Lock()
|
||||||
_, ok := actors[addr]
|
_, ok := actors[addr]
|
||||||
if !ok {
|
if !ok {
|
||||||
actors[addr] = map[types.Actor]cid.Cid{}
|
actors[addr] = map[types.Actor]cid.Cid{}
|
||||||
}
|
}
|
||||||
actors[addr][*act] = bh.ParentStateRoot
|
actors[addr][act] = bh.ParentStateRoot
|
||||||
addresses[addr] = address.Undef
|
addresses[addr] = address.Undef
|
||||||
alk.Unlock()
|
alk.Unlock()
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return
|
log.Infof("Getting messages")
|
||||||
|
|
||||||
|
msgs, incls := fetchMessages(ctx, api, toSync)
|
||||||
|
|
||||||
|
log.Infof("Resolving addresses")
|
||||||
|
|
||||||
|
for _, message := range msgs {
|
||||||
|
addresses[message.To] = address.Undef
|
||||||
|
addresses[message.From] = address.Undef
|
||||||
}
|
}
|
||||||
|
|
||||||
pts, err := api.ChainGetTipSet(ctx, types.NewTipSetKey(bh.Parents...))
|
par(50, kmaparr(addresses), func(addr address.Address) {
|
||||||
if err != nil {
|
raddr, err := api.StateLookupID(ctx, addr, nil)
|
||||||
log.Error(err)
|
if err != nil {
|
||||||
return
|
log.Warn(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alk.Lock()
|
||||||
|
addresses[addr] = raddr
|
||||||
|
alk.Unlock()
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Infof("Getting miner info")
|
||||||
|
|
||||||
|
miners := map[minerKey]*minerInfo{}
|
||||||
|
|
||||||
|
for addr, m := range actors {
|
||||||
|
for actor, c := range m {
|
||||||
|
if actor.Code != actors2.StorageMinerCodeCid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
miners[minerKey{
|
||||||
|
addr: addr,
|
||||||
|
act: actor,
|
||||||
|
stateroot: c,
|
||||||
|
}] = &minerInfo{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changes, err := api.StateChangedActors(ctx, pts.ParentState(), bh.ParentStateRoot)
|
par(50, kvmaparr(miners), func(it func() (minerKey, *minerInfo)) {
|
||||||
if err != nil {
|
k, info := it()
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for a, act := range changes {
|
sszs, err := api.StateMinerSectorCount(ctx, k.addr, nil)
|
||||||
addr, err := address.NewFromString(a)
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.psize = sszs.Pset
|
||||||
|
info.ssize = sszs.Sset
|
||||||
|
|
||||||
|
astb, err := api.ChainReadObj(ctx, k.act.Head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
alk.Lock()
|
if err := info.state.UnmarshalCBOR(bytes.NewReader(astb)); err != nil {
|
||||||
_, ok := actors[addr]
|
log.Error(err)
|
||||||
if !ok {
|
return
|
||||||
actors[addr] = map[types.Actor]cid.Cid{}
|
|
||||||
}
|
|
||||||
actors[addr][act] = bh.ParentStateRoot
|
|
||||||
addresses[addr] = address.Undef
|
|
||||||
alk.Unlock()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := st.storeActors(actors); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Persisting miners")
|
|
||||||
|
|
||||||
miners := map[minerKey]*minerInfo{}
|
|
||||||
|
|
||||||
for addr, m := range actors {
|
|
||||||
for actor, c := range m {
|
|
||||||
if actor.Code != actors2.StorageMinerCodeCid {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
miners[minerKey{
|
ib, err := api.ChainReadObj(ctx, info.state.Info)
|
||||||
addr: addr,
|
if err != nil {
|
||||||
act: actor,
|
log.Error(err)
|
||||||
stateroot: c,
|
return
|
||||||
}] = &minerInfo{}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
par(50, kvmaparr(miners), func(it func() (minerKey, *minerInfo)) {
|
if err := info.info.UnmarshalCBOR(bytes.NewReader(ib)); err != nil {
|
||||||
k, info := it()
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
sszs, err := api.StateMinerSectorCount(ctx, k.addr, nil)
|
log.Info("Getting receipts")
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
receipts := fetchParentReceipts(ctx, api, toSync)
|
||||||
|
|
||||||
|
log.Info("Storing headers")
|
||||||
|
|
||||||
|
if err := st.storeHeaders(toSync, true); err != nil {
|
||||||
|
log.Errorf("%+v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
info.psize = sszs.Pset
|
|
||||||
info.ssize = sszs.Sset
|
|
||||||
|
|
||||||
astb, err := api.ChainReadObj(ctx, k.act.Head)
|
log.Info("Storing address mapping")
|
||||||
if err != nil {
|
|
||||||
|
if err := st.storeAddressMap(addresses); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := info.state.UnmarshalCBOR(bytes.NewReader(astb)); err != nil {
|
log.Info("Storing actors")
|
||||||
|
|
||||||
|
if err := st.storeActors(actors); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ib, err := api.ChainReadObj(ctx, info.state.Info)
|
log.Info("Storing miners")
|
||||||
if err != nil {
|
|
||||||
|
if err := st.storeMiners(miners); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := info.info.UnmarshalCBOR(bytes.NewReader(ib)); err != nil {
|
log.Infof("Storing messages")
|
||||||
|
|
||||||
|
if err := st.storeMessages(msgs); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
if err := st.storeMiners(miners); err != nil {
|
log.Info("Storing message inclusions")
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Persisting headers")
|
if err := st.storeMsgInclusions(incls); err != nil {
|
||||||
if err := st.storeHeaders(toSync, true); err != nil {
|
log.Error(err)
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Getting messages")
|
|
||||||
|
|
||||||
msgs, incls := fetchMessages(ctx, api, toSync)
|
|
||||||
|
|
||||||
if err := st.storeMessages(msgs); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := st.storeMsgInclusions(incls); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Getting parent receipts")
|
|
||||||
|
|
||||||
receipts := fetchParentReceipts(ctx, api, toSync)
|
|
||||||
|
|
||||||
if err := st.storeReceipts(receipts); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Resolving addresses")
|
|
||||||
|
|
||||||
for _, message := range msgs {
|
|
||||||
addresses[message.To] = address.Undef
|
|
||||||
addresses[message.From] = address.Undef
|
|
||||||
}
|
|
||||||
|
|
||||||
par(50, kmaparr(addresses), func(addr address.Address) {
|
|
||||||
raddr, err := api.StateLookupID(ctx, addr, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alk.Lock()
|
|
||||||
addresses[addr] = raddr
|
|
||||||
alk.Unlock()
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := st.storeAddressMap(addresses); err != nil {
|
log.Infof("Storing parent receipts")
|
||||||
log.Error(err)
|
|
||||||
return
|
if err := st.storeReceipts(receipts); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Infof("Sync stage done")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Sync done")
|
log.Infof("Sync done")
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
rice "github.com/GeertJohan/go.rice"
|
rice "github.com/GeertJohan/go.rice"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -42,8 +43,15 @@ func newHandler(api api.FullNode, st *storage) (*handler, error) {
|
|||||||
"queryNum": h.queryNum,
|
"queryNum": h.queryNum,
|
||||||
"sizeStr": sizeStr,
|
"sizeStr": sizeStr,
|
||||||
"strings": h.strings,
|
"strings": h.strings,
|
||||||
|
"qstr": h.qstr,
|
||||||
|
"qstrs": h.qstrs,
|
||||||
"messages": h.messages,
|
"messages": h.messages,
|
||||||
|
|
||||||
|
"pageDown": pageDown,
|
||||||
|
"parseInt": func(s string) (int, error) { i, e := strconv.ParseInt(s, 10, 64); return int(i), e },
|
||||||
|
"substr": func(i, j int, s string) string { return s[i:j] },
|
||||||
|
"sub": func(a, b int) int { return a - b }, // TODO: really not builtin?
|
||||||
|
|
||||||
"param": func(string) string { return "" }, // replaced in request handler
|
"param": func(string) string { return "" }, // replaced in request handler
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,10 +161,9 @@ func (h *handler) netPower(slashFilt string) (types.BigInt, error) {
|
|||||||
if slashFilt != "" {
|
if slashFilt != "" {
|
||||||
slashFilt = " where " + slashFilt
|
slashFilt = " where " + slashFilt
|
||||||
}
|
}
|
||||||
return h.queryNum(`select sum(power) from (
|
return h.queryNum(`select sum(power) from (select distinct on (addr) power, slashed_at from miner_heads
|
||||||
select miner_heads.power, miner_heads.slashed_at, max(height) from miner_heads
|
inner join blocks b on miner_heads.stateroot = b.parentStateRoot
|
||||||
inner join blocks b on miner_heads.stateroot = b.parentStateRoot
|
order by addr, height desc) as p` + slashFilt)
|
||||||
group by miner_heads.addr)` + slashFilt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) queryNum(q string, p ...interface{}) (types.BigInt, error) {
|
func (h *handler) queryNum(q string, p ...interface{}) (types.BigInt, error) {
|
||||||
@ -193,7 +200,7 @@ func (h *handler) strings(table string, col string, filter string, args ...inter
|
|||||||
if len(filter) > 0 {
|
if len(filter) > 0 {
|
||||||
filter = " where " + filter
|
filter = " where " + filter
|
||||||
}
|
}
|
||||||
log.Info("strings qstr ", "select "+col+" from "+table+filter)
|
log.Info("strings qstr ", "select "+col+" from "+table+filter, args)
|
||||||
rws, err := h.st.db.Query("select "+col+" from "+table+filter, args...)
|
rws, err := h.st.db.Query("select "+col+" from "+table+filter, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -209,11 +216,40 @@ func (h *handler) strings(table string, col string, filter string, args ...inter
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handler) qstr(q string, p ...interface{}) (string, error) {
|
||||||
|
// explicitly not caring about sql injection too much, this doesn't take user input
|
||||||
|
|
||||||
|
r, err := h.qstrs(q, 1, p...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return r[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) qstrs(q string, n int, p ...interface{}) ([]string, error) {
|
||||||
|
// explicitly not caring about sql injection too much, this doesn't take user input
|
||||||
|
|
||||||
|
c := make([]string, n)
|
||||||
|
ia := make([]interface{}, n)
|
||||||
|
for i := range c {
|
||||||
|
ia[i] = &c[i]
|
||||||
|
}
|
||||||
|
err := h.st.db.QueryRow(q, p...).Scan(ia...)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("qnum ", q, p, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) messages(filter string, args ...interface{}) (out []types.Message, err error) {
|
func (h *handler) messages(filter string, args ...interface{}) (out []types.Message, err error) {
|
||||||
if len(filter) > 0 {
|
if len(filter) > 0 {
|
||||||
filter = " where " + filter
|
filter = " where " + filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Info("select * from messages " + filter)
|
||||||
|
|
||||||
rws, err := h.st.db.Query("select * from messages "+filter, args...)
|
rws, err := h.st.db.Query("select * from messages "+filter, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -250,4 +286,13 @@ func (h *handler) messages(filter string, args ...interface{}) (out []types.Mess
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pageDown(base, n int) []int {
|
||||||
|
out := make([]int, n)
|
||||||
|
for i := range out {
|
||||||
|
out[i] = base - i
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
var _ http.Handler = &handler{}
|
var _ http.Handler = &handler{}
|
||||||
|
2
go.mod
2
go.mod
@ -52,6 +52,7 @@ require (
|
|||||||
github.com/ipfs/go-merkledag v0.2.4
|
github.com/ipfs/go-merkledag v0.2.4
|
||||||
github.com/ipfs/go-path v0.0.7
|
github.com/ipfs/go-path v0.0.7
|
||||||
github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb
|
github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb
|
||||||
|
github.com/lib/pq v1.2.0
|
||||||
github.com/libp2p/go-libp2p v0.4.2
|
github.com/libp2p/go-libp2p v0.4.2
|
||||||
github.com/libp2p/go-libp2p-circuit v0.1.4
|
github.com/libp2p/go-libp2p-circuit v0.1.4
|
||||||
github.com/libp2p/go-libp2p-connmgr v0.1.0
|
github.com/libp2p/go-libp2p-connmgr v0.1.0
|
||||||
@ -69,7 +70,6 @@ require (
|
|||||||
github.com/libp2p/go-libp2p-tls v0.1.0
|
github.com/libp2p/go-libp2p-tls v0.1.0
|
||||||
github.com/libp2p/go-libp2p-yamux v0.2.1
|
github.com/libp2p/go-libp2p-yamux v0.2.1
|
||||||
github.com/libp2p/go-maddr-filter v0.0.5
|
github.com/libp2p/go-maddr-filter v0.0.5
|
||||||
github.com/mattn/go-sqlite3 v1.12.0
|
|
||||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
|
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
|
||||||
github.com/minio/sha256-simd v0.1.1
|
github.com/minio/sha256-simd v0.1.1
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
|
12
go.sum
12
go.sum
@ -10,6 +10,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||||||
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
||||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||||
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
||||||
|
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
||||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||||
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
|
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
|
||||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||||
@ -115,6 +116,12 @@ github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:
|
|||||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
||||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||||
|
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
|
||||||
|
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
|
||||||
|
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
|
||||||
|
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
||||||
|
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
|
||||||
|
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||||
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
|
||||||
github.com/filecoin-project/go-paramfetch v0.0.1 h1:gV7bs5YaqlgpGFMiLxInGK2L1FyCXUE0rimz4L7ghoE=
|
github.com/filecoin-project/go-paramfetch v0.0.1 h1:gV7bs5YaqlgpGFMiLxInGK2L1FyCXUE0rimz4L7ghoE=
|
||||||
github.com/filecoin-project/go-paramfetch v0.0.1/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc=
|
github.com/filecoin-project/go-paramfetch v0.0.1/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc=
|
||||||
@ -358,6 +365,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88=
|
github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88=
|
||||||
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
|
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
|
||||||
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
|
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
|
||||||
@ -561,8 +570,6 @@ github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg
|
|||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do=
|
|
||||||
github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
@ -709,6 +716,7 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
|
|||||||
github.com/urfave/cli/v2 v2.0.0 h1:+HU9SCbu8GnEUFtIBfuUNXN39ofWViIEJIp6SURMpCg=
|
github.com/urfave/cli/v2 v2.0.0 h1:+HU9SCbu8GnEUFtIBfuUNXN39ofWViIEJIp6SURMpCg=
|
||||||
github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
|
Loading…
Reference in New Issue
Block a user