-
Notifications
You must be signed in to change notification settings - Fork 106
/
statetrie_test.go
203 lines (180 loc) · 5.8 KB
/
statetrie_test.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package byzcoin
import (
"bytes"
"encoding/binary"
"encoding/hex"
"math/big"
"strconv"
"strings"
"testing"
"time"
"golang.org/x/xerrors"
"github.com/stretchr/testify/require"
"go.dedis.ch/onet/v3/log"
"go.dedis.ch/kyber/v3/util/random"
"go.dedis.ch/cothority/v3/darc"
)
// TestStateTrie is a sanity check for setting and retrieving keys, values and
// index. The main functionalities are tested in the trie package.
func TestStateTrie(t *testing.T) {
b := newBCTRun(t, nil)
defer b.CloseAll()
st, err := b.Services[0].getStateTrie(b.Genesis.SkipChainID())
require.NoError(t, err)
require.NotNil(t, st)
require.NotEqual(t, -1, st.GetIndex())
key := []byte("testInstance")
contractID := "testContract"
value := []byte("testValue")
version := uint64(123)
darcID := darc.ID([]byte("123"))
sc := StateChange{
StateAction: Create,
InstanceID: key,
ContractID: contractID,
Value: value,
Version: version,
DarcID: darcID,
}
// store with bad expected root hash should fail, value should not be inside
require.Error(t, st.VerifiedStoreAll([]StateChange{sc}, 5, CurrentVersion, []byte("badhash")))
_, _, _, _, err = st.GetValues(key)
require.True(t, xerrors.Is(err, errKeyNotSet))
// store the state changes normally using StoreAll and it should work
require.NoError(t, st.StoreAll([]StateChange{sc}, 5, CurrentVersion))
require.Equal(t, st.GetIndex(), 5)
require.NoError(t, st.StoreAll([]StateChange{sc}, 6, CurrentVersion))
require.Equal(t, st.GetIndex(), 6)
_, _, _, _, err = st.GetValues(append(key, byte(0)))
require.True(t, xerrors.Is(err, errKeyNotSet))
val, ver, cid, did, err := st.GetValues(key)
require.NoError(t, err)
require.Equal(t, value, val)
require.Equal(t, version, ver)
require.Equal(t, cid, string(contractID))
require.True(t, did.Equal(darcID))
// test the staging state trie, most of the tests are done in the trie package
key2 := []byte("key2")
val2 := []byte("val2")
sst := st.MakeStagingStateTrie()
oldRoot := sst.GetRoot()
require.NoError(t, sst.Set(key2, val2))
newRoot := sst.GetRoot()
require.False(t, bytes.Equal(oldRoot, newRoot))
candidateVal2, err := sst.Get(key2)
require.NoError(t, err)
require.True(t, bytes.Equal(val2, candidateVal2))
// test the commit of staging state trie, root should be computed differently now, but it should be the same
require.NoError(t, sst.Commit())
require.True(t, bytes.Equal(sst.GetRoot(), newRoot))
}
// TestDarcRetrieval is used as a benchmark here. It stores a lot of darcs
// that are all linked in a tree-structure, and then uses EvalExprDarc to
// check whether an identity is valid or not.
// Use with
// go test -v -cpuprofile cpu.prof -run TestDarcRetrieval
// go tool pprof -pdf cpu.prof
func TestDarcRetrieval(t *testing.T) {
darcDepth := 3
darcWidth := 10
entries := 1000
bArgs := defaultBCTArgs
bArgs.PropagationInterval = 10 * time.Second
b := newBCTRun(t, &bArgs)
defer b.CloseAll()
// When running with `-cpuprofile`, additional go-routines are added,
// which should be ignored.
log.AddUserUninterestingGoroutine("/runtime/cpuprof.go")
st, err := b.Services[0].getStateTrie(b.Genesis.SkipChainID())
require.NoError(t, err)
require.NotEqual(t, -1, st.GetIndex())
log.Lvl1("Creating random entries")
value := make([]byte, 128)
scs := make([]StateChange, entries)
for e := 0; e < entries; e++ {
scs[e] = StateChange{
StateAction: Create,
InstanceID: random.Bits(256, true, random.New()),
ContractID: ContractDarcID,
Value: value,
}
}
require.NoError(t, st.StoreAll(scs, 5, CurrentVersion))
log.Lvl1("Creating darcs")
counter := 0
// Store the tree of darcs in a 2D-array with the root in index 0 and
// the leafs in index darcDepth-1
darcs := make([][]*darc.Darc, darcDepth)
for d := darcDepth - 1; d >= 0; d-- {
width := big.NewInt(0).Exp(big.NewInt(int64(darcWidth)),
big.NewInt(int64(d)), nil).Int64()
darcs[d] = make([]*darc.Darc, width)
// The leafs just hold a dummy reference to a darc
if d == darcDepth-1 {
for i := range darcs[d] {
counter++
id := make([]byte, 32)
binary.BigEndian.PutUint64(id, uint64(counter))
ids := []darc.Identity{darc.NewIdentityDarc(id)}
rules := darc.InitRules(ids, ids)
darcs[d][i] = darc.NewDarc(rules, []byte(strconv.Itoa(counter)))
}
} else {
for i := range darcs[d] {
counter++
ids := make([]darc.Identity, darcWidth)
for id := range ids {
ids[id] = darc.NewIdentityDarc(
darcs[d+1][i*darcWidth+id].GetBaseID())
}
rules := darc.InitRules(ids, ids)
darcs[d][i] = darc.NewDarc(rules, []byte(strconv.Itoa(counter)))
}
}
}
scs = make([]StateChange, 0, counter)
for d, dd := range darcs {
for w, dw := range dd {
log.Lvlf3("darc[%d][%d]: %x has %s", d, w,
dw.GetBaseID(), dw.Rules)
buf, err := dw.ToProto()
require.NoError(t, err)
scs = append(scs, StateChange{
StateAction: Create,
InstanceID: dw.GetBaseID(),
ContractID: ContractDarcID,
Value: buf,
Version: 0,
})
}
}
require.NoError(t, st.StoreAll(scs, 5, CurrentVersion))
getDarcs := func(s string, latest bool) *darc.Darc {
if !latest {
log.Error("cannot handle intermediate darcs")
return nil
}
id, err := hex.DecodeString(strings.Replace(s, "darc:", "", 1))
if err != nil || len(id) != 32 {
log.Error("invalid darc id", s, len(id), err)
return nil
}
d, err := st.LoadDarc(id)
if err != nil {
return nil
}
return d
}
log.Lvl1("Starting to search")
root := darcs[0][0]
rootSign := root.Rules.GetSignExpr()
start := time.Now()
for i := range darcs[darcDepth-1] {
id := darc.ID(make([]byte, 32))
binary.BigEndian.PutUint64(id, uint64(i+1))
darcID := darc.NewIdentityDarc(id)
err = darc.EvalExprDarc(rootSign, getDarcs, true, darcID.String())
require.NoError(t, err)
}
log.Lvl1("time to search:", time.Since(start))
}