-
Notifications
You must be signed in to change notification settings - Fork 212
/
identities.go
125 lines (114 loc) · 3.81 KB
/
identities.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package identities
import (
"context"
"fmt"
"time"
"github.com/spacemeshos/go-spacemesh/codec"
"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/go-spacemesh/malfeasance/wire"
"github.com/spacemeshos/go-spacemesh/sql"
)
// SetMalicious records identity as malicious.
func SetMalicious(db sql.Executor, nodeID types.NodeID, proof []byte, received time.Time) error {
_, err := db.Exec(`insert into identities (pubkey, proof, received)
values (?1, ?2, ?3)
on conflict do nothing;`,
func(stmt *sql.Statement) {
stmt.BindBytes(1, nodeID.Bytes())
stmt.BindBytes(2, proof)
stmt.BindInt64(3, received.UnixNano())
}, nil,
)
if err != nil {
return fmt.Errorf("set malicious %v: %w", nodeID, err)
}
return nil
}
// IsMalicious returns true if identity is known to be malicious.
func IsMalicious(db sql.Executor, nodeID types.NodeID) (bool, error) {
rows, err := db.Exec("select 1 from identities where pubkey = ?1;",
func(stmt *sql.Statement) {
stmt.BindBytes(1, nodeID.Bytes())
}, nil)
if err != nil {
return false, fmt.Errorf("is malicious %v: %w", nodeID, err)
}
return rows > 0, nil
}
// GetMalfeasanceProof returns the malfeasance proof for the given identity.
func GetMalfeasanceProof(db sql.Executor, nodeID types.NodeID) (*wire.MalfeasanceProof, error) {
var (
data []byte
received time.Time
)
rows, err := db.Exec("select proof, received from identities where pubkey = ?1;",
func(stmt *sql.Statement) {
stmt.BindBytes(1, nodeID.Bytes())
}, func(stmt *sql.Statement) bool {
data = make([]byte, stmt.ColumnLen(0))
stmt.ColumnBytes(0, data[:])
received = time.Unix(0, stmt.ColumnInt64(1)).Local()
return true
})
if err != nil {
return nil, fmt.Errorf("proof %v: %w", nodeID, err)
}
if rows == 0 {
return nil, sql.ErrNotFound
}
var proof wire.MalfeasanceProof
if err = codec.Decode(data, &proof); err != nil {
return nil, err
}
proof.SetReceived(received.Local())
return &proof, nil
}
// GetBlobSizes returns the sizes of the blobs corresponding to malfeasance proofs for the
// specified identities. For non-existent proofs, the corresponding items are set to -1.
func GetBlobSizes(db sql.Executor, ids [][]byte) (sizes []int, err error) {
return sql.GetBlobSizes(db, "select pubkey, length(proof) from identities where pubkey in", ids)
}
// LoadMalfeasanceBlob returns the malfeasance proof in raw bytes for the given identity.
func LoadMalfeasanceBlob(ctx context.Context, db sql.Executor, nodeID []byte, blob *sql.Blob) error {
return sql.LoadBlob(db, "select proof from identities where pubkey = ?1;", nodeID, blob)
}
// IterateMalicious invokes the specified callback for each malicious node ID.
// It stops if the callback returns an error.
func IterateMalicious(
db sql.Executor,
callback func(total int, id types.NodeID) error,
) error {
var callbackErr error
dec := func(stmt *sql.Statement) bool {
var nid types.NodeID
total := stmt.ColumnInt(0)
stmt.ColumnBytes(1, nid[:])
if callbackErr = callback(total, nid); callbackErr != nil {
return false
}
return true
}
// Get total count in the same select statement to avoid the need for transaction
if _, err := db.Exec(
"select (select count(*) from identities where proof is not null) as total, "+
"pubkey from identities where proof is not null", nil, dec); err != nil {
return fmt.Errorf("get malicious identities: %w", err)
}
return callbackErr
}
// GetMalicious retrives malicious node IDs from the database.
func GetMalicious(db sql.Executor) (nids []types.NodeID, err error) {
if err = IterateMalicious(db, func(total int, nid types.NodeID) error {
if nids == nil {
nids = make([]types.NodeID, 0, total)
}
nids = append(nids, nid)
return nil
}); err != nil {
return nil, err
}
if len(nids) != cap(nids) {
panic("BUG: bad malicious node ID count")
}
return nids, nil
}