mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
272 lines
5.7 KiB
Go
272 lines
5.7 KiB
Go
package simulations
|
|
|
|
import (
|
|
"bytes"
|
|
"math/rand"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/event"
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
|
)
|
|
|
|
// Journal is an instance of a guaranteed no-loss subscription using event.TypeMux
|
|
// Network components POST events to the TypeMux, which then is read by the journal
|
|
// Each journal belongs to a subscription
|
|
type Journal struct {
|
|
lock sync.Mutex
|
|
counter int
|
|
cursor int
|
|
sub event.Subscription
|
|
events []*event.Event
|
|
}
|
|
|
|
// func (self *Journal) SnapshotAt(pos int) {}
|
|
|
|
// NewJournal constructor takes eventer and types to subscribe to
|
|
func NewJournal(eventer *event.TypeMux, types ...interface{}) *Journal {
|
|
self := &Journal{}
|
|
self.sub = eventer.Subscribe(types...)
|
|
go func() {
|
|
self.Write()
|
|
}()
|
|
return self
|
|
}
|
|
|
|
func (self *Journal) Close() {
|
|
self.sub.Unsubscribe()
|
|
}
|
|
|
|
// Write is a forever loop
|
|
func (self *Journal) Write() {
|
|
for {
|
|
select {
|
|
case ev, ok := <-self.sub.Chan():
|
|
if !ok {
|
|
return
|
|
}
|
|
self.append(ev)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (self *Journal) append(evs ...*event.Event) {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
self.events = append(self.events, evs...)
|
|
}
|
|
|
|
func (self *Journal) WaitEntries(n int) {
|
|
for self.NewEntries() < n {
|
|
}
|
|
}
|
|
|
|
func (self *Journal) NewEntries() int {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
return len(self.events) - self.cursor
|
|
}
|
|
|
|
func (self *Journal) Read(f func(*event.Event) bool) (read int, err error) {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
for self.cursor < len(self.events) && f(self.events[self.cursor]) {
|
|
read++
|
|
self.cursor++
|
|
}
|
|
self.reset(self.cursor)
|
|
return read, nil
|
|
}
|
|
|
|
func (self *Journal) Reset(n int) {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
self.reset(n)
|
|
}
|
|
|
|
func (self *Journal) reset(n int) {
|
|
if n > self.counter {
|
|
n = self.counter
|
|
}
|
|
self.events = self.events[self.cursor:]
|
|
self.cursor = 0
|
|
}
|
|
|
|
func (self *Journal) Counter() int {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
return self.counter
|
|
}
|
|
|
|
// type History()
|
|
|
|
func (self *Journal) Cursor() int {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
return self.cursor
|
|
}
|
|
|
|
// deltas: changes in the number of cumulative actions: non-negative integers.
|
|
// base unit is the fixed minimal interval between two measurements (time quantum)
|
|
// acceleration : to slow down you just set the base unit higher.
|
|
// to speed up: skip x number of base units
|
|
// frequency: given as the (constant or average) number of base units between measurements
|
|
// if resolution is expressed as the inverse of frequency = preserved information
|
|
// setting the acceleration
|
|
// beginning of the record (lifespan) of the network is index 0
|
|
// acceleration means that snapshots are rarer so the same span can be generated by the journal
|
|
// then update logs can be compressed (toonly one state transition per affected node)
|
|
// epoch, epochcount
|
|
|
|
type Delta struct {
|
|
On int
|
|
Off int
|
|
}
|
|
|
|
func oneOutOf(n int) int {
|
|
t := rand.Intn(n)
|
|
if t == 0 {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func deltas(i int) (d []*Delta) {
|
|
if i == 0 {
|
|
return []*Delta{
|
|
&Delta{10, 0},
|
|
&Delta{20, 0},
|
|
}
|
|
}
|
|
return []*Delta{
|
|
&Delta{oneOutOf(10), oneOutOf(10)},
|
|
&Delta{oneOutOf(2), oneOutOf(2)},
|
|
}
|
|
}
|
|
|
|
func mockJournalTest(nw *Network, ticker *<-chan time.Time) {
|
|
|
|
ids := RandomNodeIDs(100)
|
|
action := "Off"
|
|
for n := 0; ; n++ {
|
|
select {
|
|
case <-*ticker:
|
|
var entries []*Entry
|
|
|
|
if n == 0 {
|
|
|
|
entries = []*Entry{
|
|
&Entry{
|
|
Type: "Node",
|
|
Action: "On",
|
|
Object: &SimNode{ID: ids[0]},
|
|
},
|
|
&Entry{
|
|
Type: "Node",
|
|
Action: "On",
|
|
Object: &SimNode{ID: ids[1]},
|
|
},
|
|
}
|
|
} else {
|
|
sc := &SimConn{
|
|
Caller: ids[0],
|
|
Callee: ids[1],
|
|
}
|
|
if n%3 == 0 {
|
|
if action == "On" {
|
|
action = "Off"
|
|
} else {
|
|
action = "On"
|
|
}
|
|
entries = append(entries, &Entry{
|
|
Type: "Conn",
|
|
Action: action,
|
|
Object: sc,
|
|
})
|
|
}
|
|
}
|
|
|
|
glog.V(6).Info("entries: %v", entries)
|
|
nw.AppendEntries(entries...)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MockNetwork generates random connectivity events and posts them
|
|
// to the eventer
|
|
// The journal using the eventer can then be read to visualise or
|
|
// drive connections
|
|
func MockNetwork(eventer *event.TypeMux, ticker <-chan time.Time) {
|
|
ids := RandomNodeIDs(100)
|
|
var onNodes []*SimNode
|
|
offNodes := ids
|
|
var onConns []*SimConn
|
|
|
|
n := 0
|
|
for _ = range ticker {
|
|
ds := deltas(n)
|
|
for i := 0; len(offNodes) > 0 && i < ds[0].On; i++ {
|
|
c := rand.Intn(len(offNodes))
|
|
sn := &SimNode{ID: offNodes[c]}
|
|
err := eventer.Post(&Entry{
|
|
Type: "Node",
|
|
Action: "On",
|
|
Object: sn,
|
|
})
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
onNodes = append(onNodes, sn)
|
|
offNodes = append(offNodes[0:c], offNodes[c+1:]...)
|
|
}
|
|
for i := 0; len(onNodes) > 0 && i < ds[0].Off; i++ {
|
|
c := rand.Intn(len(onNodes))
|
|
sn := onNodes[c]
|
|
err := eventer.Post(&Entry{
|
|
Type: "Node",
|
|
Action: "Off",
|
|
Object: sn,
|
|
})
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
onNodes = append(onNodes[0:c], onNodes[c+1:]...)
|
|
offNodes = append(offNodes, sn.ID)
|
|
}
|
|
for i := 0; len(onNodes) > 1 && i < ds[1].On; i++ {
|
|
caller := onNodes[rand.Intn(len(onNodes))].ID
|
|
callee := onNodes[rand.Intn(len(onNodes))].ID
|
|
if bytes.Compare(caller[:], callee[:]) >= 0 {
|
|
i--
|
|
continue
|
|
}
|
|
sc := &SimConn{
|
|
Caller: caller,
|
|
Callee: callee,
|
|
}
|
|
err := eventer.Post(&Entry{
|
|
Type: "Conn",
|
|
Action: "On",
|
|
Object: sc,
|
|
})
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
onConns = append(onConns, sc)
|
|
}
|
|
for i := 0; len(onConns) > 0 && i < ds[1].Off; i++ {
|
|
c := rand.Intn(len(onConns))
|
|
err := eventer.Post(&Entry{
|
|
Type: "Conn",
|
|
Action: "Off",
|
|
Object: onConns[c],
|
|
})
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
onConns = append(onConns[0:c], onConns[c+1:]...)
|
|
}
|
|
n++
|
|
}
|
|
|
|
}
|