mirror of https://github.com/poanetwork/gecko.git
Merge branch 'denali' into tighten-cert-permissions
This commit is contained in:
commit
e3b62549cb
|
@ -38,7 +38,18 @@ func (h *Health) Handler() *common.HTTPHandler {
|
||||||
newServer.RegisterCodec(codec, "application/json")
|
newServer.RegisterCodec(codec, "application/json")
|
||||||
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
|
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
|
||||||
newServer.RegisterService(h, "health")
|
newServer.RegisterService(h, "health")
|
||||||
return &common.HTTPHandler{LockOptions: common.NoLock, Handler: newServer}
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodGet { // GET request --> return 200 if getLiveness returns true, else 500
|
||||||
|
if _, healthy := h.health.Results(); healthy {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newServer.ServeHTTP(w, r) // Other request --> use JSON RPC
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return &common.HTTPHandler{LockOptions: common.NoLock, Handler: handler}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterHeartbeat adds a check with default options and a CheckFn that checks
|
// RegisterHeartbeat adds a check with default options and a CheckFn that checks
|
||||||
|
|
|
@ -21,6 +21,7 @@ type Database struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
mem map[string]valueDelete
|
mem map[string]valueDelete
|
||||||
db database.Database
|
db database.Database
|
||||||
|
batch database.Batch
|
||||||
}
|
}
|
||||||
|
|
||||||
type valueDelete struct {
|
type valueDelete struct {
|
||||||
|
@ -33,6 +34,7 @@ func New(db database.Database) *Database {
|
||||||
return &Database{
|
return &Database{
|
||||||
mem: make(map[string]valueDelete, memdb.DefaultSize),
|
mem: make(map[string]valueDelete, memdb.DefaultSize),
|
||||||
db: db,
|
db: db,
|
||||||
|
batch: db.NewBatch(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +171,7 @@ func (db *Database) SetDatabase(newDB database.Database) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
db.db = newDB
|
db.db = newDB
|
||||||
|
db.batch = newDB.NewBatch()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +209,9 @@ func (db *Database) Abort() {
|
||||||
|
|
||||||
func (db *Database) abort() { db.mem = make(map[string]valueDelete, memdb.DefaultSize) }
|
func (db *Database) abort() { db.mem = make(map[string]valueDelete, memdb.DefaultSize) }
|
||||||
|
|
||||||
// CommitBatch returns a batch that will commit all pending writes to the underlying database
|
// CommitBatch returns a batch that will commit all pending writes to the
|
||||||
|
// underlying database. The returned batch should be written before future calls
|
||||||
|
// to this DB unless the batch will never be written.
|
||||||
func (db *Database) CommitBatch() (database.Batch, error) {
|
func (db *Database) CommitBatch() (database.Batch, error) {
|
||||||
db.lock.Lock()
|
db.lock.Lock()
|
||||||
defer db.lock.Unlock()
|
defer db.lock.Unlock()
|
||||||
|
@ -219,21 +224,21 @@ func (db *Database) commitBatch() (database.Batch, error) {
|
||||||
return nil, database.ErrClosed
|
return nil, database.ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
batch := db.db.NewBatch()
|
db.batch.Reset()
|
||||||
for key, value := range db.mem {
|
for key, value := range db.mem {
|
||||||
if value.delete {
|
if value.delete {
|
||||||
if err := batch.Delete([]byte(key)); err != nil {
|
if err := db.batch.Delete([]byte(key)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if err := batch.Put([]byte(key), value.value); err != nil {
|
} else if err := db.batch.Put([]byte(key), value.value); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := batch.Write(); err != nil {
|
if err := db.batch.Write(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return batch, nil
|
return db.batch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements the database.Database interface
|
// Close implements the database.Database interface
|
||||||
|
|
|
@ -26,8 +26,8 @@ func TestNetworkName(t *testing.T) {
|
||||||
if name := NetworkName(EverestID); name != EverestName {
|
if name := NetworkName(EverestID); name != EverestName {
|
||||||
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, EverestName)
|
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, EverestName)
|
||||||
}
|
}
|
||||||
if name := NetworkName(TestnetID); name != EverestName {
|
if name := NetworkName(DenaliID); name != DenaliName {
|
||||||
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, EverestName)
|
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, DenaliName)
|
||||||
}
|
}
|
||||||
if name := NetworkName(4294967295); name != "network-4294967295" {
|
if name := NetworkName(4294967295); name != "network-4294967295" {
|
||||||
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, "network-4294967295")
|
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, "network-4294967295")
|
||||||
|
|
|
@ -18,7 +18,7 @@ var (
|
||||||
DenaliID uint32 = 3
|
DenaliID uint32 = 3
|
||||||
EverestID uint32 = 4
|
EverestID uint32 = 4
|
||||||
|
|
||||||
TestnetID uint32 = 4
|
TestnetID uint32 = 3
|
||||||
LocalID uint32 = 12345
|
LocalID uint32 = 12345
|
||||||
|
|
||||||
MainnetName = "mainnet"
|
MainnetName = "mainnet"
|
||||||
|
|
|
@ -30,7 +30,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dbVersion = "v0.6.0"
|
dbVersion = "v0.5.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Results of parsing the CLI
|
// Results of parsing the CLI
|
||||||
|
|
|
@ -170,21 +170,23 @@ const (
|
||||||
Version
|
Version
|
||||||
GetPeerList
|
GetPeerList
|
||||||
PeerList
|
PeerList
|
||||||
Ping
|
|
||||||
Pong
|
|
||||||
// Bootstrapping:
|
// Bootstrapping:
|
||||||
GetAcceptedFrontier
|
GetAcceptedFrontier
|
||||||
AcceptedFrontier
|
AcceptedFrontier
|
||||||
GetAccepted
|
GetAccepted
|
||||||
Accepted
|
Accepted
|
||||||
GetAncestors
|
|
||||||
MultiPut
|
|
||||||
// Consensus:
|
// Consensus:
|
||||||
Get
|
Get
|
||||||
Put
|
Put
|
||||||
PushQuery
|
PushQuery
|
||||||
PullQuery
|
PullQuery
|
||||||
Chits
|
Chits
|
||||||
|
|
||||||
|
// TODO: Reorder these messages when we transition to everest
|
||||||
|
GetAncestors
|
||||||
|
MultiPut
|
||||||
|
Ping
|
||||||
|
Pong
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defines the messages that can be sent/received with this network
|
// Defines the messages that can be sent/received with this network
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (p *peer) Start() {
|
||||||
// Initially send the version to the peer
|
// Initially send the version to the peer
|
||||||
go p.Version()
|
go p.Version()
|
||||||
go p.requestVersion()
|
go p.requestVersion()
|
||||||
go p.sendPings()
|
// go p.sendPings()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *peer) sendPings() {
|
func (p *peer) sendPings() {
|
||||||
|
@ -107,10 +107,10 @@ func (p *peer) requestVersion() {
|
||||||
func (p *peer) ReadMessages() {
|
func (p *peer) ReadMessages() {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
if err := p.conn.SetReadDeadline(p.net.clock.Time().Add(p.net.pingPongTimeout)); err != nil {
|
// if err := p.conn.SetReadDeadline(p.net.clock.Time().Add(p.net.pingPongTimeout)); err != nil {
|
||||||
p.net.log.Verbo("error on setting the connection read timeout %s", err)
|
// p.net.log.Verbo("error on setting the connection read timeout %s", err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
pendingBuffer := wrappers.Packer{}
|
pendingBuffer := wrappers.Packer{}
|
||||||
readBuffer := make([]byte, 1<<10)
|
readBuffer := make([]byte, 1<<10)
|
||||||
|
@ -246,11 +246,11 @@ func (p *peer) handle(msg Msg) {
|
||||||
currentTime := p.net.clock.Time()
|
currentTime := p.net.clock.Time()
|
||||||
atomic.StoreInt64(&p.lastReceived, currentTime.Unix())
|
atomic.StoreInt64(&p.lastReceived, currentTime.Unix())
|
||||||
|
|
||||||
if err := p.conn.SetReadDeadline(currentTime.Add(p.net.pingPongTimeout)); err != nil {
|
// if err := p.conn.SetReadDeadline(currentTime.Add(p.net.pingPongTimeout)); err != nil {
|
||||||
p.net.log.Verbo("error on setting the connection read timeout %s, closing the connection", err)
|
// p.net.log.Verbo("error on setting the connection read timeout %s, closing the connection", err)
|
||||||
p.Close()
|
// p.Close()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
op := msg.Op()
|
op := msg.Op()
|
||||||
msgMetrics := p.net.message(op)
|
msgMetrics := p.net.message(op)
|
||||||
|
|
|
@ -57,7 +57,7 @@ var (
|
||||||
genesisHashKey = []byte("genesisID")
|
genesisHashKey = []byte("genesisID")
|
||||||
|
|
||||||
// Version is the version of this code
|
// Version is the version of this code
|
||||||
Version = version.NewDefaultVersion("avalanche", 0, 6, 0)
|
Version = version.NewDefaultVersion("avalanche", 0, 5, 6)
|
||||||
versionParser = version.NewDefaultParser()
|
versionParser = version.NewDefaultParser()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -141,7 +141,9 @@ func (ta *Topological) RecordPoll(responses ids.UniqueBag) error {
|
||||||
votes := ta.pushVotes(kahns, leaves)
|
votes := ta.pushVotes(kahns, leaves)
|
||||||
// Update the conflict graph: O(|Transactions|)
|
// Update the conflict graph: O(|Transactions|)
|
||||||
ta.ctx.Log.Verbo("Updating consumer confidences based on:\n%s", &votes)
|
ta.ctx.Log.Verbo("Updating consumer confidences based on:\n%s", &votes)
|
||||||
ta.cg.RecordPoll(votes)
|
if err := ta.cg.RecordPoll(votes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Update the dag: O(|Live Set|)
|
// Update the dag: O(|Live Set|)
|
||||||
return ta.updateFrontiers()
|
return ta.updateFrontiers()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,830 +4,7 @@
|
||||||
package avalanche
|
package avalanche
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/ava-labs/gecko/snow"
|
|
||||||
"github.com/ava-labs/gecko/snow/choices"
|
|
||||||
"github.com/ava-labs/gecko/snow/consensus/snowball"
|
|
||||||
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTopologicalParams(t *testing.T) { ParamsTest(t, TopologicalFactory{}) }
|
func TestTopological(t *testing.T) { ConsensusTest(t, TopologicalFactory{}) }
|
||||||
|
|
||||||
func TestTopologicalAdd(t *testing.T) { AddTest(t, TopologicalFactory{}) }
|
|
||||||
|
|
||||||
func TestTopologicalVertexIssued(t *testing.T) { VertexIssuedTest(t, TopologicalFactory{}) }
|
|
||||||
|
|
||||||
func TestTopologicalTxIssued(t *testing.T) { TxIssuedTest(t, TopologicalFactory{}) }
|
|
||||||
|
|
||||||
func TestAvalancheVoting(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 2,
|
|
||||||
Alpha: 2,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 2,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
ta.Add(vtx1)
|
|
||||||
|
|
||||||
sm := make(ids.UniqueBag)
|
|
||||||
sm.Add(0, vtx1.id)
|
|
||||||
sm.Add(1, vtx1.id)
|
|
||||||
ta.RecordPoll(sm)
|
|
||||||
|
|
||||||
if ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too early")
|
|
||||||
} else if !ids.UnsortedEquals([]ids.ID{vtx1.id}, ta.Preferences().List()) {
|
|
||||||
t.Fatalf("Initial frontier failed to be set")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.RecordPoll(sm)
|
|
||||||
|
|
||||||
if !ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too late")
|
|
||||||
} else if !ids.UnsortedEquals([]ids.ID{vtx1.id}, ta.Preferences().List()) {
|
|
||||||
t.Fatalf("Initial frontier failed to be set")
|
|
||||||
} else if tx0.Status() != choices.Rejected {
|
|
||||||
t.Fatalf("Tx should have been rejected")
|
|
||||||
} else if tx1.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("Tx should have been accepted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheIgnoreInvalidVoting(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 3,
|
|
||||||
Alpha: 2,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
ta.Add(vtx1)
|
|
||||||
|
|
||||||
sm := make(ids.UniqueBag)
|
|
||||||
|
|
||||||
sm.Add(0, vtx0.id)
|
|
||||||
sm.Add(1, vtx1.id)
|
|
||||||
|
|
||||||
// Add Illegal Vote cast by Response 2
|
|
||||||
sm.Add(2, vtx0.id)
|
|
||||||
sm.Add(2, vtx1.id)
|
|
||||||
|
|
||||||
ta.RecordPoll(sm)
|
|
||||||
|
|
||||||
if ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too early")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheTransitiveVoting(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 2,
|
|
||||||
Alpha: 2,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 2,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID(), GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[1])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: []Vertex{vtx0},
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 2,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
vtx2 := &Vtx{
|
|
||||||
dependencies: []Vertex{vtx1},
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 3,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
ta.Add(vtx1)
|
|
||||||
ta.Add(vtx2)
|
|
||||||
|
|
||||||
sm1 := make(ids.UniqueBag)
|
|
||||||
sm1.Add(0, vtx0.id)
|
|
||||||
sm1.Add(1, vtx2.id)
|
|
||||||
ta.RecordPoll(sm1)
|
|
||||||
|
|
||||||
if ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too early")
|
|
||||||
} else if !ids.UnsortedEquals([]ids.ID{vtx2.id}, ta.Preferences().List()) {
|
|
||||||
t.Fatalf("Initial frontier failed to be set")
|
|
||||||
} else if tx0.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("Tx should have been accepted")
|
|
||||||
}
|
|
||||||
|
|
||||||
sm2 := make(ids.UniqueBag)
|
|
||||||
sm2.Add(0, vtx2.id)
|
|
||||||
sm2.Add(1, vtx2.id)
|
|
||||||
ta.RecordPoll(sm2)
|
|
||||||
|
|
||||||
if !ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too late")
|
|
||||||
} else if !ids.UnsortedEquals([]ids.ID{vtx2.id}, ta.Preferences().List()) {
|
|
||||||
t.Fatalf("Initial frontier failed to be set")
|
|
||||||
} else if tx0.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("Tx should have been accepted")
|
|
||||||
} else if tx1.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("Tx should have been accepted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheSplitVoting(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 2,
|
|
||||||
Alpha: 2,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 2,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
ta.Add(vtx1)
|
|
||||||
|
|
||||||
sm1 := make(ids.UniqueBag)
|
|
||||||
sm1.Add(0, vtx0.id)
|
|
||||||
sm1.Add(1, vtx1.id)
|
|
||||||
ta.RecordPoll(sm1)
|
|
||||||
|
|
||||||
if !ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too late")
|
|
||||||
} else if !ids.UnsortedEquals([]ids.ID{vtx0.id, vtx1.id}, ta.Preferences().List()) {
|
|
||||||
t.Fatalf("Initial frontier failed to be set")
|
|
||||||
} else if tx0.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("Tx should have been accepted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheTransitiveRejection(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 2,
|
|
||||||
Alpha: 2,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 2,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID(), GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx2 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx2.Ins.Add(utxos[1])
|
|
||||||
|
|
||||||
vtx2 := &Vtx{
|
|
||||||
dependencies: []Vertex{vtx0},
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx2},
|
|
||||||
height: 2,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
ta.Add(vtx1)
|
|
||||||
ta.Add(vtx2)
|
|
||||||
|
|
||||||
sm := make(ids.UniqueBag)
|
|
||||||
sm.Add(0, vtx1.id)
|
|
||||||
sm.Add(1, vtx1.id)
|
|
||||||
ta.RecordPoll(sm)
|
|
||||||
|
|
||||||
if ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too early")
|
|
||||||
} else if !ids.UnsortedEquals([]ids.ID{vtx1.id}, ta.Preferences().List()) {
|
|
||||||
t.Fatalf("Initial frontier failed to be set")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.RecordPoll(sm)
|
|
||||||
|
|
||||||
if ta.Finalized() {
|
|
||||||
t.Fatalf("An avalanche instance finalized too early")
|
|
||||||
} else if !ids.UnsortedEquals([]ids.ID{vtx1.id}, ta.Preferences().List()) {
|
|
||||||
t.Fatalf("Initial frontier failed to be set")
|
|
||||||
} else if tx0.Status() != choices.Rejected {
|
|
||||||
t.Fatalf("Tx should have been rejected")
|
|
||||||
} else if tx1.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("Tx should have been accepted")
|
|
||||||
} else if tx2.Status() != choices.Processing {
|
|
||||||
t.Fatalf("Tx should not have been decided")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.preferenceCache = make(map[[32]byte]bool)
|
|
||||||
ta.virtuousCache = make(map[[32]byte]bool)
|
|
||||||
|
|
||||||
ta.update(vtx2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheVirtuous(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 2,
|
|
||||||
Alpha: 2,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 2,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID(), GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
if virtuous := ta.Virtuous(); virtuous.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
|
||||||
} else if !virtuous.Contains(vts[0].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
} else if !virtuous.Contains(vts[1].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
}
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx2 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx2.Ins.Add(utxos[1])
|
|
||||||
|
|
||||||
vtx2 := &Vtx{
|
|
||||||
dependencies: []Vertex{vtx0},
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx2},
|
|
||||||
height: 2,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
|
|
||||||
if virtuous := ta.Virtuous(); virtuous.Len() != 1 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
|
||||||
} else if !virtuous.Contains(vtx0.id) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx1)
|
|
||||||
|
|
||||||
if virtuous := ta.Virtuous(); virtuous.Len() != 1 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
|
||||||
} else if !virtuous.Contains(vtx0.id) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.updateFrontiers()
|
|
||||||
|
|
||||||
if virtuous := ta.Virtuous(); virtuous.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
|
||||||
} else if !virtuous.Contains(vts[0].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
} else if !virtuous.Contains(vts[1].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx2)
|
|
||||||
|
|
||||||
if virtuous := ta.Virtuous(); virtuous.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
|
||||||
} else if !virtuous.Contains(vts[0].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
} else if !virtuous.Contains(vts[1].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.updateFrontiers()
|
|
||||||
|
|
||||||
if virtuous := ta.Virtuous(); virtuous.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
|
||||||
} else if !virtuous.Contains(vts[0].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
} else if !virtuous.Contains(vts[1].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheIsVirtuous(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 2,
|
|
||||||
Alpha: 2,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 2,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID(), GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
if virtuous := ta.Virtuous(); virtuous.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
|
||||||
} else if !virtuous.Contains(vts[0].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
} else if !virtuous.Contains(vts[1].ID()) {
|
|
||||||
t.Fatalf("Wrong virtuous")
|
|
||||||
}
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ta.IsVirtuous(tx0) {
|
|
||||||
t.Fatalf("Should be virtuous.")
|
|
||||||
} else if !ta.IsVirtuous(tx1) {
|
|
||||||
t.Fatalf("Should be virtuous.")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
|
|
||||||
if !ta.IsVirtuous(tx0) {
|
|
||||||
t.Fatalf("Should be virtuous.")
|
|
||||||
} else if ta.IsVirtuous(tx1) {
|
|
||||||
t.Fatalf("Should not be virtuous.")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx1)
|
|
||||||
|
|
||||||
if ta.IsVirtuous(tx0) {
|
|
||||||
t.Fatalf("Should not be virtuous.")
|
|
||||||
} else if ta.IsVirtuous(tx1) {
|
|
||||||
t.Fatalf("Should not be virtuous.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheQuiesce(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 1,
|
|
||||||
Alpha: 1,
|
|
||||||
BetaVirtuous: 1,
|
|
||||||
BetaRogue: 1,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID(), GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx2 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx2.Ins.Add(utxos[1])
|
|
||||||
|
|
||||||
vtx2 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx2},
|
|
||||||
height: 2,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
|
|
||||||
if ta.Quiesce() {
|
|
||||||
t.Fatalf("Shouldn't quiesce")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx1)
|
|
||||||
|
|
||||||
if !ta.Quiesce() {
|
|
||||||
t.Fatalf("Should quiesce")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx2)
|
|
||||||
|
|
||||||
if ta.Quiesce() {
|
|
||||||
t.Fatalf("Shouldn't quiesce")
|
|
||||||
}
|
|
||||||
|
|
||||||
sm := make(ids.UniqueBag)
|
|
||||||
sm.Add(0, vtx2.id)
|
|
||||||
ta.RecordPoll(sm)
|
|
||||||
|
|
||||||
if !ta.Quiesce() {
|
|
||||||
t.Fatalf("Should quiesce")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAvalancheOrphans(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Parameters: snowball.Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 1,
|
|
||||||
Alpha: 1,
|
|
||||||
BetaVirtuous: math.MaxInt32,
|
|
||||||
BetaRogue: math.MaxInt32,
|
|
||||||
ConcurrentRepolls: 1,
|
|
||||||
},
|
|
||||||
Parents: 2,
|
|
||||||
BatchSize: 1,
|
|
||||||
}
|
|
||||||
vts := []Vertex{&Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}, &Vtx{
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Accepted,
|
|
||||||
}}
|
|
||||||
utxos := []ids.ID{GenerateID(), GenerateID()}
|
|
||||||
|
|
||||||
ta := Topological{}
|
|
||||||
ta.Initialize(snow.DefaultContextTest(), params, vts)
|
|
||||||
|
|
||||||
tx0 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx0.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx0 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx0},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx1 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx1.Ins.Add(utxos[0])
|
|
||||||
|
|
||||||
vtx1 := &Vtx{
|
|
||||||
dependencies: vts,
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx1},
|
|
||||||
height: 1,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx2 := &snowstorm.TestTx{
|
|
||||||
Identifier: GenerateID(),
|
|
||||||
Stat: choices.Processing,
|
|
||||||
}
|
|
||||||
tx2.Ins.Add(utxos[1])
|
|
||||||
|
|
||||||
vtx2 := &Vtx{
|
|
||||||
dependencies: []Vertex{vtx0},
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: []snowstorm.Tx{tx2},
|
|
||||||
height: 2,
|
|
||||||
status: choices.Processing,
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx0)
|
|
||||||
|
|
||||||
if orphans := ta.Orphans(); orphans.Len() != 0 {
|
|
||||||
t.Fatalf("Wrong number of orphans")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx1)
|
|
||||||
|
|
||||||
if orphans := ta.Orphans(); orphans.Len() != 0 {
|
|
||||||
t.Fatalf("Wrong number of orphans")
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.Add(vtx2)
|
|
||||||
|
|
||||||
if orphans := ta.Orphans(); orphans.Len() != 0 {
|
|
||||||
t.Fatalf("Wrong number of orphans")
|
|
||||||
}
|
|
||||||
|
|
||||||
sm := make(ids.UniqueBag)
|
|
||||||
sm.Add(0, vtx1.id)
|
|
||||||
ta.RecordPoll(sm)
|
|
||||||
|
|
||||||
if orphans := ta.Orphans(); orphans.Len() != 1 {
|
|
||||||
t.Fatalf("Wrong number of orphans")
|
|
||||||
} else if !orphans.Contains(tx2.ID()) {
|
|
||||||
t.Fatalf("Wrong orphan")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ type Vtx struct {
|
||||||
height uint64
|
height uint64
|
||||||
status choices.Status
|
status choices.Status
|
||||||
|
|
||||||
|
Validity error
|
||||||
bytes []byte
|
bytes []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +29,8 @@ func (v *Vtx) Parents() []Vertex { return v.dependencies }
|
||||||
func (v *Vtx) Height() uint64 { return v.height }
|
func (v *Vtx) Height() uint64 { return v.height }
|
||||||
func (v *Vtx) Txs() []snowstorm.Tx { return v.txs }
|
func (v *Vtx) Txs() []snowstorm.Tx { return v.txs }
|
||||||
func (v *Vtx) Status() choices.Status { return v.status }
|
func (v *Vtx) Status() choices.Status { return v.status }
|
||||||
func (v *Vtx) Live() {}
|
func (v *Vtx) Accept() error { v.status = choices.Accepted; return v.Validity }
|
||||||
func (v *Vtx) Accept() error { v.status = choices.Accepted; return nil }
|
func (v *Vtx) Reject() error { v.status = choices.Rejected; return v.Validity }
|
||||||
func (v *Vtx) Reject() error { v.status = choices.Rejected; return nil }
|
|
||||||
func (v *Vtx) Bytes() []byte { return v.bytes }
|
func (v *Vtx) Bytes() []byte { return v.bytes }
|
||||||
|
|
||||||
type sortVts []*Vtx
|
type sortVts []*Vtx
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package snowball
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ByzantineFactory implements Factory by returning a byzantine struct
|
|
||||||
type ByzantineFactory struct{}
|
|
||||||
|
|
||||||
// New implements Factory
|
|
||||||
func (ByzantineFactory) New() Consensus { return &Byzantine{} }
|
|
||||||
|
|
||||||
// Byzantine is a naive implementation of a multi-choice snowball instance
|
|
||||||
type Byzantine struct {
|
|
||||||
// params contains all the configurations of a snowball instance
|
|
||||||
params Parameters
|
|
||||||
|
|
||||||
// Hardcode the preference
|
|
||||||
preference ids.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize implements the Consensus interface
|
|
||||||
func (b *Byzantine) Initialize(params Parameters, choice ids.ID) {
|
|
||||||
b.params = params
|
|
||||||
b.preference = choice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parameters implements the Consensus interface
|
|
||||||
func (b *Byzantine) Parameters() Parameters { return b.params }
|
|
||||||
|
|
||||||
// Add implements the Consensus interface
|
|
||||||
func (b *Byzantine) Add(choice ids.ID) {}
|
|
||||||
|
|
||||||
// Preference implements the Consensus interface
|
|
||||||
func (b *Byzantine) Preference() ids.ID { return b.preference }
|
|
||||||
|
|
||||||
// RecordPoll implements the Consensus interface
|
|
||||||
func (b *Byzantine) RecordPoll(votes ids.Bag) {}
|
|
||||||
|
|
||||||
// RecordUnsuccessfulPoll implements the Consensus interface
|
|
||||||
func (b *Byzantine) RecordUnsuccessfulPoll() {}
|
|
||||||
|
|
||||||
// Finalized implements the Consensus interface
|
|
||||||
func (b *Byzantine) Finalized() bool { return true }
|
|
||||||
func (b *Byzantine) String() string { return b.preference.String() }
|
|
|
@ -1,54 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package snowball
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestByzantine(t *testing.T) {
|
|
||||||
params := Parameters{
|
|
||||||
Metrics: prometheus.NewRegistry(),
|
|
||||||
K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
byzFactory := ByzantineFactory{}
|
|
||||||
byz := byzFactory.New()
|
|
||||||
byz.Initialize(params, Blue)
|
|
||||||
|
|
||||||
if ret := byz.Parameters(); ret != params {
|
|
||||||
t.Fatalf("Should have returned the correct params")
|
|
||||||
}
|
|
||||||
|
|
||||||
byz.Add(Green)
|
|
||||||
|
|
||||||
if pref := byz.Preference(); !pref.Equals(Blue) {
|
|
||||||
t.Fatalf("Wrong preference, expected %s returned %s", Blue, pref)
|
|
||||||
}
|
|
||||||
|
|
||||||
oneGreen := ids.Bag{}
|
|
||||||
oneGreen.Add(Green)
|
|
||||||
byz.RecordPoll(oneGreen)
|
|
||||||
|
|
||||||
if pref := byz.Preference(); !pref.Equals(Blue) {
|
|
||||||
t.Fatalf("Wrong preference, expected %s returned %s", Blue, pref)
|
|
||||||
}
|
|
||||||
|
|
||||||
byz.RecordUnsuccessfulPoll()
|
|
||||||
|
|
||||||
if pref := byz.Preference(); !pref.Equals(Blue) {
|
|
||||||
t.Fatalf("Wrong preference, expected %s returned %s", Blue, pref)
|
|
||||||
}
|
|
||||||
|
|
||||||
if final := byz.Finalized(); !final {
|
|
||||||
t.Fatalf("Should be marked as accepted")
|
|
||||||
}
|
|
||||||
|
|
||||||
if str := byz.String(); str != Blue.String() {
|
|
||||||
t.Fatalf("Wrong string, expected %s returned %s", Blue, str)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,6 +11,46 @@ import (
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ByzantineFactory implements Factory by returning a byzantine struct
|
||||||
|
type ByzantineFactory struct{}
|
||||||
|
|
||||||
|
// New implements Factory
|
||||||
|
func (ByzantineFactory) New() Consensus { return &Byzantine{} }
|
||||||
|
|
||||||
|
// Byzantine is a naive implementation of a multi-choice snowball instance
|
||||||
|
type Byzantine struct {
|
||||||
|
// params contains all the configurations of a snowball instance
|
||||||
|
params Parameters
|
||||||
|
|
||||||
|
// Hardcode the preference
|
||||||
|
preference ids.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize implements the Consensus interface
|
||||||
|
func (b *Byzantine) Initialize(params Parameters, choice ids.ID) {
|
||||||
|
b.params = params
|
||||||
|
b.preference = choice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameters implements the Consensus interface
|
||||||
|
func (b *Byzantine) Parameters() Parameters { return b.params }
|
||||||
|
|
||||||
|
// Add implements the Consensus interface
|
||||||
|
func (b *Byzantine) Add(choice ids.ID) {}
|
||||||
|
|
||||||
|
// Preference implements the Consensus interface
|
||||||
|
func (b *Byzantine) Preference() ids.ID { return b.preference }
|
||||||
|
|
||||||
|
// RecordPoll implements the Consensus interface
|
||||||
|
func (b *Byzantine) RecordPoll(votes ids.Bag) {}
|
||||||
|
|
||||||
|
// RecordUnsuccessfulPoll implements the Consensus interface
|
||||||
|
func (b *Byzantine) RecordUnsuccessfulPoll() {}
|
||||||
|
|
||||||
|
// Finalized implements the Consensus interface
|
||||||
|
func (b *Byzantine) Finalized() bool { return true }
|
||||||
|
func (b *Byzantine) String() string { return b.preference.String() }
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Red = ids.Empty.Prefix(0)
|
Red = ids.Empty.Prefix(0)
|
||||||
Blue = ids.Empty.Prefix(1)
|
Blue = ids.Empty.Prefix(1)
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (f *Flat) Parameters() Parameters { return f.params }
|
||||||
// RecordPoll implements the Consensus interface
|
// RecordPoll implements the Consensus interface
|
||||||
func (f *Flat) RecordPoll(votes ids.Bag) {
|
func (f *Flat) RecordPoll(votes ids.Bag) {
|
||||||
if pollMode, numVotes := votes.Mode(); numVotes >= f.params.Alpha {
|
if pollMode, numVotes := votes.Mode(); numVotes >= f.params.Alpha {
|
||||||
f.nnarySnowball.RecordSuccessfulPoll(pollMode)
|
f.RecordSuccessfulPoll(pollMode)
|
||||||
} else {
|
} else {
|
||||||
f.RecordUnsuccessfulPoll()
|
f.RecordUnsuccessfulPoll()
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (sf *nnarySnowflake) RecordSuccessfulPoll(choice ids.ID) {
|
||||||
return // This instace is already decided.
|
return // This instace is already decided.
|
||||||
}
|
}
|
||||||
|
|
||||||
if preference := sf.nnarySlush.Preference(); preference.Equals(choice) {
|
if preference := sf.Preference(); preference.Equals(choice) {
|
||||||
sf.confidence++
|
sf.confidence++
|
||||||
} else {
|
} else {
|
||||||
// confidence is set to 1 because there has already been 1 successful
|
// confidence is set to 1 because there has already been 1 successful
|
||||||
|
|
|
@ -17,6 +17,7 @@ type TestBlock struct {
|
||||||
height int
|
height int
|
||||||
status choices.Status
|
status choices.Status
|
||||||
bytes []byte
|
bytes []byte
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TestBlock) Parent() Block { return b.parent }
|
func (b *TestBlock) Parent() Block { return b.parent }
|
||||||
|
@ -27,16 +28,16 @@ func (b *TestBlock) Accept() error {
|
||||||
return errors.New("Dis-agreement")
|
return errors.New("Dis-agreement")
|
||||||
}
|
}
|
||||||
b.status = choices.Accepted
|
b.status = choices.Accepted
|
||||||
return nil
|
return b.err
|
||||||
}
|
}
|
||||||
func (b *TestBlock) Reject() error {
|
func (b *TestBlock) Reject() error {
|
||||||
if b.status.Decided() && b.status != choices.Rejected {
|
if b.status.Decided() && b.status != choices.Rejected {
|
||||||
return errors.New("Dis-agreement")
|
return errors.New("Dis-agreement")
|
||||||
}
|
}
|
||||||
b.status = choices.Rejected
|
b.status = choices.Rejected
|
||||||
return nil
|
return b.err
|
||||||
}
|
}
|
||||||
func (b *TestBlock) Verify() error { return nil }
|
func (b *TestBlock) Verify() error { return b.err }
|
||||||
func (b *TestBlock) Bytes() []byte { return b.bytes }
|
func (b *TestBlock) Bytes() []byte { return b.bytes }
|
||||||
|
|
||||||
type sortBlocks []*TestBlock
|
type sortBlocks []*TestBlock
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package snowman
|
package snowman
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -42,6 +43,10 @@ var (
|
||||||
MetricsProcessingErrorTest,
|
MetricsProcessingErrorTest,
|
||||||
MetricsAcceptedErrorTest,
|
MetricsAcceptedErrorTest,
|
||||||
MetricsRejectedErrorTest,
|
MetricsRejectedErrorTest,
|
||||||
|
ErrorOnInitialRejectionTest,
|
||||||
|
ErrorOnAcceptTest,
|
||||||
|
ErrorOnRejectSiblingTest,
|
||||||
|
ErrorOnTransitiveRejectionTest,
|
||||||
RandomizedConsistencyTest,
|
RandomizedConsistencyTest,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -71,11 +76,9 @@ func InitializeTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
if p := sm.Parameters(); p != params {
|
if p := sm.Parameters(); p != params {
|
||||||
t.Fatalf("Wrong returned parameters")
|
t.Fatalf("Wrong returned parameters")
|
||||||
}
|
} else if pref := sm.Preference(); !pref.Equals(GenesisID) {
|
||||||
if pref := sm.Preference(); !pref.Equals(GenesisID) {
|
|
||||||
t.Fatalf("Wrong preference returned")
|
t.Fatalf("Wrong preference returned")
|
||||||
}
|
} else if !sm.Finalized() {
|
||||||
if !sm.Finalized() {
|
|
||||||
t.Fatalf("Wrong should have marked the instance as being finalized")
|
t.Fatalf("Wrong should have marked the instance as being finalized")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,9 +104,9 @@ func AddToTailTest(t *testing.T, factory Factory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding to the previous preference will update the preference
|
// Adding to the previous preference will update the preference
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if pref := sm.Preference(); !pref.Equals(block.id) {
|
} else if pref := sm.Preference(); !pref.Equals(block.id) {
|
||||||
t.Fatalf("Wrong preference. Expected %s, got %s", block.id, pref)
|
t.Fatalf("Wrong preference. Expected %s, got %s", block.id, pref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,17 +136,17 @@ func AddToNonTailTest(t *testing.T, factory Factory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding to the previous preference will update the preference
|
// Adding to the previous preference will update the preference
|
||||||
sm.Add(firstBlock)
|
if err := sm.Add(firstBlock); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
} else if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
||||||
t.Fatalf("Wrong preference. Expected %s, got %s", firstBlock.id, pref)
|
t.Fatalf("Wrong preference. Expected %s, got %s", firstBlock.id, pref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding to something other than the previous preference won't update the
|
// Adding to something other than the previous preference won't update the
|
||||||
// preference
|
// preference
|
||||||
sm.Add(secondBlock)
|
if err := sm.Add(secondBlock); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
} else if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
||||||
t.Fatalf("Wrong preference. Expected %s, got %s", firstBlock.id, pref)
|
t.Fatalf("Wrong preference. Expected %s, got %s", firstBlock.id, pref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,9 +174,9 @@ func AddToUnknownTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
// Adding a block with an unknown parent means the parent must have already
|
// Adding a block with an unknown parent means the parent must have already
|
||||||
// been rejected. Therefore the block should be immediately rejected
|
// been rejected. Therefore the block should be immediately rejected
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if pref := sm.Preference(); !pref.Equals(GenesisID) {
|
} else if pref := sm.Preference(); !pref.Equals(GenesisID) {
|
||||||
t.Fatalf("Wrong preference. Expected %s, got %s", GenesisID, pref)
|
t.Fatalf("Wrong preference. Expected %s, got %s", GenesisID, pref)
|
||||||
} else if status := block.Status(); status != choices.Rejected {
|
} else if status := block.Status(); status != choices.Rejected {
|
||||||
t.Fatalf("Should have rejected the block")
|
t.Fatalf("Should have rejected the block")
|
||||||
|
@ -269,9 +272,9 @@ func IssuedIssuedTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if !sm.Issued(block) {
|
} else if !sm.Issued(block) {
|
||||||
t.Fatalf("Should have marked a pending block as having been issued")
|
t.Fatalf("Should have marked a pending block as having been issued")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,24 +299,23 @@ func RecordPollAcceptSingleBlockTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(block.id)
|
votes.Add(block.id)
|
||||||
|
if err := sm.RecordPoll(votes); err != nil {
|
||||||
sm.RecordPoll(votes)
|
t.Fatal(err)
|
||||||
|
} else if pref := sm.Preference(); !pref.Equals(block.id) {
|
||||||
if pref := sm.Preference(); !pref.Equals(block.id) {
|
|
||||||
t.Fatalf("Preference returned the wrong block")
|
t.Fatalf("Preference returned the wrong block")
|
||||||
} else if sm.Finalized() {
|
} else if sm.Finalized() {
|
||||||
t.Fatalf("Snowman instance finalized too soon")
|
t.Fatalf("Snowman instance finalized too soon")
|
||||||
} else if status := block.Status(); status != choices.Processing {
|
} else if status := block.Status(); status != choices.Processing {
|
||||||
t.Fatalf("Block's status changed unexpectedly")
|
t.Fatalf("Block's status changed unexpectedly")
|
||||||
}
|
} else if err := sm.RecordPoll(votes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
sm.RecordPoll(votes)
|
} else if pref := sm.Preference(); !pref.Equals(block.id) {
|
||||||
|
|
||||||
if pref := sm.Preference(); !pref.Equals(block.id) {
|
|
||||||
t.Fatalf("Preference returned the wrong block")
|
t.Fatalf("Preference returned the wrong block")
|
||||||
} else if !sm.Finalized() {
|
} else if !sm.Finalized() {
|
||||||
t.Fatalf("Snowman instance didn't finalize")
|
t.Fatalf("Snowman instance didn't finalize")
|
||||||
|
@ -347,15 +349,18 @@ func RecordPollAcceptAndRejectTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(firstBlock)
|
if err := sm.Add(firstBlock); err != nil {
|
||||||
sm.Add(secondBlock)
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(secondBlock); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(firstBlock.id)
|
votes.Add(firstBlock.id)
|
||||||
|
|
||||||
sm.RecordPoll(votes)
|
if err := sm.RecordPoll(votes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
} else if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
||||||
t.Fatalf("Preference returned the wrong block")
|
t.Fatalf("Preference returned the wrong block")
|
||||||
} else if sm.Finalized() {
|
} else if sm.Finalized() {
|
||||||
t.Fatalf("Snowman instance finalized too soon")
|
t.Fatalf("Snowman instance finalized too soon")
|
||||||
|
@ -363,11 +368,9 @@ func RecordPollAcceptAndRejectTest(t *testing.T, factory Factory) {
|
||||||
t.Fatalf("Block's status changed unexpectedly")
|
t.Fatalf("Block's status changed unexpectedly")
|
||||||
} else if status := secondBlock.Status(); status != choices.Processing {
|
} else if status := secondBlock.Status(); status != choices.Processing {
|
||||||
t.Fatalf("Block's status changed unexpectedly")
|
t.Fatalf("Block's status changed unexpectedly")
|
||||||
}
|
} else if err := sm.RecordPoll(votes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
sm.RecordPoll(votes)
|
} else if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
||||||
|
|
||||||
if pref := sm.Preference(); !pref.Equals(firstBlock.id) {
|
|
||||||
t.Fatalf("Preference returned the wrong block")
|
t.Fatalf("Preference returned the wrong block")
|
||||||
} else if !sm.Finalized() {
|
} else if !sm.Finalized() {
|
||||||
t.Fatalf("Snowman instance didn't finalize")
|
t.Fatalf("Snowman instance didn't finalize")
|
||||||
|
@ -394,9 +397,9 @@ func RecordPollWhenFinalizedTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(GenesisID)
|
votes.Add(GenesisID)
|
||||||
sm.RecordPoll(votes)
|
if err := sm.RecordPoll(votes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if !sm.Finalized() {
|
} else if !sm.Finalized() {
|
||||||
t.Fatalf("Consensus should still be finalized")
|
t.Fatalf("Consensus should still be finalized")
|
||||||
} else if pref := sm.Preference(); !GenesisID.Equals(pref) {
|
} else if pref := sm.Preference(); !GenesisID.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
|
@ -433,9 +436,13 @@ func RecordPollRejectTransitivelyTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block0)
|
if err := sm.Add(block0); err != nil {
|
||||||
sm.Add(block1)
|
t.Fatal(err)
|
||||||
sm.Add(block2)
|
} else if err := sm.Add(block1); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Current graph structure:
|
// Current graph structure:
|
||||||
// G
|
// G
|
||||||
|
@ -447,7 +454,9 @@ func RecordPollRejectTransitivelyTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(block0.id)
|
votes.Add(block0.id)
|
||||||
sm.RecordPoll(votes)
|
if err := sm.RecordPoll(votes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Current graph structure:
|
// Current graph structure:
|
||||||
// 0
|
// 0
|
||||||
|
@ -457,9 +466,7 @@ func RecordPollRejectTransitivelyTest(t *testing.T, factory Factory) {
|
||||||
t.Fatalf("Finalized too late")
|
t.Fatalf("Finalized too late")
|
||||||
} else if pref := sm.Preference(); !block0.id.Equals(pref) {
|
} else if pref := sm.Preference(); !block0.id.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
}
|
} else if status := block0.Status(); status != choices.Accepted {
|
||||||
|
|
||||||
if status := block0.Status(); status != choices.Accepted {
|
|
||||||
t.Fatalf("Wrong status returned")
|
t.Fatalf("Wrong status returned")
|
||||||
} else if status := block1.Status(); status != choices.Rejected {
|
} else if status := block1.Status(); status != choices.Rejected {
|
||||||
t.Fatalf("Wrong status returned")
|
t.Fatalf("Wrong status returned")
|
||||||
|
@ -503,10 +510,15 @@ func RecordPollTransitivelyResetConfidenceTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block0)
|
if err := sm.Add(block0); err != nil {
|
||||||
sm.Add(block1)
|
t.Fatal(err)
|
||||||
sm.Add(block2)
|
} else if err := sm.Add(block1); err != nil {
|
||||||
sm.Add(block3)
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Current graph structure:
|
// Current graph structure:
|
||||||
// G
|
// G
|
||||||
|
@ -517,26 +529,24 @@ func RecordPollTransitivelyResetConfidenceTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
votesFor2 := ids.Bag{}
|
votesFor2 := ids.Bag{}
|
||||||
votesFor2.Add(block2.id)
|
votesFor2.Add(block2.id)
|
||||||
sm.RecordPoll(votesFor2)
|
if err := sm.RecordPoll(votesFor2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if sm.Finalized() {
|
} else if sm.Finalized() {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
}
|
}
|
||||||
|
|
||||||
emptyVotes := ids.Bag{}
|
emptyVotes := ids.Bag{}
|
||||||
sm.RecordPoll(emptyVotes)
|
if err := sm.RecordPoll(emptyVotes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if sm.Finalized() {
|
} else if sm.Finalized() {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
}
|
} else if err := sm.RecordPoll(votesFor2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
sm.RecordPoll(votesFor2)
|
} else if sm.Finalized() {
|
||||||
|
|
||||||
if sm.Finalized() {
|
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
|
@ -544,23 +554,19 @@ func RecordPollTransitivelyResetConfidenceTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
votesFor3 := ids.Bag{}
|
votesFor3 := ids.Bag{}
|
||||||
votesFor3.Add(block3.id)
|
votesFor3.Add(block3.id)
|
||||||
sm.RecordPoll(votesFor3)
|
if err := sm.RecordPoll(votesFor3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if sm.Finalized() {
|
} else if sm.Finalized() {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
} else if pref := sm.Preference(); !block2.id.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
}
|
} else if err := sm.RecordPoll(votesFor3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
sm.RecordPoll(votesFor3)
|
} else if !sm.Finalized() {
|
||||||
|
|
||||||
if !sm.Finalized() {
|
|
||||||
t.Fatalf("Finalized too late")
|
t.Fatalf("Finalized too late")
|
||||||
} else if pref := sm.Preference(); !block3.id.Equals(pref) {
|
} else if pref := sm.Preference(); !block3.id.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
}
|
} else if status := block0.Status(); status != choices.Rejected {
|
||||||
|
|
||||||
if status := block0.Status(); status != choices.Rejected {
|
|
||||||
t.Fatalf("Wrong status returned")
|
t.Fatalf("Wrong status returned")
|
||||||
} else if status := block1.Status(); status != choices.Accepted {
|
} else if status := block1.Status(); status != choices.Accepted {
|
||||||
t.Fatalf("Wrong status returned")
|
t.Fatalf("Wrong status returned")
|
||||||
|
@ -592,19 +598,23 @@ func RecordPollInvalidVoteTest(t *testing.T, factory Factory) {
|
||||||
}
|
}
|
||||||
unknownBlockID := ids.Empty.Prefix(2)
|
unknownBlockID := ids.Empty.Prefix(2)
|
||||||
|
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
validVotes := ids.Bag{}
|
validVotes := ids.Bag{}
|
||||||
validVotes.Add(block.id)
|
validVotes.Add(block.id)
|
||||||
sm.RecordPoll(validVotes)
|
if err := sm.RecordPoll(validVotes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
invalidVotes := ids.Bag{}
|
invalidVotes := ids.Bag{}
|
||||||
invalidVotes.Add(unknownBlockID)
|
invalidVotes.Add(unknownBlockID)
|
||||||
sm.RecordPoll(invalidVotes)
|
if err := sm.RecordPoll(invalidVotes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
sm.RecordPoll(validVotes)
|
} else if err := sm.RecordPoll(validVotes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if sm.Finalized() {
|
} else if sm.Finalized() {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
} else if pref := sm.Preference(); !block.id.Equals(pref) {
|
} else if pref := sm.Preference(); !block.id.Equals(pref) {
|
||||||
t.Fatalf("Wrong preference listed")
|
t.Fatalf("Wrong preference listed")
|
||||||
|
@ -651,11 +661,17 @@ func RecordPollTransitiveVotingTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block0)
|
if err := sm.Add(block0); err != nil {
|
||||||
sm.Add(block1)
|
t.Fatal(err)
|
||||||
sm.Add(block2)
|
} else if err := sm.Add(block1); err != nil {
|
||||||
sm.Add(block3)
|
t.Fatal(err)
|
||||||
sm.Add(block4)
|
} else if err := sm.Add(block2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block4); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Current graph structure:
|
// Current graph structure:
|
||||||
// G
|
// G
|
||||||
|
@ -668,10 +684,14 @@ func RecordPollTransitiveVotingTest(t *testing.T, factory Factory) {
|
||||||
// Tail = 2
|
// Tail = 2
|
||||||
|
|
||||||
votes0_2_4 := ids.Bag{}
|
votes0_2_4 := ids.Bag{}
|
||||||
votes0_2_4.Add(block0.id)
|
votes0_2_4.Add(
|
||||||
votes0_2_4.Add(block2.id)
|
block0.id,
|
||||||
votes0_2_4.Add(block4.id)
|
block2.id,
|
||||||
sm.RecordPoll(votes0_2_4)
|
block4.id,
|
||||||
|
)
|
||||||
|
if err := sm.RecordPoll(votes0_2_4); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Current graph structure:
|
// Current graph structure:
|
||||||
// 0
|
// 0
|
||||||
|
@ -699,7 +719,9 @@ func RecordPollTransitiveVotingTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
dep2_2_2 := ids.Bag{}
|
dep2_2_2 := ids.Bag{}
|
||||||
dep2_2_2.AddCount(block2.id, 3)
|
dep2_2_2.AddCount(block2.id, 3)
|
||||||
sm.RecordPoll(dep2_2_2)
|
if err := sm.RecordPoll(dep2_2_2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Current graph structure:
|
// Current graph structure:
|
||||||
// 2
|
// 2
|
||||||
|
@ -757,20 +779,25 @@ func RecordPollDivergedVotingTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block0)
|
if err := sm.Add(block0); err != nil {
|
||||||
sm.Add(block1)
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block1); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
votes0 := ids.Bag{}
|
votes0 := ids.Bag{}
|
||||||
votes0.Add(block0.id)
|
votes0.Add(block0.id)
|
||||||
sm.RecordPoll(votes0)
|
if err := sm.RecordPoll(votes0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
sm.Add(block2)
|
} else if err := sm.Add(block2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// dep2 is already rejected.
|
// dep2 is already rejected.
|
||||||
|
|
||||||
sm.Add(block3)
|
if err := sm.Add(block3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if status := block0.Status(); status == choices.Accepted {
|
} else if status := block0.Status(); status == choices.Accepted {
|
||||||
t.Fatalf("Shouldn't be accepted yet")
|
t.Fatalf("Shouldn't be accepted yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,9 +805,9 @@ func RecordPollDivergedVotingTest(t *testing.T, factory Factory) {
|
||||||
// dep0. Because dep2 is already rejected, this will accept dep0.
|
// dep0. Because dep2 is already rejected, this will accept dep0.
|
||||||
votes3 := ids.Bag{}
|
votes3 := ids.Bag{}
|
||||||
votes3.Add(block3.id)
|
votes3.Add(block3.id)
|
||||||
sm.RecordPoll(votes3)
|
if err := sm.RecordPoll(votes3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if !sm.Finalized() {
|
} else if !sm.Finalized() {
|
||||||
t.Fatalf("Finalized too late")
|
t.Fatalf("Finalized too late")
|
||||||
} else if status := block0.Status(); status != choices.Accepted {
|
} else if status := block0.Status(); status != choices.Accepted {
|
||||||
t.Fatalf("Should be accepted")
|
t.Fatalf("Should be accepted")
|
||||||
|
@ -818,14 +845,15 @@ func MetricsProcessingErrorTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(block.id)
|
votes.Add(block.id)
|
||||||
|
if err := sm.RecordPoll(votes); err != nil {
|
||||||
sm.RecordPoll(votes)
|
t.Fatal(err)
|
||||||
|
} else if !sm.Finalized() {
|
||||||
if !sm.Finalized() {
|
|
||||||
t.Fatalf("Snowman instance didn't finalize")
|
t.Fatalf("Snowman instance didn't finalize")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -861,14 +889,15 @@ func MetricsAcceptedErrorTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(block.id)
|
votes.Add(block.id)
|
||||||
|
if err := sm.RecordPoll(votes); err != nil {
|
||||||
sm.RecordPoll(votes)
|
t.Fatal(err)
|
||||||
|
} else if !sm.Finalized() {
|
||||||
if !sm.Finalized() {
|
|
||||||
t.Fatalf("Snowman instance didn't finalize")
|
t.Fatalf("Snowman instance didn't finalize")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -904,18 +933,171 @@ func MetricsRejectedErrorTest(t *testing.T, factory Factory) {
|
||||||
status: choices.Processing,
|
status: choices.Processing,
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Add(block)
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(block.id)
|
votes.Add(block.id)
|
||||||
|
if err := sm.RecordPoll(votes); err != nil {
|
||||||
sm.RecordPoll(votes)
|
t.Fatal(err)
|
||||||
|
} else if !sm.Finalized() {
|
||||||
if !sm.Finalized() {
|
|
||||||
t.Fatalf("Snowman instance didn't finalize")
|
t.Fatalf("Snowman instance didn't finalize")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ErrorOnInitialRejectionTest(t *testing.T, factory Factory) {
|
||||||
|
sm := factory.New()
|
||||||
|
|
||||||
|
ctx := snow.DefaultContextTest()
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1,
|
||||||
|
Alpha: 1,
|
||||||
|
BetaVirtuous: 1,
|
||||||
|
BetaRogue: 1,
|
||||||
|
ConcurrentRepolls: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.Initialize(ctx, params, GenesisID)
|
||||||
|
|
||||||
|
rejectedBlock := &TestBlock{
|
||||||
|
id: ids.Empty.Prefix(1),
|
||||||
|
status: choices.Rejected,
|
||||||
|
}
|
||||||
|
|
||||||
|
block := &TestBlock{
|
||||||
|
parent: rejectedBlock,
|
||||||
|
id: ids.Empty.Prefix(2),
|
||||||
|
status: choices.Processing,
|
||||||
|
err: errors.New(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sm.Add(block); err == nil {
|
||||||
|
t.Fatalf("Should have errored on rejecting the rejectable block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorOnAcceptTest(t *testing.T, factory Factory) {
|
||||||
|
sm := factory.New()
|
||||||
|
|
||||||
|
ctx := snow.DefaultContextTest()
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1,
|
||||||
|
Alpha: 1,
|
||||||
|
BetaVirtuous: 1,
|
||||||
|
BetaRogue: 1,
|
||||||
|
ConcurrentRepolls: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.Initialize(ctx, params, GenesisID)
|
||||||
|
|
||||||
|
block := &TestBlock{
|
||||||
|
parent: Genesis,
|
||||||
|
id: ids.Empty.Prefix(1),
|
||||||
|
status: choices.Processing,
|
||||||
|
err: errors.New(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sm.Add(block); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
votes := ids.Bag{}
|
||||||
|
votes.Add(block.id)
|
||||||
|
if err := sm.RecordPoll(votes); err == nil {
|
||||||
|
t.Fatalf("Should have errored on accepted the block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorOnRejectSiblingTest(t *testing.T, factory Factory) {
|
||||||
|
sm := factory.New()
|
||||||
|
|
||||||
|
ctx := snow.DefaultContextTest()
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1,
|
||||||
|
Alpha: 1,
|
||||||
|
BetaVirtuous: 1,
|
||||||
|
BetaRogue: 1,
|
||||||
|
ConcurrentRepolls: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.Initialize(ctx, params, GenesisID)
|
||||||
|
|
||||||
|
block0 := &TestBlock{
|
||||||
|
parent: Genesis,
|
||||||
|
id: ids.Empty.Prefix(1),
|
||||||
|
status: choices.Processing,
|
||||||
|
}
|
||||||
|
block1 := &TestBlock{
|
||||||
|
parent: Genesis,
|
||||||
|
id: ids.Empty.Prefix(2),
|
||||||
|
status: choices.Processing,
|
||||||
|
err: errors.New(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sm.Add(block0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block1); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
votes := ids.Bag{}
|
||||||
|
votes.Add(block0.id)
|
||||||
|
if err := sm.RecordPoll(votes); err == nil {
|
||||||
|
t.Fatalf("Should have errored on rejecting the block's sibling")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorOnTransitiveRejectionTest(t *testing.T, factory Factory) {
|
||||||
|
sm := factory.New()
|
||||||
|
|
||||||
|
ctx := snow.DefaultContextTest()
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1,
|
||||||
|
Alpha: 1,
|
||||||
|
BetaVirtuous: 1,
|
||||||
|
BetaRogue: 1,
|
||||||
|
ConcurrentRepolls: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.Initialize(ctx, params, GenesisID)
|
||||||
|
|
||||||
|
block0 := &TestBlock{
|
||||||
|
parent: Genesis,
|
||||||
|
id: ids.Empty.Prefix(1),
|
||||||
|
status: choices.Processing,
|
||||||
|
}
|
||||||
|
block1 := &TestBlock{
|
||||||
|
parent: Genesis,
|
||||||
|
id: ids.Empty.Prefix(2),
|
||||||
|
status: choices.Processing,
|
||||||
|
}
|
||||||
|
block2 := &TestBlock{
|
||||||
|
parent: block1,
|
||||||
|
id: ids.Empty.Prefix(3),
|
||||||
|
status: choices.Processing,
|
||||||
|
err: errors.New(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sm.Add(block0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block1); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := sm.Add(block2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
votes := ids.Bag{}
|
||||||
|
votes.Add(block0.id)
|
||||||
|
if err := sm.RecordPoll(votes); err == nil {
|
||||||
|
t.Fatalf("Should have errored on transitively rejecting the block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func RandomizedConsistencyTest(t *testing.T, factory Factory) {
|
func RandomizedConsistencyTest(t *testing.T, factory Factory) {
|
||||||
numColors := 50
|
numColors := 50
|
||||||
numNodes := 100
|
numNodes := 100
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package snowstorm
|
package snowstorm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
@ -19,6 +20,28 @@ var (
|
||||||
Green = &TestTx{Identifier: ids.Empty.Prefix(1)}
|
Green = &TestTx{Identifier: ids.Empty.Prefix(1)}
|
||||||
Blue = &TestTx{Identifier: ids.Empty.Prefix(2)}
|
Blue = &TestTx{Identifier: ids.Empty.Prefix(2)}
|
||||||
Alpha = &TestTx{Identifier: ids.Empty.Prefix(3)}
|
Alpha = &TestTx{Identifier: ids.Empty.Prefix(3)}
|
||||||
|
|
||||||
|
Tests = []func(*testing.T, Factory){
|
||||||
|
MetricsTest,
|
||||||
|
ParamsTest,
|
||||||
|
IssuedTest,
|
||||||
|
LeftoverInputTest,
|
||||||
|
LowerConfidenceTest,
|
||||||
|
MiddleConfidenceTest,
|
||||||
|
IndependentTest,
|
||||||
|
VirtuousTest,
|
||||||
|
IsVirtuousTest,
|
||||||
|
QuiesceTest,
|
||||||
|
AcceptingDependencyTest,
|
||||||
|
RejectingDependencyTest,
|
||||||
|
VacuouslyAcceptedTest,
|
||||||
|
ConflictsTest,
|
||||||
|
VirtuousDependsOnRogueTest,
|
||||||
|
ErrorOnVacuouslyAcceptedTest,
|
||||||
|
ErrorOnAcceptedTest,
|
||||||
|
ErrorOnRejectingLowerConfidenceConflictTest,
|
||||||
|
ErrorOnRejectingHigherConfidenceConflictTest,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// R - G - B - A
|
// R - G - B - A
|
||||||
|
@ -46,6 +69,52 @@ func Setup() {
|
||||||
Alpha.Reset()
|
Alpha.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute all tests against a consensus implementation
|
||||||
|
func ConsensusTest(t *testing.T, factory Factory, prefix string) {
|
||||||
|
for _, test := range Tests {
|
||||||
|
test(t, factory)
|
||||||
|
}
|
||||||
|
StringTest(t, factory, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MetricsTest(t *testing.T, factory Factory) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
{
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 2,
|
||||||
|
}
|
||||||
|
params.Metrics.Register(prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "tx_processing",
|
||||||
|
}))
|
||||||
|
graph := factory.New()
|
||||||
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 2,
|
||||||
|
}
|
||||||
|
params.Metrics.Register(prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "tx_accepted",
|
||||||
|
}))
|
||||||
|
graph := factory.New()
|
||||||
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 2,
|
||||||
|
}
|
||||||
|
params.Metrics.Register(prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "tx_rejected",
|
||||||
|
}))
|
||||||
|
graph := factory.New()
|
||||||
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ParamsTest(t *testing.T, factory Factory) {
|
func ParamsTest(t *testing.T, factory Factory) {
|
||||||
Setup()
|
Setup()
|
||||||
|
|
||||||
|
@ -81,15 +150,13 @@ func IssuedTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
if issued := graph.Issued(Red); issued {
|
if issued := graph.Issued(Red); issued {
|
||||||
t.Fatalf("Haven't issued anything yet.")
|
t.Fatalf("Haven't issued anything yet.")
|
||||||
}
|
} else if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Red)
|
} else if issued := graph.Issued(Red); !issued {
|
||||||
|
|
||||||
if issued := graph.Issued(Red); !issued {
|
|
||||||
t.Fatalf("Have already issued.")
|
t.Fatalf("Have already issued.")
|
||||||
}
|
}
|
||||||
|
|
||||||
Blue.Accept()
|
_ = Blue.Accept()
|
||||||
|
|
||||||
if issued := graph.Issued(Blue); !issued {
|
if issued := graph.Issued(Blue); !issued {
|
||||||
t.Fatalf("Have already accepted.")
|
t.Fatalf("Have already accepted.")
|
||||||
|
@ -106,10 +173,12 @@ func LeftoverInputTest(t *testing.T, factory Factory) {
|
||||||
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
graph.Add(Red)
|
|
||||||
graph.Add(Green)
|
|
||||||
|
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 1 {
|
if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 1 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s got %s", Red.ID(), prefs.List()[0])
|
t.Fatalf("Wrong preference. Expected %s got %s", Red.ID(), prefs.List()[0])
|
||||||
|
@ -120,15 +189,13 @@ func LeftoverInputTest(t *testing.T, factory Factory) {
|
||||||
r := ids.Bag{}
|
r := ids.Bag{}
|
||||||
r.SetThreshold(2)
|
r.SetThreshold(2)
|
||||||
r.AddCount(Red.ID(), 2)
|
r.AddCount(Red.ID(), 2)
|
||||||
graph.RecordPoll(r)
|
if err := graph.RecordPoll(r); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 0 {
|
} else if prefs := graph.Preferences(); prefs.Len() != 0 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !graph.Finalized() {
|
} else if !graph.Finalized() {
|
||||||
t.Fatalf("Finalized too late")
|
t.Fatalf("Finalized too late")
|
||||||
}
|
} else if Red.Status() != choices.Accepted {
|
||||||
|
|
||||||
if Red.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("%s should have been accepted", Red.ID())
|
t.Fatalf("%s should have been accepted", Red.ID())
|
||||||
} else if Green.Status() != choices.Rejected {
|
} else if Green.Status() != choices.Rejected {
|
||||||
t.Fatalf("%s should have been rejected", Green.ID())
|
t.Fatalf("%s should have been rejected", Green.ID())
|
||||||
|
@ -145,11 +212,14 @@ func LowerConfidenceTest(t *testing.T, factory Factory) {
|
||||||
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
graph.Add(Red)
|
|
||||||
graph.Add(Green)
|
|
||||||
graph.Add(Blue)
|
|
||||||
|
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 1 {
|
if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Blue); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 1 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s got %s", Red.ID(), prefs.List()[0])
|
t.Fatalf("Wrong preference. Expected %s got %s", Red.ID(), prefs.List()[0])
|
||||||
|
@ -160,9 +230,9 @@ func LowerConfidenceTest(t *testing.T, factory Factory) {
|
||||||
r := ids.Bag{}
|
r := ids.Bag{}
|
||||||
r.SetThreshold(2)
|
r.SetThreshold(2)
|
||||||
r.AddCount(Red.ID(), 2)
|
r.AddCount(Red.ID(), 2)
|
||||||
graph.RecordPoll(r)
|
if err := graph.RecordPoll(r); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 1 {
|
} else if prefs := graph.Preferences(); prefs.Len() != 1 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Blue.ID()) {
|
} else if !prefs.Contains(Blue.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Blue.ID())
|
t.Fatalf("Wrong preference. Expected %s", Blue.ID())
|
||||||
|
@ -181,12 +251,16 @@ func MiddleConfidenceTest(t *testing.T, factory Factory) {
|
||||||
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
graph.Add(Red)
|
|
||||||
graph.Add(Green)
|
|
||||||
graph.Add(Alpha)
|
|
||||||
graph.Add(Blue)
|
|
||||||
|
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Alpha); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Blue); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
||||||
|
@ -199,9 +273,9 @@ func MiddleConfidenceTest(t *testing.T, factory Factory) {
|
||||||
r := ids.Bag{}
|
r := ids.Bag{}
|
||||||
r.SetThreshold(2)
|
r.SetThreshold(2)
|
||||||
r.AddCount(Red.ID(), 2)
|
r.AddCount(Red.ID(), 2)
|
||||||
graph.RecordPoll(r)
|
if err := graph.RecordPoll(r); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 1 {
|
} else if prefs := graph.Preferences(); prefs.Len() != 1 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Alpha.ID()) {
|
} else if !prefs.Contains(Alpha.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Alpha.ID())
|
t.Fatalf("Wrong preference. Expected %s", Alpha.ID())
|
||||||
|
@ -209,6 +283,7 @@ func MiddleConfidenceTest(t *testing.T, factory Factory) {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IndependentTest(t *testing.T, factory Factory) {
|
func IndependentTest(t *testing.T, factory Factory) {
|
||||||
Setup()
|
Setup()
|
||||||
|
|
||||||
|
@ -219,10 +294,12 @@ func IndependentTest(t *testing.T, factory Factory) {
|
||||||
K: 2, Alpha: 2, BetaVirtuous: 2, BetaRogue: 2,
|
K: 2, Alpha: 2, BetaVirtuous: 2, BetaRogue: 2,
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
graph.Add(Red)
|
|
||||||
graph.Add(Alpha)
|
|
||||||
|
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Alpha); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
||||||
|
@ -236,9 +313,9 @@ func IndependentTest(t *testing.T, factory Factory) {
|
||||||
ra.SetThreshold(2)
|
ra.SetThreshold(2)
|
||||||
ra.AddCount(Red.ID(), 2)
|
ra.AddCount(Red.ID(), 2)
|
||||||
ra.AddCount(Alpha.ID(), 2)
|
ra.AddCount(Alpha.ID(), 2)
|
||||||
graph.RecordPoll(ra)
|
if err := graph.RecordPoll(ra); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
||||||
|
@ -246,11 +323,9 @@ func IndependentTest(t *testing.T, factory Factory) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Alpha.ID())
|
t.Fatalf("Wrong preference. Expected %s", Alpha.ID())
|
||||||
} else if graph.Finalized() {
|
} else if graph.Finalized() {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
}
|
} else if err := graph.RecordPoll(ra); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.RecordPoll(ra)
|
} else if prefs := graph.Preferences(); prefs.Len() != 0 {
|
||||||
|
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 0 {
|
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !graph.Finalized() {
|
} else if !graph.Finalized() {
|
||||||
t.Fatalf("Finalized too late")
|
t.Fatalf("Finalized too late")
|
||||||
|
@ -267,35 +342,30 @@ func VirtuousTest(t *testing.T, factory Factory) {
|
||||||
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 1,
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
graph.Add(Red)
|
|
||||||
|
|
||||||
if virtuous := graph.Virtuous(); virtuous.Len() != 1 {
|
if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if virtuous := graph.Virtuous(); virtuous.Len() != 1 {
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
t.Fatalf("Wrong number of virtuous.")
|
||||||
} else if !virtuous.Contains(Red.ID()) {
|
} else if !virtuous.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong virtuous. Expected %s", Red.ID())
|
t.Fatalf("Wrong virtuous. Expected %s", Red.ID())
|
||||||
}
|
} else if err := graph.Add(Alpha); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Alpha)
|
} else if virtuous := graph.Virtuous(); virtuous.Len() != 2 {
|
||||||
|
|
||||||
if virtuous := graph.Virtuous(); virtuous.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
t.Fatalf("Wrong number of virtuous.")
|
||||||
} else if !virtuous.Contains(Red.ID()) {
|
} else if !virtuous.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong virtuous. Expected %s", Red.ID())
|
t.Fatalf("Wrong virtuous. Expected %s", Red.ID())
|
||||||
} else if !virtuous.Contains(Alpha.ID()) {
|
} else if !virtuous.Contains(Alpha.ID()) {
|
||||||
t.Fatalf("Wrong virtuous. Expected %s", Alpha.ID())
|
t.Fatalf("Wrong virtuous. Expected %s", Alpha.ID())
|
||||||
}
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Green)
|
} else if virtuous := graph.Virtuous(); virtuous.Len() != 1 {
|
||||||
|
|
||||||
if virtuous := graph.Virtuous(); virtuous.Len() != 1 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
t.Fatalf("Wrong number of virtuous.")
|
||||||
} else if !virtuous.Contains(Alpha.ID()) {
|
} else if !virtuous.Contains(Alpha.ID()) {
|
||||||
t.Fatalf("Wrong virtuous. Expected %s", Alpha.ID())
|
t.Fatalf("Wrong virtuous. Expected %s", Alpha.ID())
|
||||||
}
|
} else if err := graph.Add(Blue); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Blue)
|
} else if virtuous := graph.Virtuous(); virtuous.Len() != 0 {
|
||||||
|
|
||||||
if virtuous := graph.Virtuous(); virtuous.Len() != 0 {
|
|
||||||
t.Fatalf("Wrong number of virtuous.")
|
t.Fatalf("Wrong number of virtuous.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,11 +389,9 @@ func IsVirtuousTest(t *testing.T, factory Factory) {
|
||||||
t.Fatalf("Should be virtuous")
|
t.Fatalf("Should be virtuous")
|
||||||
} else if !graph.IsVirtuous(Alpha) {
|
} else if !graph.IsVirtuous(Alpha) {
|
||||||
t.Fatalf("Should be virtuous")
|
t.Fatalf("Should be virtuous")
|
||||||
}
|
} else if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Red)
|
} else if !graph.IsVirtuous(Red) {
|
||||||
|
|
||||||
if !graph.IsVirtuous(Red) {
|
|
||||||
t.Fatalf("Should be virtuous")
|
t.Fatalf("Should be virtuous")
|
||||||
} else if graph.IsVirtuous(Green) {
|
} else if graph.IsVirtuous(Green) {
|
||||||
t.Fatalf("Should not be virtuous")
|
t.Fatalf("Should not be virtuous")
|
||||||
|
@ -331,11 +399,9 @@ func IsVirtuousTest(t *testing.T, factory Factory) {
|
||||||
t.Fatalf("Should be virtuous")
|
t.Fatalf("Should be virtuous")
|
||||||
} else if !graph.IsVirtuous(Alpha) {
|
} else if !graph.IsVirtuous(Alpha) {
|
||||||
t.Fatalf("Should be virtuous")
|
t.Fatalf("Should be virtuous")
|
||||||
}
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Green)
|
} else if graph.IsVirtuous(Red) {
|
||||||
|
|
||||||
if graph.IsVirtuous(Red) {
|
|
||||||
t.Fatalf("Should not be virtuous")
|
t.Fatalf("Should not be virtuous")
|
||||||
} else if graph.IsVirtuous(Green) {
|
} else if graph.IsVirtuous(Green) {
|
||||||
t.Fatalf("Should not be virtuous")
|
t.Fatalf("Should not be virtuous")
|
||||||
|
@ -357,17 +423,13 @@ func QuiesceTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
if !graph.Quiesce() {
|
if !graph.Quiesce() {
|
||||||
t.Fatalf("Should quiesce")
|
t.Fatalf("Should quiesce")
|
||||||
}
|
} else if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Red)
|
} else if graph.Quiesce() {
|
||||||
|
|
||||||
if graph.Quiesce() {
|
|
||||||
t.Fatalf("Shouldn't quiesce")
|
t.Fatalf("Shouldn't quiesce")
|
||||||
}
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(Green)
|
} else if !graph.Quiesce() {
|
||||||
|
|
||||||
if !graph.Quiesce() {
|
|
||||||
t.Fatalf("Should quiesce")
|
t.Fatalf("Should quiesce")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,11 +452,13 @@ func AcceptingDependencyTest(t *testing.T, factory Factory) {
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
|
||||||
graph.Add(Red)
|
if err := graph.Add(Red); err != nil {
|
||||||
graph.Add(Green)
|
t.Fatal(err)
|
||||||
graph.Add(purple)
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
} else if err := graph.Add(purple); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
||||||
|
@ -410,10 +474,9 @@ func AcceptingDependencyTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
g := ids.Bag{}
|
g := ids.Bag{}
|
||||||
g.Add(Green.ID())
|
g.Add(Green.ID())
|
||||||
|
if err := graph.RecordPoll(g); err != nil {
|
||||||
graph.RecordPoll(g)
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Green.ID()) {
|
} else if !prefs.Contains(Green.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Green.ID())
|
t.Fatalf("Wrong preference. Expected %s", Green.ID())
|
||||||
|
@ -429,10 +492,9 @@ func AcceptingDependencyTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
rp := ids.Bag{}
|
rp := ids.Bag{}
|
||||||
rp.Add(Red.ID(), purple.ID())
|
rp.Add(Red.ID(), purple.ID())
|
||||||
|
if err := graph.RecordPoll(rp); err != nil {
|
||||||
graph.RecordPoll(rp)
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Green.ID()) {
|
} else if !prefs.Contains(Green.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Green.ID())
|
t.Fatalf("Wrong preference. Expected %s", Green.ID())
|
||||||
|
@ -448,10 +510,9 @@ func AcceptingDependencyTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
r := ids.Bag{}
|
r := ids.Bag{}
|
||||||
r.Add(Red.ID())
|
r.Add(Red.ID())
|
||||||
|
if err := graph.RecordPoll(r); err != nil {
|
||||||
graph.RecordPoll(r)
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 0 {
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 0 {
|
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if Red.Status() != choices.Accepted {
|
} else if Red.Status() != choices.Accepted {
|
||||||
t.Fatalf("Wrong status. %s should be %s", Red.ID(), choices.Accepted)
|
t.Fatalf("Wrong status. %s should be %s", Red.ID(), choices.Accepted)
|
||||||
|
@ -480,12 +541,15 @@ func RejectingDependencyTest(t *testing.T, factory Factory) {
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
|
||||||
graph.Add(Red)
|
if err := graph.Add(Red); err != nil {
|
||||||
graph.Add(Green)
|
t.Fatal(err)
|
||||||
graph.Add(Blue)
|
} else if err := graph.Add(Green); err != nil {
|
||||||
graph.Add(purple)
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Blue); err != nil {
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(purple); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
t.Fatalf("Wrong preference. Expected %s", Red.ID())
|
||||||
|
@ -503,10 +567,9 @@ func RejectingDependencyTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
gp := ids.Bag{}
|
gp := ids.Bag{}
|
||||||
gp.Add(Green.ID(), purple.ID())
|
gp.Add(Green.ID(), purple.ID())
|
||||||
|
if err := graph.RecordPoll(gp); err != nil {
|
||||||
graph.RecordPoll(gp)
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 2 {
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 2 {
|
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Green.ID()) {
|
} else if !prefs.Contains(Green.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Green.ID())
|
t.Fatalf("Wrong preference. Expected %s", Green.ID())
|
||||||
|
@ -520,11 +583,9 @@ func RejectingDependencyTest(t *testing.T, factory Factory) {
|
||||||
t.Fatalf("Wrong status. %s should be %s", Blue.ID(), choices.Processing)
|
t.Fatalf("Wrong status. %s should be %s", Blue.ID(), choices.Processing)
|
||||||
} else if purple.Status() != choices.Processing {
|
} else if purple.Status() != choices.Processing {
|
||||||
t.Fatalf("Wrong status. %s should be %s", purple.ID(), choices.Processing)
|
t.Fatalf("Wrong status. %s should be %s", purple.ID(), choices.Processing)
|
||||||
}
|
} else if err := graph.RecordPoll(gp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.RecordPoll(gp)
|
} else if prefs := graph.Preferences(); prefs.Len() != 0 {
|
||||||
|
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 0 {
|
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if Red.Status() != choices.Rejected {
|
} else if Red.Status() != choices.Rejected {
|
||||||
t.Fatalf("Wrong status. %s should be %s", Red.ID(), choices.Rejected)
|
t.Fatalf("Wrong status. %s should be %s", Red.ID(), choices.Rejected)
|
||||||
|
@ -553,9 +614,9 @@ func VacuouslyAcceptedTest(t *testing.T, factory Factory) {
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
|
||||||
graph.Add(purple)
|
if err := graph.Add(purple); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 0 {
|
} else if prefs := graph.Preferences(); prefs.Len() != 0 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if status := purple.Status(); status != choices.Accepted {
|
} else if status := purple.Status(); status != choices.Accepted {
|
||||||
t.Fatalf("Wrong status. %s should be %s", purple.ID(), choices.Accepted)
|
t.Fatalf("Wrong status. %s should be %s", purple.ID(), choices.Accepted)
|
||||||
|
@ -593,17 +654,15 @@ func ConflictsTest(t *testing.T, factory Factory) {
|
||||||
Ins: insPurple,
|
Ins: insPurple,
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.Add(purple)
|
if err := graph.Add(purple); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
if orangeConflicts := graph.Conflicts(orange); orangeConflicts.Len() != 1 {
|
} else if orangeConflicts := graph.Conflicts(orange); orangeConflicts.Len() != 1 {
|
||||||
t.Fatalf("Wrong number of conflicts")
|
t.Fatalf("Wrong number of conflicts")
|
||||||
} else if !orangeConflicts.Contains(purple.Identifier) {
|
} else if !orangeConflicts.Contains(purple.Identifier) {
|
||||||
t.Fatalf("Conflicts does not contain the right transaction")
|
t.Fatalf("Conflicts does not contain the right transaction")
|
||||||
}
|
} else if err := graph.Add(orange); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
graph.Add(orange)
|
} else if orangeConflicts := graph.Conflicts(orange); orangeConflicts.Len() != 1 {
|
||||||
|
|
||||||
if orangeConflicts := graph.Conflicts(orange); orangeConflicts.Len() != 1 {
|
|
||||||
t.Fatalf("Wrong number of conflicts")
|
t.Fatalf("Wrong number of conflicts")
|
||||||
} else if !orangeConflicts.Contains(purple.Identifier) {
|
} else if !orangeConflicts.Contains(purple.Identifier) {
|
||||||
t.Fatalf("Conflicts does not contain the right transaction")
|
t.Fatalf("Conflicts does not contain the right transaction")
|
||||||
|
@ -643,17 +702,20 @@ func VirtuousDependsOnRogueTest(t *testing.T, factory Factory) {
|
||||||
|
|
||||||
virtuous.Ins.Add(input2)
|
virtuous.Ins.Add(input2)
|
||||||
|
|
||||||
graph.Add(rogue1)
|
if err := graph.Add(rogue1); err != nil {
|
||||||
graph.Add(rogue2)
|
t.Fatal(err)
|
||||||
graph.Add(virtuous)
|
} else if err := graph.Add(rogue2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(virtuous); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
votes := ids.Bag{}
|
votes := ids.Bag{}
|
||||||
votes.Add(rogue1.ID())
|
votes.Add(rogue1.ID())
|
||||||
votes.Add(virtuous.ID())
|
votes.Add(virtuous.ID())
|
||||||
|
if err := graph.RecordPoll(votes); err != nil {
|
||||||
graph.RecordPoll(votes)
|
t.Fatal(err)
|
||||||
|
} else if status := rogue1.Status(); status != choices.Processing {
|
||||||
if status := rogue1.Status(); status != choices.Processing {
|
|
||||||
t.Fatalf("Rogue Tx is %s expected %s", status, choices.Processing)
|
t.Fatalf("Rogue Tx is %s expected %s", status, choices.Processing)
|
||||||
} else if status := rogue2.Status(); status != choices.Processing {
|
} else if status := rogue2.Status(); status != choices.Processing {
|
||||||
t.Fatalf("Rogue Tx is %s expected %s", status, choices.Processing)
|
t.Fatalf("Rogue Tx is %s expected %s", status, choices.Processing)
|
||||||
|
@ -664,6 +726,135 @@ func VirtuousDependsOnRogueTest(t *testing.T, factory Factory) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ErrorOnVacuouslyAcceptedTest(t *testing.T, factory Factory) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
graph := factory.New()
|
||||||
|
|
||||||
|
purple := &TestTx{
|
||||||
|
Identifier: ids.Empty.Prefix(7),
|
||||||
|
Stat: choices.Processing,
|
||||||
|
Validity: errors.New(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2,
|
||||||
|
}
|
||||||
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
|
||||||
|
if err := graph.Add(purple); err == nil {
|
||||||
|
t.Fatalf("Should have errored on acceptance")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorOnAcceptedTest(t *testing.T, factory Factory) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
graph := factory.New()
|
||||||
|
|
||||||
|
purple := &TestTx{
|
||||||
|
Identifier: ids.Empty.Prefix(7),
|
||||||
|
Stat: choices.Processing,
|
||||||
|
Validity: errors.New(""),
|
||||||
|
}
|
||||||
|
purple.Ins.Add(ids.Empty.Prefix(4))
|
||||||
|
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2,
|
||||||
|
}
|
||||||
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
|
||||||
|
if err := graph.Add(purple); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
votes := ids.Bag{}
|
||||||
|
votes.Add(purple.ID())
|
||||||
|
if err := graph.RecordPoll(votes); err == nil {
|
||||||
|
t.Fatalf("Should have errored on accepting an invalid tx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorOnRejectingLowerConfidenceConflictTest(t *testing.T, factory Factory) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
graph := factory.New()
|
||||||
|
|
||||||
|
X := ids.Empty.Prefix(4)
|
||||||
|
|
||||||
|
purple := &TestTx{
|
||||||
|
Identifier: ids.Empty.Prefix(7),
|
||||||
|
Stat: choices.Processing,
|
||||||
|
}
|
||||||
|
purple.Ins.Add(X)
|
||||||
|
|
||||||
|
pink := &TestTx{
|
||||||
|
Identifier: ids.Empty.Prefix(8),
|
||||||
|
Stat: choices.Processing,
|
||||||
|
Validity: errors.New(""),
|
||||||
|
}
|
||||||
|
pink.Ins.Add(X)
|
||||||
|
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1,
|
||||||
|
}
|
||||||
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
|
||||||
|
if err := graph.Add(purple); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(pink); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
votes := ids.Bag{}
|
||||||
|
votes.Add(purple.ID())
|
||||||
|
if err := graph.RecordPoll(votes); err == nil {
|
||||||
|
t.Fatalf("Should have errored on rejecting an invalid tx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorOnRejectingHigherConfidenceConflictTest(t *testing.T, factory Factory) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
graph := factory.New()
|
||||||
|
|
||||||
|
X := ids.Empty.Prefix(4)
|
||||||
|
|
||||||
|
purple := &TestTx{
|
||||||
|
Identifier: ids.Empty.Prefix(7),
|
||||||
|
Stat: choices.Processing,
|
||||||
|
}
|
||||||
|
purple.Ins.Add(X)
|
||||||
|
|
||||||
|
pink := &TestTx{
|
||||||
|
Identifier: ids.Empty.Prefix(8),
|
||||||
|
Stat: choices.Processing,
|
||||||
|
Validity: errors.New(""),
|
||||||
|
}
|
||||||
|
pink.Ins.Add(X)
|
||||||
|
|
||||||
|
params := snowball.Parameters{
|
||||||
|
Metrics: prometheus.NewRegistry(),
|
||||||
|
K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1,
|
||||||
|
}
|
||||||
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
|
|
||||||
|
if err := graph.Add(pink); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(purple); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
votes := ids.Bag{}
|
||||||
|
votes.Add(purple.ID())
|
||||||
|
if err := graph.RecordPoll(votes); err == nil {
|
||||||
|
t.Fatalf("Should have errored on rejecting an invalid tx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func StringTest(t *testing.T, factory Factory, prefix string) {
|
func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
Setup()
|
Setup()
|
||||||
|
|
||||||
|
@ -674,12 +865,16 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 2,
|
K: 2, Alpha: 2, BetaVirtuous: 1, BetaRogue: 2,
|
||||||
}
|
}
|
||||||
graph.Initialize(snow.DefaultContextTest(), params)
|
graph.Initialize(snow.DefaultContextTest(), params)
|
||||||
graph.Add(Red)
|
|
||||||
graph.Add(Green)
|
|
||||||
graph.Add(Blue)
|
|
||||||
graph.Add(Alpha)
|
|
||||||
|
|
||||||
if prefs := graph.Preferences(); prefs.Len() != 1 {
|
if err := graph.Add(Red); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Green); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Blue); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Alpha); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if prefs := graph.Preferences(); prefs.Len() != 1 {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !prefs.Contains(Red.ID()) {
|
} else if !prefs.Contains(Red.ID()) {
|
||||||
t.Fatalf("Wrong preference. Expected %s got %s", Red.ID(), prefs.List()[0])
|
t.Fatalf("Wrong preference. Expected %s got %s", Red.ID(), prefs.List()[0])
|
||||||
|
@ -691,8 +886,11 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
rb.SetThreshold(2)
|
rb.SetThreshold(2)
|
||||||
rb.AddCount(Red.ID(), 2)
|
rb.AddCount(Red.ID(), 2)
|
||||||
rb.AddCount(Blue.ID(), 2)
|
rb.AddCount(Blue.ID(), 2)
|
||||||
graph.RecordPoll(rb)
|
if err := graph.RecordPoll(rb); err != nil {
|
||||||
graph.Add(Blue)
|
t.Fatal(err)
|
||||||
|
} else if err := graph.Add(Blue); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
expected := prefix + "(\n" +
|
expected := prefix + "(\n" +
|
||||||
|
@ -720,7 +918,9 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
ga.SetThreshold(2)
|
ga.SetThreshold(2)
|
||||||
ga.AddCount(Green.ID(), 2)
|
ga.AddCount(Green.ID(), 2)
|
||||||
ga.AddCount(Alpha.ID(), 2)
|
ga.AddCount(Alpha.ID(), 2)
|
||||||
graph.RecordPoll(ga)
|
if err := graph.RecordPoll(ga); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
expected := prefix + "(\n" +
|
expected := prefix + "(\n" +
|
||||||
|
@ -745,7 +945,9 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
empty := ids.Bag{}
|
empty := ids.Bag{}
|
||||||
graph.RecordPoll(empty)
|
if err := graph.RecordPoll(empty); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
expected := prefix + "(\n" +
|
expected := prefix + "(\n" +
|
||||||
|
@ -767,10 +969,10 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Blue.ID())
|
t.Fatalf("Wrong preference. Expected %s", Blue.ID())
|
||||||
} else if graph.Finalized() {
|
} else if graph.Finalized() {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
|
} else if err := graph.RecordPoll(ga); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.RecordPoll(ga)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
expected := prefix + "(\n" +
|
expected := prefix + "(\n" +
|
||||||
" Choice[0] = ID: LUC1cmcxnfNR9LdkACS2ccGKLEK7SYqB4gLLTycQfg1koyfSq Confidence: 0 Bias: 1\n" +
|
" Choice[0] = ID: LUC1cmcxnfNR9LdkACS2ccGKLEK7SYqB4gLLTycQfg1koyfSq Confidence: 0 Bias: 1\n" +
|
||||||
|
@ -791,10 +993,10 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
t.Fatalf("Wrong preference. Expected %s", Alpha.ID())
|
t.Fatalf("Wrong preference. Expected %s", Alpha.ID())
|
||||||
} else if graph.Finalized() {
|
} else if graph.Finalized() {
|
||||||
t.Fatalf("Finalized too early")
|
t.Fatalf("Finalized too early")
|
||||||
|
} else if err := graph.RecordPoll(ga); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.RecordPoll(ga)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
expected := prefix + "()"
|
expected := prefix + "()"
|
||||||
if str := graph.String(); str != expected {
|
if str := graph.String(); str != expected {
|
||||||
|
@ -806,9 +1008,7 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !graph.Finalized() {
|
} else if !graph.Finalized() {
|
||||||
t.Fatalf("Finalized too late")
|
t.Fatalf("Finalized too late")
|
||||||
}
|
} else if Green.Status() != choices.Accepted {
|
||||||
|
|
||||||
if Green.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("%s should have been accepted", Green.ID())
|
t.Fatalf("%s should have been accepted", Green.ID())
|
||||||
} else if Alpha.Status() != choices.Accepted {
|
} else if Alpha.Status() != choices.Accepted {
|
||||||
t.Fatalf("%s should have been accepted", Alpha.ID())
|
t.Fatalf("%s should have been accepted", Alpha.ID())
|
||||||
|
@ -816,10 +1016,10 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
t.Fatalf("%s should have been rejected", Red.ID())
|
t.Fatalf("%s should have been rejected", Red.ID())
|
||||||
} else if Blue.Status() != choices.Rejected {
|
} else if Blue.Status() != choices.Rejected {
|
||||||
t.Fatalf("%s should have been rejected", Blue.ID())
|
t.Fatalf("%s should have been rejected", Blue.ID())
|
||||||
|
} else if err := graph.RecordPoll(rb); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.RecordPoll(rb)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
expected := prefix + "()"
|
expected := prefix + "()"
|
||||||
if str := graph.String(); str != expected {
|
if str := graph.String(); str != expected {
|
||||||
|
@ -831,9 +1031,7 @@ func StringTest(t *testing.T, factory Factory, prefix string) {
|
||||||
t.Fatalf("Wrong number of preferences.")
|
t.Fatalf("Wrong number of preferences.")
|
||||||
} else if !graph.Finalized() {
|
} else if !graph.Finalized() {
|
||||||
t.Fatalf("Finalized too late")
|
t.Fatalf("Finalized too late")
|
||||||
}
|
} else if Green.Status() != choices.Accepted {
|
||||||
|
|
||||||
if Green.Status() != choices.Accepted {
|
|
||||||
t.Fatalf("%s should have been accepted", Green.ID())
|
t.Fatalf("%s should have been accepted", Green.ID())
|
||||||
} else if Alpha.Status() != choices.Accepted {
|
} else if Alpha.Status() != choices.Accepted {
|
||||||
t.Fatalf("%s should have been accepted", Alpha.ID())
|
t.Fatalf("%s should have been accepted", Alpha.ID())
|
||||||
|
|
|
@ -7,34 +7,4 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDirectedParams(t *testing.T) { ParamsTest(t, DirectedFactory{}) }
|
func TestDirectedConsensus(t *testing.T) { ConsensusTest(t, DirectedFactory{}, "DG") }
|
||||||
|
|
||||||
func TestDirectedIssued(t *testing.T) { IssuedTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedLeftoverInput(t *testing.T) { LeftoverInputTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedLowerConfidence(t *testing.T) { LowerConfidenceTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedMiddleConfidence(t *testing.T) { MiddleConfidenceTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedIndependent(t *testing.T) { IndependentTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedVirtuous(t *testing.T) { VirtuousTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedIsVirtuous(t *testing.T) { IsVirtuousTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedConflicts(t *testing.T) { ConflictsTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedQuiesce(t *testing.T) { QuiesceTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedAcceptingDependency(t *testing.T) { AcceptingDependencyTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedRejectingDependency(t *testing.T) { RejectingDependencyTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedVacuouslyAccepted(t *testing.T) { VacuouslyAcceptedTest(t, DirectedFactory{}) }
|
|
||||||
|
|
||||||
func TestDirectedVirtuousDependsOnRogue(t *testing.T) {
|
|
||||||
VirtuousDependsOnRogueTest(t, DirectedFactory{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDirectedString(t *testing.T) { StringTest(t, DirectedFactory{}, "DG") }
|
|
||||||
|
|
|
@ -7,32 +7,4 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInputParams(t *testing.T) { ParamsTest(t, InputFactory{}) }
|
func TestInputConsensus(t *testing.T) { ConsensusTest(t, InputFactory{}, "IG") }
|
||||||
|
|
||||||
func TestInputIssued(t *testing.T) { IssuedTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputLeftoverInput(t *testing.T) { LeftoverInputTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputLowerConfidence(t *testing.T) { LowerConfidenceTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputMiddleConfidence(t *testing.T) { MiddleConfidenceTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputIndependent(t *testing.T) { IndependentTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputVirtuous(t *testing.T) { VirtuousTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputIsVirtuous(t *testing.T) { IsVirtuousTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputConflicts(t *testing.T) { ConflictsTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputQuiesce(t *testing.T) { QuiesceTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputAcceptingDependency(t *testing.T) { AcceptingDependencyTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputRejectingDependency(t *testing.T) { RejectingDependencyTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputVacuouslyAccepted(t *testing.T) { VacuouslyAcceptedTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputVirtuousDependsOnRogue(t *testing.T) { VirtuousDependsOnRogueTest(t, InputFactory{}) }
|
|
||||||
|
|
||||||
func TestInputString(t *testing.T) { StringTest(t, InputFactory{}, "IG") }
|
|
||||||
|
|
|
@ -31,10 +31,10 @@ func (tx *TestTx) InputIDs() ids.Set { return tx.Ins }
|
||||||
func (tx *TestTx) Status() choices.Status { return tx.Stat }
|
func (tx *TestTx) Status() choices.Status { return tx.Stat }
|
||||||
|
|
||||||
// Accept implements the Consumer interface
|
// Accept implements the Consumer interface
|
||||||
func (tx *TestTx) Accept() error { tx.Stat = choices.Accepted; return nil }
|
func (tx *TestTx) Accept() error { tx.Stat = choices.Accepted; return tx.Validity }
|
||||||
|
|
||||||
// Reject implements the Consumer interface
|
// Reject implements the Consumer interface
|
||||||
func (tx *TestTx) Reject() error { tx.Stat = choices.Rejected; return nil }
|
func (tx *TestTx) Reject() error { tx.Stat = choices.Rejected; return tx.Validity }
|
||||||
|
|
||||||
// Reset sets the status to pending
|
// Reset sets the status to pending
|
||||||
func (tx *TestTx) Reset() { tx.Stat = choices.Processing }
|
func (tx *TestTx) Reset() { tx.Stat = choices.Processing }
|
||||||
|
|
|
@ -17,6 +17,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// We cache processed vertices where height = c * stripeDistance for c = {1,2,3...}
|
||||||
|
// This forms a "stripe" of cached DAG vertices at height stripeDistance, 2*stripeDistance, etc.
|
||||||
|
// This helps to limit the number of repeated DAG traversals performed
|
||||||
|
stripeDistance = 2000
|
||||||
|
stripeWidth = 5
|
||||||
cacheSize = 100000
|
cacheSize = 100000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -126,11 +131,14 @@ func (b *bootstrapper) fetch(vtxID ids.ID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process vertices
|
// Process vertices
|
||||||
func (b *bootstrapper) process(vtx avalanche.Vertex) error {
|
func (b *bootstrapper) process(vtxs ...avalanche.Vertex) error {
|
||||||
toProcess := newMaxVertexHeap()
|
toProcess := newMaxVertexHeap()
|
||||||
|
for _, vtx := range vtxs {
|
||||||
if _, ok := b.processedCache.Get(vtx.ID()); !ok { // only process if we haven't already
|
if _, ok := b.processedCache.Get(vtx.ID()); !ok { // only process if we haven't already
|
||||||
toProcess.Push(vtx)
|
toProcess.Push(vtx)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for toProcess.Len() > 0 {
|
for toProcess.Len() > 0 {
|
||||||
vtx := toProcess.Pop()
|
vtx := toProcess.Pop()
|
||||||
switch vtx.Status() {
|
switch vtx.Status() {
|
||||||
|
@ -172,9 +180,11 @@ func (b *bootstrapper) process(vtx avalanche.Vertex) error {
|
||||||
toProcess.Push(parent)
|
toProcess.Push(parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if vtx.Height()%stripeDistance < stripeWidth {
|
||||||
b.processedCache.Put(vtx.ID(), nil)
|
b.processedCache.Put(vtx.ID(), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := b.VtxBlocked.Commit(); err != nil {
|
if err := b.VtxBlocked.Commit(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -217,14 +227,19 @@ func (b *bootstrapper) MultiPut(vdr ids.ShortID, requestID uint32, vtxs [][]byte
|
||||||
return b.fetch(neededVtxID)
|
return b.fetch(neededVtxID)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, vtxBytes := range vtxs { // Parse/persist all the vertices
|
processVertices := make([]avalanche.Vertex, 1, len(vtxs))
|
||||||
if _, err := b.State.ParseVertex(vtxBytes); err != nil { // Persists the vtx
|
processVertices[0] = neededVtx
|
||||||
|
|
||||||
|
for _, vtxBytes := range vtxs[1:] { // Parse/persist all the vertices
|
||||||
|
if vtx, err := b.State.ParseVertex(vtxBytes); err != nil { // Persists the vtx
|
||||||
b.BootstrapConfig.Context.Log.Debug("Failed to parse vertex: %w", err)
|
b.BootstrapConfig.Context.Log.Debug("Failed to parse vertex: %w", err)
|
||||||
b.BootstrapConfig.Context.Log.Verbo("vertex: %s", formatting.DumpBytes{Bytes: vtxBytes})
|
b.BootstrapConfig.Context.Log.Verbo("vertex: %s", formatting.DumpBytes{Bytes: vtxBytes})
|
||||||
|
} else {
|
||||||
|
processVertices = append(processVertices, vtx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.process(neededVtx)
|
return b.process(processVertices...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAncestorsFailed is called when a GetAncestors message we sent fails
|
// GetAncestorsFailed is called when a GetAncestors message we sent fails
|
||||||
|
@ -245,15 +260,17 @@ func (b *bootstrapper) ForceAccepted(acceptedContainerIDs ids.Set) error {
|
||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storedVtxs := make([]avalanche.Vertex, 0, acceptedContainerIDs.Len())
|
||||||
for _, vtxID := range acceptedContainerIDs.List() {
|
for _, vtxID := range acceptedContainerIDs.List() {
|
||||||
if vtx, err := b.State.GetVertex(vtxID); err == nil {
|
if vtx, err := b.State.GetVertex(vtxID); err == nil {
|
||||||
if err := b.process(vtx); err != nil {
|
storedVtxs = append(storedVtxs, vtx)
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if err := b.fetch(vtxID); err != nil {
|
} else if err := b.fetch(vtxID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := b.process(storedVtxs...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
b.processedStartingAcceptedFrontier = true
|
b.processedStartingAcceptedFrontier = true
|
||||||
|
|
||||||
if numPending := b.outstandingRequests.Len(); numPending == 0 {
|
if numPending := b.outstandingRequests.Len(); numPending == 0 {
|
||||||
|
|
|
@ -14,7 +14,7 @@ type metrics struct {
|
||||||
numBSVtx, numBSDroppedVtx,
|
numBSVtx, numBSDroppedVtx,
|
||||||
numBSTx, numBSDroppedTx prometheus.Counter
|
numBSTx, numBSDroppedTx prometheus.Counter
|
||||||
|
|
||||||
numPolls, numVtxRequests, numTxRequests, numPendingVtx prometheus.Gauge
|
numVtxRequests, numTxRequests, numPendingVtx prometheus.Gauge
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Engine interface
|
// Initialize implements the Engine interface
|
||||||
|
@ -61,12 +61,6 @@ func (m *metrics) Initialize(log logging.Logger, namespace string, registerer pr
|
||||||
Name: "av_bs_dropped_txs",
|
Name: "av_bs_dropped_txs",
|
||||||
Help: "Number of dropped txs",
|
Help: "Number of dropped txs",
|
||||||
})
|
})
|
||||||
m.numPolls = prometheus.NewGauge(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: "av_polls",
|
|
||||||
Help: "Number of pending network polls",
|
|
||||||
})
|
|
||||||
m.numVtxRequests = prometheus.NewGauge(
|
m.numVtxRequests = prometheus.NewGauge(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
|
@ -107,9 +101,6 @@ func (m *metrics) Initialize(log logging.Logger, namespace string, registerer pr
|
||||||
if err := registerer.Register(m.numBSDroppedTx); err != nil {
|
if err := registerer.Register(m.numBSDroppedTx); err != nil {
|
||||||
log.Error("Failed to register av_bs_dropped_txs statistics due to %s", err)
|
log.Error("Failed to register av_bs_dropped_txs statistics due to %s", err)
|
||||||
}
|
}
|
||||||
if err := registerer.Register(m.numPolls); err != nil {
|
|
||||||
log.Error("Failed to register av_polls statistics due to %s", err)
|
|
||||||
}
|
|
||||||
if err := registerer.Register(m.numVtxRequests); err != nil {
|
if err := registerer.Register(m.numVtxRequests); err != nil {
|
||||||
log.Error("Failed to register av_vtx_requests statistics due to %s", err)
|
log.Error("Failed to register av_vtx_requests statistics due to %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
type earlyTermNoTraversalFactory struct {
|
||||||
|
alpha int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEarlyTermNoTraversalFactory returns a factory that returns polls with
|
||||||
|
// early termination, without doing DAG traversals
|
||||||
|
func NewEarlyTermNoTraversalFactory(alpha int) Factory {
|
||||||
|
return &earlyTermNoTraversalFactory{alpha: alpha}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *earlyTermNoTraversalFactory) New(vdrs ids.ShortSet) Poll {
|
||||||
|
return &earlyTermNoTraversalPoll{
|
||||||
|
polled: vdrs,
|
||||||
|
alpha: f.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// earlyTermNoTraversalPoll finishes when any remaining validators can't change
|
||||||
|
// the result of the poll. However, does not terminate tightly with this bound.
|
||||||
|
// It terminates as quickly as it can without performing any DAG traversals.
|
||||||
|
type earlyTermNoTraversalPoll struct {
|
||||||
|
votes ids.UniqueBag
|
||||||
|
polled ids.ShortSet
|
||||||
|
alpha int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote registers a response for this poll
|
||||||
|
func (p *earlyTermNoTraversalPoll) Vote(vdr ids.ShortID, votes []ids.ID) {
|
||||||
|
if !p.polled.Contains(vdr) {
|
||||||
|
// if the validator wasn't polled or already responded to this poll, we
|
||||||
|
// should just drop the vote
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that a validator can't respond multiple times
|
||||||
|
p.polled.Remove(vdr)
|
||||||
|
|
||||||
|
// track the votes the validator responded with
|
||||||
|
p.votes.Add(uint(p.polled.Len()), votes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished returns true when all validators have voted
|
||||||
|
func (p *earlyTermNoTraversalPoll) Finished() bool {
|
||||||
|
// If there are no outstanding queries, the poll is finished
|
||||||
|
numPending := p.polled.Len()
|
||||||
|
if numPending == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// If there are still enough pending responses to include another vertex,
|
||||||
|
// then the poll must wait for more responses
|
||||||
|
if numPending > p.alpha {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore any vertex that has already received alpha votes. To safely skip
|
||||||
|
// DAG traversal, assume that all votes for vertices with less than alpha
|
||||||
|
// votes will be applied to a single shared ancestor. In this case, the poll
|
||||||
|
// can terminate early, iff there are not enough pending votes for this
|
||||||
|
// ancestor to receive alpha votes.
|
||||||
|
partialVotes := ids.BitSet(0)
|
||||||
|
for _, vote := range p.votes.List() {
|
||||||
|
if voters := p.votes.GetSet(vote); voters.Len() < p.alpha {
|
||||||
|
partialVotes.Union(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return partialVotes.Len()+numPending < p.alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result returns the result of this poll
|
||||||
|
func (p *earlyTermNoTraversalPoll) Result() ids.UniqueBag { return p.votes }
|
||||||
|
|
||||||
|
func (p *earlyTermNoTraversalPoll) String() string {
|
||||||
|
return fmt.Sprintf("waiting on %s", p.polled)
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalResults(t *testing.T) {
|
||||||
|
alpha := 1
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1}) // k = 1
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(vdr1)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := poll.Result()
|
||||||
|
if list := result.List(); len(list) != 1 {
|
||||||
|
t.Fatalf("Wrong number of vertices returned")
|
||||||
|
} else if retVtxID := list[0]; !retVtxID.Equals(vtxID) {
|
||||||
|
t.Fatalf("Wrong vertex returned")
|
||||||
|
} else if set := result.GetSet(vtxID); set.Len() != 1 {
|
||||||
|
t.Fatalf("Wrong number of votes returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalString(t *testing.T) {
|
||||||
|
alpha := 2
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
|
||||||
|
expected := "waiting on {BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp}"
|
||||||
|
if result := poll.String(); expected != result {
|
||||||
|
t.Fatalf("Poll should have returned %s but returned %s", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalDropsDuplicatedVotes(t *testing.T) {
|
||||||
|
alpha := 2
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after getting a duplicated vote")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, votes)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalTerminatesEarly(t *testing.T) {
|
||||||
|
alpha := 3
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2})
|
||||||
|
vdr3 := ids.NewShortID([20]byte{3})
|
||||||
|
vdr4 := ids.NewShortID([20]byte{4})
|
||||||
|
vdr5 := ids.NewShortID([20]byte{5}) // k = 5
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
vdr3,
|
||||||
|
vdr4,
|
||||||
|
vdr5,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, votes)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr3, votes)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate early after receiving alpha votes for one vertex and none for other vertices")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalForSharedAncestor(t *testing.T) {
|
||||||
|
alpha := 4
|
||||||
|
|
||||||
|
vtxA := ids.NewID([32]byte{1})
|
||||||
|
vtxB := ids.NewID([32]byte{2})
|
||||||
|
vtxC := ids.NewID([32]byte{3})
|
||||||
|
vtxD := ids.NewID([32]byte{4})
|
||||||
|
|
||||||
|
// If validators 1-3 vote for frontier vertices
|
||||||
|
// B, C, and D respectively, which all share the common ancestor
|
||||||
|
// A, then we cannot terminate early with alpha = k = 4
|
||||||
|
// If the final vote is cast for any of A, B, C, or D, then
|
||||||
|
// vertex A will have transitively received alpha = 4 votes
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2})
|
||||||
|
vdr3 := ids.NewShortID([20]byte{3})
|
||||||
|
vdr4 := ids.NewShortID([20]byte{4})
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(vdr1)
|
||||||
|
vdrs.Add(vdr2)
|
||||||
|
vdrs.Add(vdr3)
|
||||||
|
vdrs.Add(vdr4)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, []ids.ID{vtxB})
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished early after receiving one vote")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, []ids.ID{vtxC})
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished early after receiving two votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr3, []ids.ID{vtxD})
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll terminated early, when a shared ancestor could have received alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr4, []ids.ID{vtxA})
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving all outstanding votes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalWithFastDrops(t *testing.T) {
|
||||||
|
alpha := 2
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2})
|
||||||
|
vdr3 := ids.NewShortID([20]byte{3}) // k = 3
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
vdr3,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, nil)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished early after dropping one vote")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, nil)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after dropping two votes")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set is a collection of polls
|
||||||
|
type Set interface {
|
||||||
|
fmt.Stringer
|
||||||
|
|
||||||
|
Add(requestID uint32, vdrs ids.ShortSet) bool
|
||||||
|
Vote(requestID uint32, vdr ids.ShortID, votes []ids.ID) (ids.UniqueBag, bool)
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll is an outstanding poll
|
||||||
|
type Poll interface {
|
||||||
|
fmt.Stringer
|
||||||
|
|
||||||
|
Vote(vdr ids.ShortID, votes []ids.ID)
|
||||||
|
Finished() bool
|
||||||
|
Result() ids.UniqueBag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory creates a new Poll
|
||||||
|
type Factory interface {
|
||||||
|
New(vdrs ids.ShortSet) Poll
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
type noEarlyTermFactory struct{}
|
||||||
|
|
||||||
|
// NewNoEarlyTermFactory returns a factory that returns polls with no early
|
||||||
|
// termination
|
||||||
|
func NewNoEarlyTermFactory() Factory { return noEarlyTermFactory{} }
|
||||||
|
|
||||||
|
func (noEarlyTermFactory) New(vdrs ids.ShortSet) Poll {
|
||||||
|
return &noEarlyTermPoll{polled: vdrs}
|
||||||
|
}
|
||||||
|
|
||||||
|
// noEarlyTermPoll finishes when all polled validators either respond to the
|
||||||
|
// query or a timeout occurs
|
||||||
|
type noEarlyTermPoll struct {
|
||||||
|
votes ids.UniqueBag
|
||||||
|
polled ids.ShortSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote registers a response for this poll
|
||||||
|
func (p *noEarlyTermPoll) Vote(vdr ids.ShortID, votes []ids.ID) {
|
||||||
|
if !p.polled.Contains(vdr) {
|
||||||
|
// if the validator wasn't polled or already responded to this poll, we
|
||||||
|
// should just drop the vote
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that a validator can't respond multiple times
|
||||||
|
p.polled.Remove(vdr)
|
||||||
|
|
||||||
|
// track the votes the validator responded with
|
||||||
|
p.votes.Add(uint(p.polled.Len()), votes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished returns true when all validators have voted
|
||||||
|
func (p *noEarlyTermPoll) Finished() bool { return p.polled.Len() == 0 }
|
||||||
|
|
||||||
|
// Result returns the result of this poll
|
||||||
|
func (p *noEarlyTermPoll) Result() ids.UniqueBag { return p.votes }
|
||||||
|
|
||||||
|
func (p *noEarlyTermPoll) String() string {
|
||||||
|
return fmt.Sprintf("waiting on %s", p.polled)
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNoEarlyTermResults(t *testing.T) {
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1}) // k = 1
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(vdr1)
|
||||||
|
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := poll.Result()
|
||||||
|
if list := result.List(); len(list) != 1 {
|
||||||
|
t.Fatalf("Wrong number of vertices returned")
|
||||||
|
} else if retVtxID := list[0]; !retVtxID.Equals(vtxID) {
|
||||||
|
t.Fatalf("Wrong vertex returned")
|
||||||
|
} else if set := result.GetSet(vtxID); set.Len() != 1 {
|
||||||
|
t.Fatalf("Wrong number of votes returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoEarlyTermString(t *testing.T) {
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
|
||||||
|
expected := "waiting on {BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp}"
|
||||||
|
if result := poll.String(); expected != result {
|
||||||
|
t.Fatalf("Poll should have returned %s but returned %s", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoEarlyTermDropsDuplicatedVotes(t *testing.T) {
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr1, votes)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after getting a duplicated vote")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, votes)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
|
"github.com/ava-labs/gecko/utils/timer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type poll struct {
|
||||||
|
Poll
|
||||||
|
start time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type set struct {
|
||||||
|
log logging.Logger
|
||||||
|
numPolls prometheus.Gauge
|
||||||
|
durPolls prometheus.Histogram
|
||||||
|
factory Factory
|
||||||
|
polls map[uint32]poll
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSet returns a new empty set of polls
|
||||||
|
func NewSet(
|
||||||
|
factory Factory,
|
||||||
|
log logging.Logger,
|
||||||
|
namespace string,
|
||||||
|
registerer prometheus.Registerer,
|
||||||
|
) Set {
|
||||||
|
numPolls := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "polls",
|
||||||
|
Help: "Number of pending network polls",
|
||||||
|
})
|
||||||
|
if err := registerer.Register(numPolls); err != nil {
|
||||||
|
log.Error("failed to register polls statistics due to %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
durPolls := prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "poll_duration",
|
||||||
|
Help: "Length of time the poll existed in milliseconds",
|
||||||
|
Buckets: timer.MillisecondsBuckets,
|
||||||
|
})
|
||||||
|
if err := registerer.Register(durPolls); err != nil {
|
||||||
|
log.Error("failed to register poll_duration statistics due to %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &set{
|
||||||
|
log: log,
|
||||||
|
numPolls: numPolls,
|
||||||
|
durPolls: durPolls,
|
||||||
|
factory: factory,
|
||||||
|
polls: make(map[uint32]poll),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the current set of polls
|
||||||
|
// Returns true if the poll was registered correctly and the network sample
|
||||||
|
// should be made.
|
||||||
|
func (s *set) Add(requestID uint32, vdrs ids.ShortSet) bool {
|
||||||
|
if _, exists := s.polls[requestID]; exists {
|
||||||
|
s.log.Debug("dropping poll due to duplicated requestID: %d", requestID)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("creating poll with requestID %d and validators %s",
|
||||||
|
requestID,
|
||||||
|
vdrs)
|
||||||
|
|
||||||
|
s.polls[requestID] = poll{
|
||||||
|
Poll: s.factory.New(vdrs), // create the new poll
|
||||||
|
start: time.Now(),
|
||||||
|
}
|
||||||
|
s.numPolls.Inc() // increase the metrics
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote registers the connections response to a query for [id]. If there was no
|
||||||
|
// query, or the response has already be registered, nothing is performed.
|
||||||
|
func (s *set) Vote(
|
||||||
|
requestID uint32,
|
||||||
|
vdr ids.ShortID,
|
||||||
|
votes []ids.ID,
|
||||||
|
) (ids.UniqueBag, bool) {
|
||||||
|
poll, exists := s.polls[requestID]
|
||||||
|
if !exists {
|
||||||
|
s.log.Verbo("dropping vote from %s to an unknown poll with requestID: %d",
|
||||||
|
vdr,
|
||||||
|
requestID)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("processing vote from %s in the poll with requestID: %d with the votes %v",
|
||||||
|
vdr,
|
||||||
|
requestID,
|
||||||
|
votes)
|
||||||
|
|
||||||
|
poll.Vote(vdr, votes)
|
||||||
|
if !poll.Finished() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("poll with requestID %d finished as %s", requestID, poll)
|
||||||
|
|
||||||
|
delete(s.polls, requestID) // remove the poll from the current set
|
||||||
|
s.durPolls.Observe(float64(time.Now().Sub(poll.start).Milliseconds()))
|
||||||
|
s.numPolls.Dec() // decrease the metrics
|
||||||
|
return poll.Result(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of outstanding polls
|
||||||
|
func (s *set) Len() int { return len(s.polls) }
|
||||||
|
|
||||||
|
func (s *set) String() string {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString(fmt.Sprintf("current polls: (Size = %d)", len(s.polls)))
|
||||||
|
for requestID, poll := range s.polls {
|
||||||
|
sb.WriteString(fmt.Sprintf("\n %d: %s", requestID, poll))
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewSetErrorOnMetrics(t *testing.T) {
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
log := logging.NoLog{}
|
||||||
|
namespace := ""
|
||||||
|
registerer := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
registerer.Register(prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "polls",
|
||||||
|
}))
|
||||||
|
registerer.Register(prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "poll_duration",
|
||||||
|
}))
|
||||||
|
|
||||||
|
_ = NewSet(factory, log, namespace, registerer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateAndFinishPoll(t *testing.T) {
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
log := logging.NoLog{}
|
||||||
|
namespace := ""
|
||||||
|
registerer := prometheus.NewRegistry()
|
||||||
|
s := NewSet(factory, log, namespace, registerer)
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
votes := []ids.ID{vtxID}
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
if s.Len() != 0 {
|
||||||
|
t.Fatalf("Shouldn't have any active polls yet")
|
||||||
|
} else if !s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Should have been able to add a new poll")
|
||||||
|
} else if s.Len() != 1 {
|
||||||
|
t.Fatalf("Should only have one active poll")
|
||||||
|
} else if s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Shouldn't have been able to add a duplicated poll")
|
||||||
|
} else if s.Len() != 1 {
|
||||||
|
t.Fatalf("Should only have one active poll")
|
||||||
|
} else if _, finished := s.Vote(1, vdr1, votes); finished {
|
||||||
|
t.Fatalf("Shouldn't have been able to finish a non-existant poll")
|
||||||
|
} else if _, finished := s.Vote(0, vdr1, votes); finished {
|
||||||
|
t.Fatalf("Shouldn't have been able to finish an ongoing poll")
|
||||||
|
} else if _, finished := s.Vote(0, vdr1, votes); finished {
|
||||||
|
t.Fatalf("Should have dropped a duplicated poll")
|
||||||
|
} else if result, finished := s.Vote(0, vdr2, votes); !finished {
|
||||||
|
t.Fatalf("Should have finished the")
|
||||||
|
} else if list := result.List(); len(list) != 1 {
|
||||||
|
t.Fatalf("Wrong number of vertices returned")
|
||||||
|
} else if retVtxID := list[0]; !retVtxID.Equals(vtxID) {
|
||||||
|
t.Fatalf("Wrong vertex returned")
|
||||||
|
} else if set := result.GetSet(vtxID); set.Len() != 2 {
|
||||||
|
t.Fatalf("Wrong number of votes returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetString(t *testing.T) {
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
log := logging.NoLog{}
|
||||||
|
namespace := ""
|
||||||
|
registerer := prometheus.NewRegistry()
|
||||||
|
s := NewSet(factory, log, namespace, registerer)
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1}) // k = 1
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(vdr1)
|
||||||
|
|
||||||
|
expected := "current polls: (Size = 1)\n" +
|
||||||
|
" 0: waiting on {6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt}"
|
||||||
|
if !s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Should have been able to add a new poll")
|
||||||
|
} else if str := s.String(); expected != str {
|
||||||
|
t.Fatalf("Set return wrong string, Expected:\n%s\nReturned:\n%s",
|
||||||
|
expected,
|
||||||
|
str)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,137 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package avalanche
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/ava-labs/gecko/utils/logging"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: There is a conservative early termination case that doesn't require dag
|
|
||||||
// traversals we may want to implement. The algorithm would go as follows:
|
|
||||||
// Keep track of the number of response that reference an ID. If an ID gets >=
|
|
||||||
// alpha responses, then remove it from all responses and place it into a chit
|
|
||||||
// list. Remove all empty responses. If the number of responses + the number of
|
|
||||||
// pending responses is less than alpha, terminate the poll.
|
|
||||||
// In the synchronous + virtuous case, when everyone returns the same hash, the
|
|
||||||
// poll now terminates after receiving alpha responses.
|
|
||||||
// In the rogue case, it is possible that the poll doesn't terminate as quickly
|
|
||||||
// as possible, because IDs may have the alpha threshold but only when counting
|
|
||||||
// transitive votes. In this case, we may wait even if it is no longer possible
|
|
||||||
// for another ID to earn alpha votes.
|
|
||||||
// Because alpha is typically set close to k, this may not be performance
|
|
||||||
// critical. However, early termination may be performance critical with crashed
|
|
||||||
// nodes.
|
|
||||||
|
|
||||||
type polls struct {
|
|
||||||
log logging.Logger
|
|
||||||
numPolls prometheus.Gauge
|
|
||||||
alpha int
|
|
||||||
m map[uint32]poll
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPolls(alpha int, log logging.Logger, numPolls prometheus.Gauge) polls {
|
|
||||||
return polls{
|
|
||||||
log: log,
|
|
||||||
numPolls: numPolls,
|
|
||||||
alpha: alpha,
|
|
||||||
m: make(map[uint32]poll),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the current set of polls
|
|
||||||
// Returns true if the poll was registered correctly and the network sample
|
|
||||||
// should be made.
|
|
||||||
func (p *polls) Add(requestID uint32, vdrs ids.ShortSet) bool {
|
|
||||||
poll, exists := p.m[requestID]
|
|
||||||
if !exists {
|
|
||||||
poll.polled = vdrs
|
|
||||||
poll.alpha = p.alpha
|
|
||||||
p.m[requestID] = poll
|
|
||||||
|
|
||||||
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
||||||
}
|
|
||||||
return !exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vote registers the connections response to a query for [id]. If there was no
|
|
||||||
// query, or the response has already be registered, nothing is performed.
|
|
||||||
func (p *polls) Vote(requestID uint32, vdr ids.ShortID, votes []ids.ID) (ids.UniqueBag, bool) {
|
|
||||||
p.log.Verbo("Vote. requestID: %d. validatorID: %s.", requestID, vdr)
|
|
||||||
poll, exists := p.m[requestID]
|
|
||||||
p.log.Verbo("Poll: %+v", poll)
|
|
||||||
if !exists {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
poll.Vote(votes, vdr)
|
|
||||||
if poll.Finished() {
|
|
||||||
p.log.Verbo("Poll is finished")
|
|
||||||
delete(p.m, requestID)
|
|
||||||
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
||||||
return poll.votes, true
|
|
||||||
}
|
|
||||||
p.m[requestID] = poll
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *polls) String() string {
|
|
||||||
sb := strings.Builder{}
|
|
||||||
|
|
||||||
sb.WriteString(fmt.Sprintf("Current polls: (Size = %d)", len(p.m)))
|
|
||||||
for requestID, poll := range p.m {
|
|
||||||
sb.WriteString(fmt.Sprintf("\n %d: %s", requestID, poll))
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// poll represents the current state of a network poll for a vertex
|
|
||||||
type poll struct {
|
|
||||||
votes ids.UniqueBag
|
|
||||||
polled ids.ShortSet
|
|
||||||
alpha int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vote registers a vote for this poll
|
|
||||||
func (p *poll) Vote(votes []ids.ID, vdr ids.ShortID) {
|
|
||||||
if p.polled.Contains(vdr) {
|
|
||||||
p.polled.Remove(vdr)
|
|
||||||
p.votes.Add(uint(p.polled.Len()), votes...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finished returns true if the poll has completed, with no more required
|
|
||||||
// responses
|
|
||||||
func (p poll) Finished() bool {
|
|
||||||
// If there are no outstanding queries, the poll is finished
|
|
||||||
numPending := p.polled.Len()
|
|
||||||
if numPending == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// If there are still enough pending responses to include another vertex,
|
|
||||||
// then the poll must wait for more responses
|
|
||||||
if numPending > p.alpha {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore any vertex that has already received alpha votes. To safely skip
|
|
||||||
// DAG traversal, assume that all votes for vertices with less than alpha
|
|
||||||
// votes will be applied to a single shared ancestor. In this case, the poll
|
|
||||||
// can terminate early, iff there are not enough pending votes for this
|
|
||||||
// ancestor to receive alpha votes.
|
|
||||||
partialVotes := ids.BitSet(0)
|
|
||||||
for _, vote := range p.votes.List() {
|
|
||||||
if voters := p.votes.GetSet(vote); voters.Len() < p.alpha {
|
|
||||||
partialVotes.Union(voters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return partialVotes.Len()+numPending < p.alpha
|
|
||||||
}
|
|
||||||
func (p poll) String() string { return fmt.Sprintf("Waiting on %d chits", p.polled.Len()) }
|
|
|
@ -1,99 +0,0 @@
|
||||||
package avalanche
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPollTerminatesEarlyVirtuousCase(t *testing.T) {
|
|
||||||
alpha := 3
|
|
||||||
|
|
||||||
vtxID := GenerateID()
|
|
||||||
votes := []ids.ID{vtxID}
|
|
||||||
|
|
||||||
vdr1 := ids.NewShortID([20]byte{1})
|
|
||||||
vdr2 := ids.NewShortID([20]byte{2})
|
|
||||||
vdr3 := ids.NewShortID([20]byte{3})
|
|
||||||
vdr4 := ids.NewShortID([20]byte{4})
|
|
||||||
vdr5 := ids.NewShortID([20]byte{5}) // k = 5
|
|
||||||
|
|
||||||
vdrs := ids.ShortSet{}
|
|
||||||
vdrs.Add(vdr1)
|
|
||||||
vdrs.Add(vdr2)
|
|
||||||
vdrs.Add(vdr3)
|
|
||||||
vdrs.Add(vdr4)
|
|
||||||
vdrs.Add(vdr5)
|
|
||||||
|
|
||||||
poll := poll{
|
|
||||||
votes: make(ids.UniqueBag),
|
|
||||||
polled: vdrs,
|
|
||||||
alpha: alpha,
|
|
||||||
}
|
|
||||||
|
|
||||||
poll.Vote(votes, vdr1)
|
|
||||||
if poll.Finished() {
|
|
||||||
t.Fatalf("Poll finished after less than alpha votes")
|
|
||||||
}
|
|
||||||
poll.Vote(votes, vdr2)
|
|
||||||
if poll.Finished() {
|
|
||||||
t.Fatalf("Poll finished after less than alpha votes")
|
|
||||||
}
|
|
||||||
poll.Vote(votes, vdr3)
|
|
||||||
if !poll.Finished() {
|
|
||||||
t.Fatalf("Poll did not terminate early after receiving alpha votes for one vertex and none for other vertices")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPollAccountsForSharedAncestor(t *testing.T) {
|
|
||||||
alpha := 4
|
|
||||||
|
|
||||||
vtxA := GenerateID()
|
|
||||||
vtxB := GenerateID()
|
|
||||||
vtxC := GenerateID()
|
|
||||||
vtxD := GenerateID()
|
|
||||||
|
|
||||||
// If validators 1-3 vote for frontier vertices
|
|
||||||
// B, C, and D respectively, which all share the common ancestor
|
|
||||||
// A, then we cannot terminate early with alpha = k = 4
|
|
||||||
// If the final vote is cast for any of A, B, C, or D, then
|
|
||||||
// vertex A will have transitively received alpha = 4 votes
|
|
||||||
vdr1 := ids.NewShortID([20]byte{1})
|
|
||||||
vdr2 := ids.NewShortID([20]byte{2})
|
|
||||||
vdr3 := ids.NewShortID([20]byte{3})
|
|
||||||
vdr4 := ids.NewShortID([20]byte{4})
|
|
||||||
|
|
||||||
vdrs := ids.ShortSet{}
|
|
||||||
vdrs.Add(vdr1)
|
|
||||||
vdrs.Add(vdr2)
|
|
||||||
vdrs.Add(vdr3)
|
|
||||||
vdrs.Add(vdr4)
|
|
||||||
|
|
||||||
poll := poll{
|
|
||||||
votes: make(ids.UniqueBag),
|
|
||||||
polled: vdrs,
|
|
||||||
alpha: alpha,
|
|
||||||
}
|
|
||||||
|
|
||||||
votes1 := []ids.ID{vtxB}
|
|
||||||
poll.Vote(votes1, vdr1)
|
|
||||||
if poll.Finished() {
|
|
||||||
t.Fatalf("Poll finished early after receiving one vote")
|
|
||||||
}
|
|
||||||
votes2 := []ids.ID{vtxC}
|
|
||||||
poll.Vote(votes2, vdr2)
|
|
||||||
if poll.Finished() {
|
|
||||||
t.Fatalf("Poll finished early after receiving two votes")
|
|
||||||
}
|
|
||||||
votes3 := []ids.ID{vtxD}
|
|
||||||
poll.Vote(votes3, vdr3)
|
|
||||||
if poll.Finished() {
|
|
||||||
t.Fatalf("Poll terminated early, when a shared ancestor could have received alpha votes")
|
|
||||||
}
|
|
||||||
|
|
||||||
votes4 := []ids.ID{vtxA}
|
|
||||||
poll.Vote(votes4, vdr4)
|
|
||||||
if !poll.Finished() {
|
|
||||||
t.Fatalf("Poll did not terminate after receiving all outstanding votes")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -54,6 +54,8 @@ func (vtx *uniqueVertex) refresh() {
|
||||||
func (vtx *uniqueVertex) Evict() {
|
func (vtx *uniqueVertex) Evict() {
|
||||||
if vtx.v != nil {
|
if vtx.v != nil {
|
||||||
vtx.v.unique = false
|
vtx.v.unique = false
|
||||||
|
// make sure the parents are able to be garbage collected
|
||||||
|
vtx.v.parents = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/ava-labs/gecko/snow/choices"
|
"github.com/ava-labs/gecko/snow/choices"
|
||||||
"github.com/ava-labs/gecko/snow/consensus/avalanche"
|
"github.com/ava-labs/gecko/snow/consensus/avalanche"
|
||||||
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
||||||
|
"github.com/ava-labs/gecko/snow/engine/avalanche/poll"
|
||||||
"github.com/ava-labs/gecko/snow/engine/common"
|
"github.com/ava-labs/gecko/snow/engine/common"
|
||||||
"github.com/ava-labs/gecko/snow/events"
|
"github.com/ava-labs/gecko/snow/events"
|
||||||
"github.com/ava-labs/gecko/utils/formatting"
|
"github.com/ava-labs/gecko/utils/formatting"
|
||||||
|
@ -31,7 +32,7 @@ type Transitive struct {
|
||||||
Config
|
Config
|
||||||
bootstrapper
|
bootstrapper
|
||||||
|
|
||||||
polls polls // track people I have asked for their preference
|
polls poll.Set // track people I have asked for their preference
|
||||||
|
|
||||||
// vtxReqs prevents asking validators for the same vertex
|
// vtxReqs prevents asking validators for the same vertex
|
||||||
vtxReqs common.Requests
|
vtxReqs common.Requests
|
||||||
|
@ -57,7 +58,12 @@ func (t *Transitive) Initialize(config Config) error {
|
||||||
|
|
||||||
t.onFinished = t.finishBootstrapping
|
t.onFinished = t.finishBootstrapping
|
||||||
|
|
||||||
t.polls = newPolls(int(config.Alpha), config.Context.Log, t.numPolls)
|
factory := poll.NewEarlyTermNoTraversalFactory(int(config.Params.Alpha))
|
||||||
|
t.polls = poll.NewSet(factory,
|
||||||
|
config.Context.Log,
|
||||||
|
config.Params.Namespace,
|
||||||
|
config.Params.Metrics,
|
||||||
|
)
|
||||||
|
|
||||||
return t.bootstrapper.Initialize(config.BootstrapConfig)
|
return t.bootstrapper.Initialize(config.BootstrapConfig)
|
||||||
}
|
}
|
||||||
|
@ -309,7 +315,7 @@ func (t *Transitive) Notify(msg common.Message) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transitive) repoll() error {
|
func (t *Transitive) repoll() error {
|
||||||
if len(t.polls.m) >= t.Params.ConcurrentRepolls || t.errs.Errored() {
|
if t.polls.Len() >= t.Params.ConcurrentRepolls || t.errs.Errored() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +324,7 @@ func (t *Transitive) repoll() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := len(t.polls.m); i < t.Params.ConcurrentRepolls; i++ {
|
for i := t.polls.Len(); i < t.Params.ConcurrentRepolls; i++ {
|
||||||
if err := t.batch(nil, false /*=force*/, true /*=empty*/); err != nil {
|
if err := t.batch(nil, false /*=force*/, true /*=empty*/); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ type metrics struct {
|
||||||
numPendingRequests, numBlocked prometheus.Gauge
|
numPendingRequests, numBlocked prometheus.Gauge
|
||||||
numBootstrapped, numDropped prometheus.Counter
|
numBootstrapped, numDropped prometheus.Counter
|
||||||
|
|
||||||
numPolls, numBlkRequests, numBlockedBlk prometheus.Gauge
|
numBlkRequests, numBlockedBlk prometheus.Gauge
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Engine interface
|
// Initialize implements the Engine interface
|
||||||
|
@ -42,12 +42,6 @@ func (m *metrics) Initialize(log logging.Logger, namespace string, registerer pr
|
||||||
Name: "sm_bs_dropped",
|
Name: "sm_bs_dropped",
|
||||||
Help: "Number of dropped bootstrap blocks",
|
Help: "Number of dropped bootstrap blocks",
|
||||||
})
|
})
|
||||||
m.numPolls = prometheus.NewGauge(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: "sm_polls",
|
|
||||||
Help: "Number of pending network polls",
|
|
||||||
})
|
|
||||||
m.numBlkRequests = prometheus.NewGauge(
|
m.numBlkRequests = prometheus.NewGauge(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
|
@ -73,9 +67,6 @@ func (m *metrics) Initialize(log logging.Logger, namespace string, registerer pr
|
||||||
if err := registerer.Register(m.numDropped); err != nil {
|
if err := registerer.Register(m.numDropped); err != nil {
|
||||||
log.Error("Failed to register sm_bs_dropped statistics due to %s", err)
|
log.Error("Failed to register sm_bs_dropped statistics due to %s", err)
|
||||||
}
|
}
|
||||||
if err := registerer.Register(m.numPolls); err != nil {
|
|
||||||
log.Error("Failed to register sm_polls statistics due to %s", err)
|
|
||||||
}
|
|
||||||
if err := registerer.Register(m.numBlkRequests); err != nil {
|
if err := registerer.Register(m.numBlkRequests); err != nil {
|
||||||
log.Error("Failed to register sm_blk_requests statistics due to %s", err)
|
log.Error("Failed to register sm_blk_requests statistics due to %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
type earlyTermNoTraversalFactory struct {
|
||||||
|
alpha int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEarlyTermNoTraversalFactory returns a factory that returns polls with
|
||||||
|
// early termination, without doing DAG traversals
|
||||||
|
func NewEarlyTermNoTraversalFactory(alpha int) Factory {
|
||||||
|
return &earlyTermNoTraversalFactory{alpha: alpha}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *earlyTermNoTraversalFactory) New(vdrs ids.ShortSet) Poll {
|
||||||
|
return &earlyTermNoTraversalPoll{
|
||||||
|
polled: vdrs,
|
||||||
|
alpha: f.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// earlyTermNoTraversalPoll finishes when any remaining validators can't change
|
||||||
|
// the result of the poll. However, does not terminate tightly with this bound.
|
||||||
|
// It terminates as quickly as it can without performing any DAG traversals.
|
||||||
|
type earlyTermNoTraversalPoll struct {
|
||||||
|
votes ids.Bag
|
||||||
|
polled ids.ShortSet
|
||||||
|
alpha int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote registers a response for this poll
|
||||||
|
func (p *earlyTermNoTraversalPoll) Vote(vdr ids.ShortID, vote ids.ID) {
|
||||||
|
if !p.polled.Contains(vdr) {
|
||||||
|
// if the validator wasn't polled or already responded to this poll, we
|
||||||
|
// should just drop the vote
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that a validator can't respond multiple times
|
||||||
|
p.polled.Remove(vdr)
|
||||||
|
|
||||||
|
// track the votes the validator responded with
|
||||||
|
p.votes.Add(vote)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop any future response for this poll
|
||||||
|
func (p *earlyTermNoTraversalPoll) Drop(vdr ids.ShortID) {
|
||||||
|
p.polled.Remove(vdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished returns true when all validators have voted
|
||||||
|
func (p *earlyTermNoTraversalPoll) Finished() bool {
|
||||||
|
remaining := p.polled.Len()
|
||||||
|
received := p.votes.Len()
|
||||||
|
_, freq := p.votes.Mode()
|
||||||
|
return remaining == 0 || // All k nodes responded
|
||||||
|
freq >= p.alpha || // An alpha majority has returned
|
||||||
|
received+remaining < p.alpha // An alpha majority can never return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result returns the result of this poll
|
||||||
|
func (p *earlyTermNoTraversalPoll) Result() ids.Bag { return p.votes }
|
||||||
|
|
||||||
|
func (p *earlyTermNoTraversalPoll) String() string {
|
||||||
|
return fmt.Sprintf("waiting on %s", p.polled)
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalResults(t *testing.T) {
|
||||||
|
alpha := 1
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1}) // k = 1
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(vdr1)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := poll.Result()
|
||||||
|
if list := result.List(); len(list) != 1 {
|
||||||
|
t.Fatalf("Wrong number of vertices returned")
|
||||||
|
} else if retVtxID := list[0]; !retVtxID.Equals(vtxID) {
|
||||||
|
t.Fatalf("Wrong vertex returned")
|
||||||
|
} else if result.Count(vtxID) != 1 {
|
||||||
|
t.Fatalf("Wrong number of votes returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalString(t *testing.T) {
|
||||||
|
alpha := 2
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
|
||||||
|
expected := "waiting on {BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp}"
|
||||||
|
if result := poll.String(); expected != result {
|
||||||
|
t.Fatalf("Poll should have returned %s but returned %s", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalDropsDuplicatedVotes(t *testing.T) {
|
||||||
|
alpha := 2
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after getting a duplicated vote")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, vtxID)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalTerminatesEarly(t *testing.T) {
|
||||||
|
alpha := 3
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2})
|
||||||
|
vdr3 := ids.NewShortID([20]byte{3})
|
||||||
|
vdr4 := ids.NewShortID([20]byte{4})
|
||||||
|
vdr5 := ids.NewShortID([20]byte{5}) // k = 5
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
vdr3,
|
||||||
|
vdr4,
|
||||||
|
vdr5,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, vtxID)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr3, vtxID)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate early after receiving alpha votes for one vertex and none for other vertices")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalForSharedAncestor(t *testing.T) {
|
||||||
|
alpha := 4
|
||||||
|
|
||||||
|
vtxA := ids.NewID([32]byte{1})
|
||||||
|
vtxB := ids.NewID([32]byte{2})
|
||||||
|
vtxC := ids.NewID([32]byte{3})
|
||||||
|
vtxD := ids.NewID([32]byte{4})
|
||||||
|
|
||||||
|
// If validators 1-3 vote for frontier vertices
|
||||||
|
// B, C, and D respectively, which all share the common ancestor
|
||||||
|
// A, then we cannot terminate early with alpha = k = 4
|
||||||
|
// If the final vote is cast for any of A, B, C, or D, then
|
||||||
|
// vertex A will have transitively received alpha = 4 votes
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2})
|
||||||
|
vdr3 := ids.NewShortID([20]byte{3})
|
||||||
|
vdr4 := ids.NewShortID([20]byte{4})
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
vdr3,
|
||||||
|
vdr4,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxB)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished early after receiving one vote")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, vtxC)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished early after receiving two votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr3, vtxD)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll terminated early, when a shared ancestor could have received alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr4, vtxA)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving all outstanding votes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarlyTermNoTraversalWithFastDrops(t *testing.T) {
|
||||||
|
alpha := 2
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2})
|
||||||
|
vdr3 := ids.NewShortID([20]byte{3}) // k = 3
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
vdr3,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewEarlyTermNoTraversalFactory(alpha)
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Drop(vdr1)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished early after dropping one vote")
|
||||||
|
}
|
||||||
|
poll.Drop(vdr2)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after dropping two votes")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set is a collection of polls
|
||||||
|
type Set interface {
|
||||||
|
fmt.Stringer
|
||||||
|
|
||||||
|
Add(requestID uint32, vdrs ids.ShortSet) bool
|
||||||
|
Vote(requestID uint32, vdr ids.ShortID, vote ids.ID) (ids.Bag, bool)
|
||||||
|
Drop(requestID uint32, vdr ids.ShortID) (ids.Bag, bool)
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll is an outstanding poll
|
||||||
|
type Poll interface {
|
||||||
|
fmt.Stringer
|
||||||
|
|
||||||
|
Vote(vdr ids.ShortID, vote ids.ID)
|
||||||
|
Drop(vdr ids.ShortID)
|
||||||
|
Finished() bool
|
||||||
|
Result() ids.Bag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory creates a new Poll
|
||||||
|
type Factory interface {
|
||||||
|
New(vdrs ids.ShortSet) Poll
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
type noEarlyTermFactory struct{}
|
||||||
|
|
||||||
|
// NewNoEarlyTermFactory returns a factory that returns polls with no early
|
||||||
|
// termination
|
||||||
|
func NewNoEarlyTermFactory() Factory { return noEarlyTermFactory{} }
|
||||||
|
|
||||||
|
func (noEarlyTermFactory) New(vdrs ids.ShortSet) Poll {
|
||||||
|
return &noEarlyTermPoll{polled: vdrs}
|
||||||
|
}
|
||||||
|
|
||||||
|
// noEarlyTermPoll finishes when all polled validators either respond to the
|
||||||
|
// query or a timeout occurs
|
||||||
|
type noEarlyTermPoll struct {
|
||||||
|
votes ids.Bag
|
||||||
|
polled ids.ShortSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote registers a response for this poll
|
||||||
|
func (p *noEarlyTermPoll) Vote(vdr ids.ShortID, vote ids.ID) {
|
||||||
|
if !p.polled.Contains(vdr) {
|
||||||
|
// if the validator wasn't polled or already responded to this poll, we
|
||||||
|
// should just drop the vote
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that a validator can't respond multiple times
|
||||||
|
p.polled.Remove(vdr)
|
||||||
|
|
||||||
|
// track the votes the validator responded with
|
||||||
|
p.votes.Add(vote)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop any future response for this poll
|
||||||
|
func (p *noEarlyTermPoll) Drop(vdr ids.ShortID) { p.polled.Remove(vdr) }
|
||||||
|
|
||||||
|
// Finished returns true when all validators have voted
|
||||||
|
func (p *noEarlyTermPoll) Finished() bool { return p.polled.Len() == 0 }
|
||||||
|
|
||||||
|
// Result returns the result of this poll
|
||||||
|
func (p *noEarlyTermPoll) Result() ids.Bag { return p.votes }
|
||||||
|
|
||||||
|
func (p *noEarlyTermPoll) String() string {
|
||||||
|
return fmt.Sprintf("waiting on %s", p.polled)
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNoEarlyTermResults(t *testing.T) {
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1}) // k = 1
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(vdr1)
|
||||||
|
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := poll.Result()
|
||||||
|
if list := result.List(); len(list) != 1 {
|
||||||
|
t.Fatalf("Wrong number of vertices returned")
|
||||||
|
} else if retVtxID := list[0]; !retVtxID.Equals(vtxID) {
|
||||||
|
t.Fatalf("Wrong vertex returned")
|
||||||
|
} else if result.Count(vtxID) != 1 {
|
||||||
|
t.Fatalf("Wrong number of votes returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoEarlyTermString(t *testing.T) {
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
|
||||||
|
expected := "waiting on {BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp}"
|
||||||
|
if result := poll.String(); expected != result {
|
||||||
|
t.Fatalf("Poll should have returned %s but returned %s", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoEarlyTermDropsDuplicatedVotes(t *testing.T) {
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
poll := factory.New(vdrs)
|
||||||
|
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after less than alpha votes")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr1, vtxID)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after getting a duplicated vote")
|
||||||
|
}
|
||||||
|
poll.Drop(vdr1)
|
||||||
|
if poll.Finished() {
|
||||||
|
t.Fatalf("Poll finished after getting a duplicated vote")
|
||||||
|
}
|
||||||
|
poll.Vote(vdr2, vtxID)
|
||||||
|
if !poll.Finished() {
|
||||||
|
t.Fatalf("Poll did not terminate after receiving k votes")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
|
"github.com/ava-labs/gecko/utils/timer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type poll struct {
|
||||||
|
Poll
|
||||||
|
start time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type set struct {
|
||||||
|
log logging.Logger
|
||||||
|
numPolls prometheus.Gauge
|
||||||
|
durPolls prometheus.Histogram
|
||||||
|
factory Factory
|
||||||
|
polls map[uint32]poll
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSet returns a new empty set of polls
|
||||||
|
func NewSet(
|
||||||
|
factory Factory,
|
||||||
|
log logging.Logger,
|
||||||
|
namespace string,
|
||||||
|
registerer prometheus.Registerer,
|
||||||
|
) Set {
|
||||||
|
numPolls := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "polls",
|
||||||
|
Help: "Number of pending network polls",
|
||||||
|
})
|
||||||
|
if err := registerer.Register(numPolls); err != nil {
|
||||||
|
log.Error("failed to register polls statistics due to %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
durPolls := prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "poll_duration",
|
||||||
|
Help: "Length of time the poll existed in milliseconds",
|
||||||
|
Buckets: timer.MillisecondsBuckets,
|
||||||
|
})
|
||||||
|
if err := registerer.Register(durPolls); err != nil {
|
||||||
|
log.Error("failed to register poll_duration statistics due to %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &set{
|
||||||
|
log: log,
|
||||||
|
numPolls: numPolls,
|
||||||
|
durPolls: durPolls,
|
||||||
|
factory: factory,
|
||||||
|
polls: make(map[uint32]poll),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the current set of polls
|
||||||
|
// Returns true if the poll was registered correctly and the network sample
|
||||||
|
// should be made.
|
||||||
|
func (s *set) Add(requestID uint32, vdrs ids.ShortSet) bool {
|
||||||
|
if _, exists := s.polls[requestID]; exists {
|
||||||
|
s.log.Debug("dropping poll due to duplicated requestID: %d", requestID)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("creating poll with requestID %d and validators %s",
|
||||||
|
requestID,
|
||||||
|
vdrs)
|
||||||
|
|
||||||
|
s.polls[requestID] = poll{
|
||||||
|
Poll: s.factory.New(vdrs), // create the new poll
|
||||||
|
start: time.Now(),
|
||||||
|
}
|
||||||
|
s.numPolls.Inc() // increase the metrics
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote registers the connections response to a query for [id]. If there was no
|
||||||
|
// query, or the response has already be registered, nothing is performed.
|
||||||
|
func (s *set) Vote(
|
||||||
|
requestID uint32,
|
||||||
|
vdr ids.ShortID,
|
||||||
|
vote ids.ID,
|
||||||
|
) (ids.Bag, bool) {
|
||||||
|
poll, exists := s.polls[requestID]
|
||||||
|
if !exists {
|
||||||
|
s.log.Verbo("dropping vote from %s to an unknown poll with requestID: %d",
|
||||||
|
vdr,
|
||||||
|
requestID)
|
||||||
|
return ids.Bag{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("processing vote from %s in the poll with requestID: %d with the vote %s",
|
||||||
|
vdr,
|
||||||
|
requestID,
|
||||||
|
vote)
|
||||||
|
|
||||||
|
poll.Vote(vdr, vote)
|
||||||
|
if !poll.Finished() {
|
||||||
|
return ids.Bag{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("poll with requestID %d finished as %s", requestID, poll)
|
||||||
|
|
||||||
|
delete(s.polls, requestID) // remove the poll from the current set
|
||||||
|
s.durPolls.Observe(float64(time.Now().Sub(poll.start).Milliseconds()))
|
||||||
|
s.numPolls.Dec() // decrease the metrics
|
||||||
|
return poll.Result(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop registers the connections response to a query for [id]. If there was no
|
||||||
|
// query, or the response has already be registered, nothing is performed.
|
||||||
|
func (s *set) Drop(requestID uint32, vdr ids.ShortID) (ids.Bag, bool) {
|
||||||
|
poll, exists := s.polls[requestID]
|
||||||
|
if !exists {
|
||||||
|
s.log.Verbo("dropping vote from %s to an unknown poll with requestID: %d",
|
||||||
|
vdr,
|
||||||
|
requestID)
|
||||||
|
return ids.Bag{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("processing dropped vote from %s in the poll with requestID: %d",
|
||||||
|
vdr,
|
||||||
|
requestID)
|
||||||
|
|
||||||
|
poll.Drop(vdr)
|
||||||
|
if !poll.Finished() {
|
||||||
|
return ids.Bag{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("poll with requestID %d finished as %s", requestID, poll)
|
||||||
|
|
||||||
|
delete(s.polls, requestID) // remove the poll from the current set
|
||||||
|
s.durPolls.Observe(float64(time.Now().Sub(poll.start).Milliseconds()))
|
||||||
|
s.numPolls.Dec() // decrease the metrics
|
||||||
|
return poll.Result(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of outstanding polls
|
||||||
|
func (s *set) Len() int { return len(s.polls) }
|
||||||
|
|
||||||
|
func (s *set) String() string {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString(fmt.Sprintf("current polls: (Size = %d)", len(s.polls)))
|
||||||
|
for requestID, poll := range s.polls {
|
||||||
|
sb.WriteString(fmt.Sprintf("\n %d: %s", requestID, poll))
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewSetErrorOnMetrics(t *testing.T) {
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
log := logging.NoLog{}
|
||||||
|
namespace := ""
|
||||||
|
registerer := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
registerer.Register(prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "polls",
|
||||||
|
}))
|
||||||
|
registerer.Register(prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "poll_duration",
|
||||||
|
}))
|
||||||
|
|
||||||
|
_ = NewSet(factory, log, namespace, registerer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateAndFinishSuccessfulPoll(t *testing.T) {
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
log := logging.NoLog{}
|
||||||
|
namespace := ""
|
||||||
|
registerer := prometheus.NewRegistry()
|
||||||
|
s := NewSet(factory, log, namespace, registerer)
|
||||||
|
|
||||||
|
vtxID := ids.NewID([32]byte{1})
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
if s.Len() != 0 {
|
||||||
|
t.Fatalf("Shouldn't have any active polls yet")
|
||||||
|
} else if !s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Should have been able to add a new poll")
|
||||||
|
} else if s.Len() != 1 {
|
||||||
|
t.Fatalf("Should only have one active poll")
|
||||||
|
} else if s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Shouldn't have been able to add a duplicated poll")
|
||||||
|
} else if s.Len() != 1 {
|
||||||
|
t.Fatalf("Should only have one active poll")
|
||||||
|
} else if _, finished := s.Vote(1, vdr1, vtxID); finished {
|
||||||
|
t.Fatalf("Shouldn't have been able to finish a non-existant poll")
|
||||||
|
} else if _, finished := s.Vote(0, vdr1, vtxID); finished {
|
||||||
|
t.Fatalf("Shouldn't have been able to finish an ongoing poll")
|
||||||
|
} else if _, finished := s.Vote(0, vdr1, vtxID); finished {
|
||||||
|
t.Fatalf("Should have dropped a duplicated poll")
|
||||||
|
} else if result, finished := s.Vote(0, vdr2, vtxID); !finished {
|
||||||
|
t.Fatalf("Should have finished the")
|
||||||
|
} else if list := result.List(); len(list) != 1 {
|
||||||
|
t.Fatalf("Wrong number of vertices returned")
|
||||||
|
} else if retVtxID := list[0]; !retVtxID.Equals(vtxID) {
|
||||||
|
t.Fatalf("Wrong vertex returned")
|
||||||
|
} else if result.Count(vtxID) != 2 {
|
||||||
|
t.Fatalf("Wrong number of votes returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateAndFinishFailedPoll(t *testing.T) {
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
log := logging.NoLog{}
|
||||||
|
namespace := ""
|
||||||
|
registerer := prometheus.NewRegistry()
|
||||||
|
s := NewSet(factory, log, namespace, registerer)
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1})
|
||||||
|
vdr2 := ids.NewShortID([20]byte{2}) // k = 2
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(
|
||||||
|
vdr1,
|
||||||
|
vdr2,
|
||||||
|
)
|
||||||
|
|
||||||
|
if s.Len() != 0 {
|
||||||
|
t.Fatalf("Shouldn't have any active polls yet")
|
||||||
|
} else if !s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Should have been able to add a new poll")
|
||||||
|
} else if s.Len() != 1 {
|
||||||
|
t.Fatalf("Should only have one active poll")
|
||||||
|
} else if s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Shouldn't have been able to add a duplicated poll")
|
||||||
|
} else if s.Len() != 1 {
|
||||||
|
t.Fatalf("Should only have one active poll")
|
||||||
|
} else if _, finished := s.Drop(1, vdr1); finished {
|
||||||
|
t.Fatalf("Shouldn't have been able to finish a non-existant poll")
|
||||||
|
} else if _, finished := s.Drop(0, vdr1); finished {
|
||||||
|
t.Fatalf("Shouldn't have been able to finish an ongoing poll")
|
||||||
|
} else if _, finished := s.Drop(0, vdr1); finished {
|
||||||
|
t.Fatalf("Should have dropped a duplicated poll")
|
||||||
|
} else if result, finished := s.Drop(0, vdr2); !finished {
|
||||||
|
t.Fatalf("Should have finished the")
|
||||||
|
} else if list := result.List(); len(list) != 0 {
|
||||||
|
t.Fatalf("Wrong number of vertices returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetString(t *testing.T) {
|
||||||
|
factory := NewNoEarlyTermFactory()
|
||||||
|
log := logging.NoLog{}
|
||||||
|
namespace := ""
|
||||||
|
registerer := prometheus.NewRegistry()
|
||||||
|
s := NewSet(factory, log, namespace, registerer)
|
||||||
|
|
||||||
|
vdr1 := ids.NewShortID([20]byte{1}) // k = 1
|
||||||
|
|
||||||
|
vdrs := ids.ShortSet{}
|
||||||
|
vdrs.Add(vdr1)
|
||||||
|
|
||||||
|
expected := "current polls: (Size = 1)\n" +
|
||||||
|
" 0: waiting on {6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt}"
|
||||||
|
if !s.Add(0, vdrs) {
|
||||||
|
t.Fatalf("Should have been able to add a new poll")
|
||||||
|
} else if str := s.String(); expected != str {
|
||||||
|
t.Fatalf("Set return wrong string, Expected:\n%s\nReturned:\n%s",
|
||||||
|
expected,
|
||||||
|
str)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,115 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package snowman
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/ava-labs/gecko/utils/logging"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
)
|
|
||||||
|
|
||||||
type polls struct {
|
|
||||||
log logging.Logger
|
|
||||||
numPolls prometheus.Gauge
|
|
||||||
alpha int
|
|
||||||
m map[uint32]poll
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the current set of polls
|
|
||||||
// Returns true if the poll was registered correctly and the network sample
|
|
||||||
// should be made.
|
|
||||||
func (p *polls) Add(requestID uint32, vdrs ids.ShortSet) bool {
|
|
||||||
poll, exists := p.m[requestID]
|
|
||||||
if !exists {
|
|
||||||
poll.alpha = p.alpha
|
|
||||||
poll.polled = vdrs
|
|
||||||
p.m[requestID] = poll
|
|
||||||
|
|
||||||
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
||||||
}
|
|
||||||
return !exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vote registers the connections response to a query for [id]. If there was no
|
|
||||||
// query, or the response has already be registered, nothing is performed.
|
|
||||||
func (p *polls) Vote(requestID uint32, vdr ids.ShortID, vote ids.ID) (ids.Bag, bool) {
|
|
||||||
p.log.Verbo("[polls.Vote] Vote: requestID: %d. validatorID: %s. Vote: %s", requestID, vdr, vote)
|
|
||||||
poll, exists := p.m[requestID]
|
|
||||||
if !exists {
|
|
||||||
return ids.Bag{}, false
|
|
||||||
}
|
|
||||||
poll.Vote(vote, vdr)
|
|
||||||
if poll.Finished() {
|
|
||||||
delete(p.m, requestID)
|
|
||||||
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
||||||
return poll.votes, true
|
|
||||||
}
|
|
||||||
p.m[requestID] = poll
|
|
||||||
return ids.Bag{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// CancelVote registers the connections failure to respond to a query for [id].
|
|
||||||
func (p *polls) CancelVote(requestID uint32, vdr ids.ShortID) (ids.Bag, bool) {
|
|
||||||
p.log.Verbo("CancelVote received. requestID: %d. validatorID: %s. Vote: %s", requestID, vdr)
|
|
||||||
poll, exists := p.m[requestID]
|
|
||||||
if !exists {
|
|
||||||
return ids.Bag{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
poll.CancelVote(vdr)
|
|
||||||
if poll.Finished() {
|
|
||||||
delete(p.m, requestID)
|
|
||||||
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
||||||
return poll.votes, true
|
|
||||||
}
|
|
||||||
p.m[requestID] = poll
|
|
||||||
return ids.Bag{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *polls) String() string {
|
|
||||||
sb := strings.Builder{}
|
|
||||||
|
|
||||||
sb.WriteString(fmt.Sprintf("Current polls: (Size = %d)", len(p.m)))
|
|
||||||
for requestID, poll := range p.m {
|
|
||||||
sb.WriteString(fmt.Sprintf("\n %d: %s", requestID, poll))
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// poll represents the current state of a network poll for a block
|
|
||||||
type poll struct {
|
|
||||||
alpha int
|
|
||||||
votes ids.Bag
|
|
||||||
polled ids.ShortSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vote registers a vote for this poll
|
|
||||||
func (p *poll) CancelVote(vdr ids.ShortID) { p.polled.Remove(vdr) }
|
|
||||||
|
|
||||||
// Vote registers a vote for this poll
|
|
||||||
func (p *poll) Vote(vote ids.ID, vdr ids.ShortID) {
|
|
||||||
if p.polled.Contains(vdr) {
|
|
||||||
p.polled.Remove(vdr)
|
|
||||||
p.votes.Add(vote)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finished returns true if the poll has completed, with no more required
|
|
||||||
// responses
|
|
||||||
func (p poll) Finished() bool {
|
|
||||||
remaining := p.polled.Len()
|
|
||||||
received := p.votes.Len()
|
|
||||||
_, freq := p.votes.Mode()
|
|
||||||
return remaining == 0 || // All k nodes responded
|
|
||||||
freq >= p.alpha || // An alpha majority has returned
|
|
||||||
received+remaining < p.alpha // An alpha majority can never return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p poll) String() string {
|
|
||||||
return fmt.Sprintf("Waiting on %d chits from %s", p.polled.Len(), p.polled)
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/ava-labs/gecko/snow/choices"
|
"github.com/ava-labs/gecko/snow/choices"
|
||||||
"github.com/ava-labs/gecko/snow/consensus/snowman"
|
"github.com/ava-labs/gecko/snow/consensus/snowman"
|
||||||
"github.com/ava-labs/gecko/snow/engine/common"
|
"github.com/ava-labs/gecko/snow/engine/common"
|
||||||
|
"github.com/ava-labs/gecko/snow/engine/snowman/poll"
|
||||||
"github.com/ava-labs/gecko/snow/events"
|
"github.com/ava-labs/gecko/snow/events"
|
||||||
"github.com/ava-labs/gecko/utils/formatting"
|
"github.com/ava-labs/gecko/utils/formatting"
|
||||||
"github.com/ava-labs/gecko/utils/wrappers"
|
"github.com/ava-labs/gecko/utils/wrappers"
|
||||||
|
@ -30,7 +31,7 @@ type Transitive struct {
|
||||||
bootstrapper
|
bootstrapper
|
||||||
|
|
||||||
// track outstanding preference requests
|
// track outstanding preference requests
|
||||||
polls polls
|
polls poll.Set
|
||||||
|
|
||||||
// blocks that have outstanding get requests
|
// blocks that have outstanding get requests
|
||||||
blkReqs common.Requests
|
blkReqs common.Requests
|
||||||
|
@ -64,10 +65,12 @@ func (t *Transitive) Initialize(config Config) error {
|
||||||
|
|
||||||
t.onFinished = t.finishBootstrapping
|
t.onFinished = t.finishBootstrapping
|
||||||
|
|
||||||
t.polls.log = config.Context.Log
|
factory := poll.NewEarlyTermNoTraversalFactory(int(config.Params.Alpha))
|
||||||
t.polls.numPolls = t.numPolls
|
t.polls = poll.NewSet(factory,
|
||||||
t.polls.alpha = t.Params.Alpha
|
config.Context.Log,
|
||||||
t.polls.m = make(map[uint32]poll)
|
config.Params.Namespace,
|
||||||
|
config.Params.Metrics,
|
||||||
|
)
|
||||||
|
|
||||||
return t.bootstrapper.Initialize(config.BootstrapConfig)
|
return t.bootstrapper.Initialize(config.BootstrapConfig)
|
||||||
}
|
}
|
||||||
|
@ -409,7 +412,7 @@ func (t *Transitive) repoll() {
|
||||||
// propagate the most likely branch as quickly as possible
|
// propagate the most likely branch as quickly as possible
|
||||||
prefID := t.Consensus.Preference()
|
prefID := t.Consensus.Preference()
|
||||||
|
|
||||||
for i := len(t.polls.m); i < t.Params.ConcurrentRepolls; i++ {
|
for i := t.polls.Len(); i < t.Params.ConcurrentRepolls; i++ {
|
||||||
t.pullSample(prefID)
|
t.pullSample(prefID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -810,13 +810,13 @@ func TestVoteCanceling(t *testing.T) {
|
||||||
|
|
||||||
te.insert(blk)
|
te.insert(blk)
|
||||||
|
|
||||||
if len(te.polls.m) != 1 {
|
if te.polls.Len() != 1 {
|
||||||
t.Fatalf("Shouldn't have finished blocking issue")
|
t.Fatalf("Shouldn't have finished blocking issue")
|
||||||
}
|
}
|
||||||
|
|
||||||
te.QueryFailed(vdr0.ID(), *queryRequestID)
|
te.QueryFailed(vdr0.ID(), *queryRequestID)
|
||||||
|
|
||||||
if len(te.polls.m) != 1 {
|
if te.polls.Len() != 1 {
|
||||||
t.Fatalf("Shouldn't have finished blocking issue")
|
t.Fatalf("Shouldn't have finished blocking issue")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ func (v *voter) Update() {
|
||||||
results := ids.Bag{}
|
results := ids.Bag{}
|
||||||
finished := false
|
finished := false
|
||||||
if v.response.IsZero() {
|
if v.response.IsZero() {
|
||||||
results, finished = v.t.polls.CancelVote(v.requestID, v.vdr)
|
results, finished = v.t.polls.Drop(v.requestID, v.vdr)
|
||||||
} else {
|
} else {
|
||||||
results, finished = v.t.polls.Vote(v.requestID, v.vdr, v.response)
|
results, finished = v.t.polls.Vote(v.requestID, v.vdr, v.response)
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,7 +320,7 @@ func (sr *ChainRouter) QueryFailed(validatorID ids.ShortID, chainID ids.ID, requ
|
||||||
if chain, exists := sr.chains[chainID.Key()]; exists {
|
if chain, exists := sr.chains[chainID.Key()]; exists {
|
||||||
chain.QueryFailed(validatorID, requestID)
|
chain.QueryFailed(validatorID, requestID)
|
||||||
} else {
|
} else {
|
||||||
sr.log.Error("QueryFailed(%s, %s, %d, %s) dropped due to unknown chain", validatorID, chainID, requestID)
|
sr.log.Error("QueryFailed(%s, %s, %d) dropped due to unknown chain", validatorID, chainID, requestID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/ava-labs/gecko/database"
|
"github.com/ava-labs/gecko/database"
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/snow"
|
"github.com/ava-labs/gecko/snow"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ func (t *BaseTx) InputUTXOs() []*ava.UTXOID {
|
||||||
return utxos
|
return utxos
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetIDs returns the IDs of the assets this transaction depends on
|
// ConsumedAssetIDs returns the IDs of the assets this transaction consumes
|
||||||
func (t *BaseTx) AssetIDs() ids.Set {
|
func (t *BaseTx) ConsumedAssetIDs() ids.Set {
|
||||||
assets := ids.Set{}
|
assets := ids.Set{}
|
||||||
for _, in := range t.Ins {
|
for _, in := range t.Ins {
|
||||||
assets.Add(in.AssetID())
|
assets.Add(in.AssetID())
|
||||||
|
@ -55,6 +55,11 @@ func (t *BaseTx) AssetIDs() ids.Set {
|
||||||
return assets
|
return assets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssetIDs returns the IDs of the assets this transaction depends on
|
||||||
|
func (t *BaseTx) AssetIDs() ids.Set {
|
||||||
|
return t.ConsumedAssetIDs()
|
||||||
|
}
|
||||||
|
|
||||||
// NumCredentials returns the number of expected credentials
|
// NumCredentials returns the number of expected credentials
|
||||||
func (t *BaseTx) NumCredentials() int { return len(t.Ins) }
|
func (t *BaseTx) NumCredentials() int { return len(t.Ins) }
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ func TestBaseTxSerialization(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -87,7 +87,7 @@ func TestBaseTxSerialization(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -125,7 +125,7 @@ func TestBaseTxGetters(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -135,7 +135,7 @@ func TestBaseTxGetters(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -162,6 +162,10 @@ func TestBaseTxGetters(t *testing.T) {
|
||||||
t.Fatalf("Wrong number of assets returned")
|
t.Fatalf("Wrong number of assets returned")
|
||||||
} else if !assets.Contains(asset) {
|
} else if !assets.Contains(asset) {
|
||||||
t.Fatalf("Wrong asset returned")
|
t.Fatalf("Wrong asset returned")
|
||||||
|
} else if assets := tx.ConsumedAssetIDs(); assets.Len() != 1 {
|
||||||
|
t.Fatalf("Wrong number of consumed assets returned")
|
||||||
|
} else if !assets.Contains(asset) {
|
||||||
|
t.Fatalf("Wrong consumed asset returned")
|
||||||
} else if utxos := tx.UTXOs(); len(utxos) != 1 {
|
} else if utxos := tx.UTXOs(); len(utxos) != 1 {
|
||||||
t.Fatalf("Wrong number of utxos returned")
|
t.Fatalf("Wrong number of utxos returned")
|
||||||
} else if utxo := utxos[0]; !utxo.TxID.Equals(txID) {
|
} else if utxo := utxos[0]; !utxo.TxID.Equals(txID) {
|
||||||
|
@ -179,7 +183,7 @@ func TestBaseTxSyntacticVerify(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -189,7 +193,7 @@ func TestBaseTxSyntacticVerify(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -230,7 +234,7 @@ func TestBaseTxSyntacticVerifyWrongNetworkID(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: 0,
|
NetID: 0,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -240,7 +244,7 @@ func TestBaseTxSyntacticVerifyWrongNetworkID(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -272,7 +276,7 @@ func TestBaseTxSyntacticVerifyWrongChainID(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: ids.Empty,
|
BCID: ids.Empty,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -282,7 +286,7 @@ func TestBaseTxSyntacticVerifyWrongChainID(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -315,7 +319,7 @@ func TestBaseTxSyntacticVerifyInvalidOutput(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{nil},
|
Outs: []*ava.TransferableOutput{nil},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -348,7 +352,7 @@ func TestBaseTxSyntacticVerifyUnsortedOutputs(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{
|
||||||
&ava.TransferableOutput{
|
{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 2,
|
Amt: 2,
|
||||||
|
@ -358,7 +362,7 @@ func TestBaseTxSyntacticVerifyUnsortedOutputs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&ava.TransferableOutput{
|
{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 1,
|
Amt: 1,
|
||||||
|
@ -370,7 +374,7 @@ func TestBaseTxSyntacticVerifyUnsortedOutputs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ins: []*ava.TransferableInput{
|
Ins: []*ava.TransferableInput{
|
||||||
&ava.TransferableInput{
|
{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -403,7 +407,7 @@ func TestBaseTxSyntacticVerifyInvalidInput(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -428,7 +432,7 @@ func TestBaseTxSyntacticVerifyInputOverflow(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -439,7 +443,7 @@ func TestBaseTxSyntacticVerifyInputOverflow(t *testing.T) {
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{
|
Ins: []*ava.TransferableInput{
|
||||||
&ava.TransferableInput{
|
{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -457,7 +461,7 @@ func TestBaseTxSyntacticVerifyInputOverflow(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&ava.TransferableInput{
|
{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -491,7 +495,7 @@ func TestBaseTxSyntacticVerifyOutputOverflow(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{
|
||||||
&ava.TransferableOutput{
|
{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 2,
|
Amt: 2,
|
||||||
|
@ -501,7 +505,7 @@ func TestBaseTxSyntacticVerifyOutputOverflow(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&ava.TransferableOutput{
|
{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: math.MaxUint64,
|
Amt: math.MaxUint64,
|
||||||
|
@ -512,7 +516,7 @@ func TestBaseTxSyntacticVerifyOutputOverflow(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -544,7 +548,7 @@ func TestBaseTxSyntacticVerifyInsufficientFunds(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: math.MaxUint64,
|
Amt: math.MaxUint64,
|
||||||
|
@ -554,7 +558,7 @@ func TestBaseTxSyntacticVerifyInsufficientFunds(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -586,7 +590,7 @@ func TestBaseTxSyntacticVerifyUninitialized(t *testing.T) {
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 12345,
|
Amt: 12345,
|
||||||
|
@ -596,7 +600,7 @@ func TestBaseTxSyntacticVerifyUninitialized(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
@ -633,7 +637,7 @@ func TestBaseTxSemanticVerify(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -702,7 +706,7 @@ func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -754,7 +758,7 @@ func TestBaseTxSemanticVerifyWrongAssetID(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -825,11 +829,11 @@ func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) {
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{
|
[]*common.Fx{
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
},
|
},
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.NewID([32]byte{1}),
|
ID: ids.NewID([32]byte{1}),
|
||||||
Fx: &testFx{},
|
Fx: &testFx{},
|
||||||
},
|
},
|
||||||
|
@ -863,7 +867,7 @@ func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -923,7 +927,7 @@ func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -941,9 +945,7 @@ func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
|
||||||
}}
|
}}
|
||||||
|
|
||||||
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
||||||
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
Sigs: [][crypto.SECP256K1RSigLen]byte{{}},
|
||||||
[crypto.SECP256K1RSigLen]byte{},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
b, err := vm.codec.Marshal(tx)
|
b, err := vm.codec.Marshal(tx)
|
||||||
|
@ -977,7 +979,7 @@ func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -1044,7 +1046,7 @@ func TestBaseTxSemanticVerifyInvalidUTXO(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: math.MaxUint32,
|
OutputIndex: math.MaxUint32,
|
||||||
|
@ -1107,7 +1109,7 @@ func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
|
||||||
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -1122,7 +1124,7 @@ func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 50000,
|
Amt: 50000,
|
||||||
|
@ -1179,7 +1181,7 @@ func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: txID,
|
TxID: txID,
|
||||||
OutputIndex: 2,
|
OutputIndex: 2,
|
||||||
|
@ -1241,7 +1243,7 @@ func TestBaseTxSemanticVerifyPendingWrongAssetID(t *testing.T) {
|
||||||
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -1256,7 +1258,7 @@ func TestBaseTxSemanticVerifyPendingWrongAssetID(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 50000,
|
Amt: 50000,
|
||||||
|
@ -1313,7 +1315,7 @@ func TestBaseTxSemanticVerifyPendingWrongAssetID(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: txID,
|
TxID: txID,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -1381,11 +1383,11 @@ func TestBaseTxSemanticVerifyPendingUnauthorizedFx(t *testing.T) {
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{
|
[]*common.Fx{
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.NewID([32]byte{1}),
|
ID: ids.NewID([32]byte{1}),
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
},
|
},
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &testFx{},
|
Fx: &testFx{},
|
||||||
},
|
},
|
||||||
|
@ -1419,7 +1421,7 @@ func TestBaseTxSemanticVerifyPendingUnauthorizedFx(t *testing.T) {
|
||||||
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -1434,7 +1436,7 @@ func TestBaseTxSemanticVerifyPendingUnauthorizedFx(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 50000,
|
Amt: 50000,
|
||||||
|
@ -1491,7 +1493,7 @@ func TestBaseTxSemanticVerifyPendingUnauthorizedFx(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: txID,
|
TxID: txID,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -1543,11 +1545,11 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) {
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{
|
[]*common.Fx{
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.NewID([32]byte{1}),
|
ID: ids.NewID([32]byte{1}),
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
},
|
},
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &testFx{},
|
Fx: &testFx{},
|
||||||
},
|
},
|
||||||
|
@ -1581,7 +1583,7 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) {
|
||||||
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
pendingTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -1596,7 +1598,7 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 50000,
|
Amt: 50000,
|
||||||
|
@ -1653,7 +1655,7 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: txID,
|
TxID: txID,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -1671,9 +1673,7 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) {
|
||||||
}}
|
}}
|
||||||
|
|
||||||
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
||||||
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
Sigs: [][crypto.SECP256K1RSigLen]byte{{}},
|
||||||
[crypto.SECP256K1RSigLen]byte{},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
b, err = vm.codec.Marshal(tx)
|
b, err = vm.codec.Marshal(tx)
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||||
)
|
)
|
||||||
|
@ -93,7 +93,7 @@ func TestCreateAssetTxSerialization(t *testing.T) {
|
||||||
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||||
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
||||||
}),
|
}),
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{
|
Asset: ava.Asset{
|
||||||
ID: ids.NewID([32]byte{
|
ID: ids.NewID([32]byte{
|
||||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
@ -122,7 +122,7 @@ func TestCreateAssetTxSerialization(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.NewID([32]byte{
|
TxID: ids.NewID([32]byte{
|
||||||
0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81,
|
0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81,
|
||||||
|
@ -152,7 +152,7 @@ func TestCreateAssetTxSerialization(t *testing.T) {
|
||||||
Symbol: "VIX",
|
Symbol: "VIX",
|
||||||
Denomination: 2,
|
Denomination: 2,
|
||||||
States: []*InitialState{
|
States: []*InitialState{
|
||||||
&InitialState{
|
{
|
||||||
FxID: 0,
|
FxID: 0,
|
||||||
Outs: []verify.Verifiable{
|
Outs: []verify.Verifiable{
|
||||||
&secp256k1fx.TransferOutput{
|
&secp256k1fx.TransferOutput{
|
||||||
|
|
|
@ -12,11 +12,11 @@ import (
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/snow"
|
"github.com/ava-labs/gecko/snow"
|
||||||
"github.com/ava-labs/gecko/snow/engine/common"
|
"github.com/ava-labs/gecko/snow/engine/common"
|
||||||
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
"github.com/ava-labs/gecko/utils/crypto"
|
"github.com/ava-labs/gecko/utils/crypto"
|
||||||
"github.com/ava-labs/gecko/utils/hashing"
|
"github.com/ava-labs/gecko/utils/hashing"
|
||||||
"github.com/ava-labs/gecko/utils/logging"
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
|
||||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ func TestExportTxSerialization(t *testing.T) {
|
||||||
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||||
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
||||||
}),
|
}),
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
|
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
|
||||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||||
|
@ -141,7 +141,7 @@ func TestIssueExportTx(t *testing.T) {
|
||||||
memdb.New(),
|
memdb.New(),
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{&common.Fx{
|
[]*common.Fx{{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
}},
|
}},
|
||||||
|
@ -167,7 +167,7 @@ func TestIssueExportTx(t *testing.T) {
|
||||||
BaseTx: BaseTx{
|
BaseTx: BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: avaID,
|
TxID: avaID,
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -179,7 +179,7 @@ func TestIssueExportTx(t *testing.T) {
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: avaID},
|
Asset: ava.Asset{ID: avaID},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 50000,
|
Amt: 50000,
|
||||||
|
@ -297,7 +297,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
|
||||||
memdb.New(),
|
memdb.New(),
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{&common.Fx{
|
[]*common.Fx{{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
}},
|
}},
|
||||||
|
@ -323,7 +323,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
|
||||||
BaseTx: BaseTx{
|
BaseTx: BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: avaID,
|
TxID: avaID,
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -335,7 +335,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: avaID},
|
Asset: ava.Asset{ID: avaID},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 50000,
|
Amt: 50000,
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"github.com/ava-labs/gecko/database/versiondb"
|
"github.com/ava-labs/gecko/database/versiondb"
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/snow"
|
"github.com/ava-labs/gecko/snow"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,6 +33,15 @@ func (t *ImportTx) InputUTXOs() []*ava.UTXOID {
|
||||||
return utxos
|
return utxos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConsumedAssetIDs returns the IDs of the assets this transaction consumes
|
||||||
|
func (t *ImportTx) ConsumedAssetIDs() ids.Set {
|
||||||
|
assets := t.BaseTx.AssetIDs()
|
||||||
|
for _, in := range t.Ins {
|
||||||
|
assets.Add(in.AssetID())
|
||||||
|
}
|
||||||
|
return assets
|
||||||
|
}
|
||||||
|
|
||||||
// AssetIDs returns the IDs of the assets this transaction depends on
|
// AssetIDs returns the IDs of the assets this transaction depends on
|
||||||
func (t *ImportTx) AssetIDs() ids.Set {
|
func (t *ImportTx) AssetIDs() ids.Set {
|
||||||
assets := t.BaseTx.AssetIDs()
|
assets := t.BaseTx.AssetIDs()
|
||||||
|
|
|
@ -68,7 +68,7 @@ func TestImportTxSerialization(t *testing.T) {
|
||||||
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
|
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
|
||||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||||
|
@ -130,7 +130,7 @@ func TestIssueImportTx(t *testing.T) {
|
||||||
memdb.New(),
|
memdb.New(),
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{&common.Fx{
|
[]*common.Fx{{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
}},
|
}},
|
||||||
|
@ -166,7 +166,7 @@ func TestIssueImportTx(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
},
|
},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: utxoID,
|
UTXOID: utxoID,
|
||||||
Asset: ava.Asset{ID: avaID},
|
Asset: ava.Asset{ID: avaID},
|
||||||
In: &secp256k1fx.TransferInput{
|
In: &secp256k1fx.TransferInput{
|
||||||
|
@ -288,7 +288,7 @@ func TestForceAcceptImportTx(t *testing.T) {
|
||||||
memdb.New(),
|
memdb.New(),
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{&common.Fx{
|
[]*common.Fx{{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
}},
|
}},
|
||||||
|
@ -326,7 +326,7 @@ func TestForceAcceptImportTx(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
},
|
},
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: utxoID,
|
UTXOID: utxoID,
|
||||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||||
In: &secp256k1fx.TransferInput{
|
In: &secp256k1fx.TransferInput{
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,11 +43,11 @@ func TestOperationVerifyUTXOIDsNotSorted(t *testing.T) {
|
||||||
op := &Operation{
|
op := &Operation{
|
||||||
Asset: ava.Asset{ID: ids.Empty},
|
Asset: ava.Asset{ID: ids.Empty},
|
||||||
UTXOIDs: []*ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{
|
||||||
&ava.UTXOID{
|
{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
},
|
},
|
||||||
&ava.UTXOID{
|
{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
},
|
},
|
||||||
|
@ -64,7 +64,7 @@ func TestOperationVerify(t *testing.T) {
|
||||||
op := &Operation{
|
op := &Operation{
|
||||||
Asset: ava.Asset{ID: ids.Empty},
|
Asset: ava.Asset{ID: ids.Empty},
|
||||||
UTXOIDs: []*ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{
|
||||||
&ava.UTXOID{
|
{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
},
|
},
|
||||||
|
@ -81,20 +81,20 @@ func TestOperationSorting(t *testing.T) {
|
||||||
c.RegisterType(&testOperable{})
|
c.RegisterType(&testOperable{})
|
||||||
|
|
||||||
ops := []*Operation{
|
ops := []*Operation{
|
||||||
&Operation{
|
{
|
||||||
Asset: ava.Asset{ID: ids.Empty},
|
Asset: ava.Asset{ID: ids.Empty},
|
||||||
UTXOIDs: []*ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{
|
||||||
&ava.UTXOID{
|
{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Op: &testOperable{},
|
Op: &testOperable{},
|
||||||
},
|
},
|
||||||
&Operation{
|
{
|
||||||
Asset: ava.Asset{ID: ids.Empty},
|
Asset: ava.Asset{ID: ids.Empty},
|
||||||
UTXOIDs: []*ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{
|
||||||
&ava.UTXOID{
|
{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
},
|
},
|
||||||
|
@ -112,7 +112,7 @@ func TestOperationSorting(t *testing.T) {
|
||||||
ops = append(ops, &Operation{
|
ops = append(ops, &Operation{
|
||||||
Asset: ava.Asset{ID: ids.Empty},
|
Asset: ava.Asset{ID: ids.Empty},
|
||||||
UTXOIDs: []*ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{
|
||||||
&ava.UTXOID{
|
{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/snow"
|
"github.com/ava-labs/gecko/snow"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,6 +39,17 @@ func (t *OperationTx) InputUTXOs() []*ava.UTXOID {
|
||||||
return utxos
|
return utxos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConsumedAssetIDs returns the IDs of the assets this transaction consumes
|
||||||
|
func (t *OperationTx) ConsumedAssetIDs() ids.Set {
|
||||||
|
assets := t.BaseTx.AssetIDs()
|
||||||
|
for _, op := range t.Ops {
|
||||||
|
if len(op.UTXOIDs) > 0 {
|
||||||
|
assets.Add(op.AssetID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return assets
|
||||||
|
}
|
||||||
|
|
||||||
// AssetIDs returns the IDs of the assets this transaction depends on
|
// AssetIDs returns the IDs of the assets this transaction depends on
|
||||||
func (t *OperationTx) AssetIDs() ids.Set {
|
func (t *OperationTx) AssetIDs() ids.Set {
|
||||||
assets := t.BaseTx.AssetIDs()
|
assets := t.BaseTx.AssetIDs()
|
||||||
|
|
|
@ -38,7 +38,7 @@ func TestPrefixedSetsAndGets(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -160,9 +160,7 @@ func TestPrefixedFundingAddresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
Asset: ava.Asset{ID: ids.Empty},
|
Asset: ava.Asset{ID: ids.Empty},
|
||||||
Out: &ava.TestAddressable{
|
Out: &ava.TestAddressable{
|
||||||
Addrs: [][]byte{
|
Addrs: [][]byte{{0}},
|
||||||
[]byte{0},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -792,7 +792,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
|
||||||
|
|
||||||
ava.SortTransferableInputsWithSigners(ins, keys)
|
ava.SortTransferableInputsWithSigners(ins, keys)
|
||||||
|
|
||||||
outs := []*ava.TransferableOutput{&ava.TransferableOutput{
|
outs := []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: assetID},
|
Asset: ava.Asset{ID: assetID},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: uint64(args.Amount),
|
Amt: uint64(args.Amount),
|
||||||
|
@ -946,7 +946,7 @@ func (service *Service) CreateMintTx(r *http.Request, args *CreateMintTxArgs, re
|
||||||
BCID: service.vm.ctx.ChainID,
|
BCID: service.vm.ctx.ChainID,
|
||||||
},
|
},
|
||||||
Ops: []*Operation{
|
Ops: []*Operation{
|
||||||
&Operation{
|
{
|
||||||
Asset: ava.Asset{ID: assetID},
|
Asset: ava.Asset{ID: assetID},
|
||||||
UTXOIDs: []*ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{
|
||||||
&utxo.UTXOID,
|
&utxo.UTXOID,
|
||||||
|
@ -1197,7 +1197,7 @@ func (service *Service) ImportAVA(_ *http.Request, args *ImportAVAArgs, reply *I
|
||||||
|
|
||||||
ava.SortTransferableInputsWithSigners(ins, keys)
|
ava.SortTransferableInputsWithSigners(ins, keys)
|
||||||
|
|
||||||
outs := []*ava.TransferableOutput{&ava.TransferableOutput{
|
outs := []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: service.vm.ava},
|
Asset: ava.Asset{ID: service.vm.ava},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: amount,
|
Amt: amount,
|
||||||
|
@ -1352,7 +1352,7 @@ func (service *Service) ExportAVA(_ *http.Request, args *ExportAVAArgs, reply *E
|
||||||
|
|
||||||
ava.SortTransferableInputsWithSigners(ins, keys)
|
ava.SortTransferableInputsWithSigners(ins, keys)
|
||||||
|
|
||||||
exportOuts := []*ava.TransferableOutput{&ava.TransferableOutput{
|
exportOuts := []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: service.vm.ava},
|
Asset: ava.Asset{ID: service.vm.ava},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: uint64(args.Amount),
|
Amt: uint64(args.Amount),
|
||||||
|
|
|
@ -300,7 +300,7 @@ func TestCreateFixedCapAsset(t *testing.T) {
|
||||||
Name: "test asset",
|
Name: "test asset",
|
||||||
Symbol: "test",
|
Symbol: "test",
|
||||||
Denomination: 1,
|
Denomination: 1,
|
||||||
InitialHolders: []*Holder{&Holder{
|
InitialHolders: []*Holder{{
|
||||||
Amount: 123456789,
|
Amount: 123456789,
|
||||||
Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
|
Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
|
||||||
}},
|
}},
|
||||||
|
@ -326,7 +326,7 @@ func TestCreateVariableCapAsset(t *testing.T) {
|
||||||
Name: "test asset",
|
Name: "test asset",
|
||||||
Symbol: "test",
|
Symbol: "test",
|
||||||
MinterSets: []Owners{
|
MinterSets: []Owners{
|
||||||
Owners{
|
{
|
||||||
Threshold: 1,
|
Threshold: 1,
|
||||||
Minters: []string{
|
Minters: []string{
|
||||||
vm.Format(keys[0].PublicKey().Address().Bytes()),
|
vm.Format(keys[0].PublicKey().Address().Bytes()),
|
||||||
|
@ -367,7 +367,7 @@ func TestImportAvmKey(t *testing.T) {
|
||||||
factory := crypto.FactorySECP256K1R{}
|
factory := crypto.FactorySECP256K1R{}
|
||||||
skIntf, err := factory.NewPrivateKey()
|
skIntf, err := factory.NewPrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("problem generating private key: %w", err)
|
t.Fatalf("problem generating private key: %s", err)
|
||||||
}
|
}
|
||||||
sk := skIntf.(*crypto.PrivateKeySECP256K1R)
|
sk := skIntf.(*crypto.PrivateKeySECP256K1R)
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ func TestImportAvmKeyNoDuplicates(t *testing.T) {
|
||||||
factory := crypto.FactorySECP256K1R{}
|
factory := crypto.FactorySECP256K1R{}
|
||||||
skIntf, err := factory.NewPrivateKey()
|
skIntf, err := factory.NewPrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("problem generating private key: %w", err)
|
t.Fatalf("problem generating private key: %s", err)
|
||||||
}
|
}
|
||||||
sk := skIntf.(*crypto.PrivateKeySECP256K1R)
|
sk := skIntf.(*crypto.PrivateKeySECP256K1R)
|
||||||
|
|
||||||
|
|
|
@ -288,7 +288,7 @@ func TestStateTXs(t *testing.T) {
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
|
|
@ -11,12 +11,12 @@ func TestBuildGenesis(t *testing.T) {
|
||||||
ss := StaticService{}
|
ss := StaticService{}
|
||||||
|
|
||||||
args := BuildGenesisArgs{GenesisData: map[string]AssetDefinition{
|
args := BuildGenesisArgs{GenesisData: map[string]AssetDefinition{
|
||||||
"asset1": AssetDefinition{
|
"asset1": {
|
||||||
Name: "myFixedCapAsset",
|
Name: "myFixedCapAsset",
|
||||||
Symbol: "MFCA",
|
Symbol: "MFCA",
|
||||||
Denomination: 8,
|
Denomination: 8,
|
||||||
InitialState: map[string][]interface{}{
|
InitialState: map[string][]interface{}{
|
||||||
"fixedCap": []interface{}{
|
"fixedCap": {
|
||||||
Holder{
|
Holder{
|
||||||
Amount: 100000,
|
Amount: 100000,
|
||||||
Address: "A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy",
|
Address: "A9bTQjfYGBFK3JPRJqF2eh3JYL7cHocvy",
|
||||||
|
@ -36,11 +36,11 @@ func TestBuildGenesis(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"asset2": AssetDefinition{
|
"asset2": {
|
||||||
Name: "myVarCapAsset",
|
Name: "myVarCapAsset",
|
||||||
Symbol: "MVCA",
|
Symbol: "MVCA",
|
||||||
InitialState: map[string][]interface{}{
|
InitialState: map[string][]interface{}{
|
||||||
"variableCap": []interface{}{
|
"variableCap": {
|
||||||
Owners{
|
Owners{
|
||||||
Threshold: 1,
|
Threshold: 1,
|
||||||
Minters: []string{
|
Minters: []string{
|
||||||
|
@ -58,10 +58,10 @@ func TestBuildGenesis(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"asset3": AssetDefinition{
|
"asset3": {
|
||||||
Name: "myOtherVarCapAsset",
|
Name: "myOtherVarCapAsset",
|
||||||
InitialState: map[string][]interface{}{
|
InitialState: map[string][]interface{}{
|
||||||
"variableCap": []interface{}{
|
"variableCap": {
|
||||||
Owners{
|
Owners{
|
||||||
Threshold: 1,
|
Threshold: 1,
|
||||||
Minters: []string{
|
Minters: []string{
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/ava-labs/gecko/database"
|
"github.com/ava-labs/gecko/database"
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/snow"
|
"github.com/ava-labs/gecko/snow"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +24,9 @@ type UnsignedTx interface {
|
||||||
ID() ids.ID
|
ID() ids.ID
|
||||||
Bytes() []byte
|
Bytes() []byte
|
||||||
|
|
||||||
|
ConsumedAssetIDs() ids.Set
|
||||||
AssetIDs() ids.Set
|
AssetIDs() ids.Set
|
||||||
|
|
||||||
NumCredentials() int
|
NumCredentials() int
|
||||||
InputUTXOs() []*ava.UTXOID
|
InputUTXOs() []*ava.UTXOID
|
||||||
UTXOs() []*ava.UTXO
|
UTXOs() []*ava.UTXO
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
"github.com/ava-labs/gecko/utils/units"
|
"github.com/ava-labs/gecko/utils/units"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||||
)
|
)
|
||||||
|
@ -56,7 +56,7 @@ func TestTxInvalidCredential(t *testing.T) {
|
||||||
UnsignedTx: &BaseTx{
|
UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -95,7 +95,7 @@ func TestTxInvalidUnsignedTx(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{
|
Ins: []*ava.TransferableInput{
|
||||||
&ava.TransferableInput{
|
{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -110,7 +110,7 @@ func TestTxInvalidUnsignedTx(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&ava.TransferableInput{
|
{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: ids.Empty,
|
TxID: ids.Empty,
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -153,7 +153,7 @@ func TestTxInvalidNumberOfCredentials(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{
|
Ins: []*ava.TransferableInput{
|
||||||
&ava.TransferableInput{
|
{
|
||||||
UTXOID: ava.UTXOID{TxID: ids.Empty, OutputIndex: 0},
|
UTXOID: ava.UTXOID{TxID: ids.Empty, OutputIndex: 0},
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
In: &secp256k1fx.TransferInput{
|
In: &secp256k1fx.TransferInput{
|
||||||
|
@ -165,7 +165,7 @@ func TestTxInvalidNumberOfCredentials(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&ava.TransferableInput{
|
{
|
||||||
UTXOID: ava.UTXOID{TxID: ids.Empty, OutputIndex: 1},
|
UTXOID: ava.UTXOID{TxID: ids.Empty, OutputIndex: 1},
|
||||||
Asset: ava.Asset{ID: asset},
|
Asset: ava.Asset{ID: asset},
|
||||||
In: &secp256k1fx.TransferInput{
|
In: &secp256k1fx.TransferInput{
|
||||||
|
|
|
@ -85,7 +85,11 @@ func (tx *UniqueTx) refresh() {
|
||||||
|
|
||||||
// Evict is called when this UniqueTx will no longer be returned from a cache
|
// Evict is called when this UniqueTx will no longer be returned from a cache
|
||||||
// lookup
|
// lookup
|
||||||
func (tx *UniqueTx) Evict() { tx.unique = false } // Lock is already held here
|
func (tx *UniqueTx) Evict() {
|
||||||
|
// Lock is already held here
|
||||||
|
tx.unique = false
|
||||||
|
tx.deps = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *UniqueTx) setStatus(status choices.Status) error {
|
func (tx *UniqueTx) setStatus(status choices.Status) error {
|
||||||
tx.refresh()
|
tx.refresh()
|
||||||
|
@ -206,23 +210,26 @@ func (tx *UniqueTx) Dependencies() []snowstorm.Tx {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
txID, _ := in.InputSource()
|
txID, _ := in.InputSource()
|
||||||
if !txIDs.Contains(txID) {
|
if txIDs.Contains(txID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
txIDs.Add(txID)
|
txIDs.Add(txID)
|
||||||
tx.deps = append(tx.deps, &UniqueTx{
|
tx.deps = append(tx.deps, &UniqueTx{
|
||||||
vm: tx.vm,
|
vm: tx.vm,
|
||||||
txID: txID,
|
txID: txID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
consumedIDs := tx.Tx.ConsumedAssetIDs()
|
||||||
for _, assetID := range tx.Tx.AssetIDs().List() {
|
for _, assetID := range tx.Tx.AssetIDs().List() {
|
||||||
if !txIDs.Contains(assetID) {
|
if consumedIDs.Contains(assetID) || txIDs.Contains(assetID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
txIDs.Add(assetID)
|
txIDs.Add(assetID)
|
||||||
tx.deps = append(tx.deps, &UniqueTx{
|
tx.deps = append(tx.deps, &UniqueTx{
|
||||||
vm: tx.vm,
|
vm: tx.vm,
|
||||||
txID: assetID,
|
txID: assetID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return tx.deps
|
return tx.deps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,12 @@ import (
|
||||||
"github.com/ava-labs/gecko/snow/choices"
|
"github.com/ava-labs/gecko/snow/choices"
|
||||||
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
||||||
"github.com/ava-labs/gecko/snow/engine/common"
|
"github.com/ava-labs/gecko/snow/engine/common"
|
||||||
|
"github.com/ava-labs/gecko/utils/codec"
|
||||||
"github.com/ava-labs/gecko/utils/formatting"
|
"github.com/ava-labs/gecko/utils/formatting"
|
||||||
"github.com/ava-labs/gecko/utils/logging"
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
"github.com/ava-labs/gecko/utils/timer"
|
"github.com/ava-labs/gecko/utils/timer"
|
||||||
"github.com/ava-labs/gecko/utils/wrappers"
|
"github.com/ava-labs/gecko/utils/wrappers"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/utils/codec"
|
|
||||||
|
|
||||||
cjson "github.com/ava-labs/gecko/utils/json"
|
cjson "github.com/ava-labs/gecko/utils/json"
|
||||||
)
|
)
|
||||||
|
@ -35,7 +35,7 @@ const (
|
||||||
batchSize = 30
|
batchSize = 30
|
||||||
stateCacheSize = 10000
|
stateCacheSize = 10000
|
||||||
idCacheSize = 10000
|
idCacheSize = 10000
|
||||||
txCacheSize = 100000
|
txCacheSize = 10000
|
||||||
addressSep = "-"
|
addressSep = "-"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -248,8 +248,8 @@ func (vm *VM) CreateHandlers() map[string]*common.HTTPHandler {
|
||||||
rpcServer.RegisterService(&Service{vm: vm}, "avm") // name this service "avm"
|
rpcServer.RegisterService(&Service{vm: vm}, "avm") // name this service "avm"
|
||||||
|
|
||||||
return map[string]*common.HTTPHandler{
|
return map[string]*common.HTTPHandler{
|
||||||
"": &common.HTTPHandler{Handler: rpcServer},
|
"": {Handler: rpcServer},
|
||||||
"/pubsub": &common.HTTPHandler{LockOptions: common.NoLock, Handler: vm.pubsub},
|
"/pubsub": {LockOptions: common.NoLock, Handler: vm.pubsub},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +261,7 @@ func (vm *VM) CreateStaticHandlers() map[string]*common.HTTPHandler {
|
||||||
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
|
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
|
||||||
newServer.RegisterService(&StaticService{}, "avm") // name this service "avm"
|
newServer.RegisterService(&StaticService{}, "avm") // name this service "avm"
|
||||||
return map[string]*common.HTTPHandler{
|
return map[string]*common.HTTPHandler{
|
||||||
"": &common.HTTPHandler{LockOptions: common.WriteLock, Handler: newServer},
|
"": {LockOptions: common.WriteLock, Handler: newServer},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,11 +84,11 @@ func BuildGenesisTest(t *testing.T) []byte {
|
||||||
addr2 := keys[2].PublicKey().Address()
|
addr2 := keys[2].PublicKey().Address()
|
||||||
|
|
||||||
args := BuildGenesisArgs{GenesisData: map[string]AssetDefinition{
|
args := BuildGenesisArgs{GenesisData: map[string]AssetDefinition{
|
||||||
"asset1": AssetDefinition{
|
"asset1": {
|
||||||
Name: "myFixedCapAsset",
|
Name: "myFixedCapAsset",
|
||||||
Symbol: "MFCA",
|
Symbol: "MFCA",
|
||||||
InitialState: map[string][]interface{}{
|
InitialState: map[string][]interface{}{
|
||||||
"fixedCap": []interface{}{
|
"fixedCap": {
|
||||||
Holder{
|
Holder{
|
||||||
Amount: 100000,
|
Amount: 100000,
|
||||||
Address: addr0.String(),
|
Address: addr0.String(),
|
||||||
|
@ -108,11 +108,11 @@ func BuildGenesisTest(t *testing.T) []byte {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"asset2": AssetDefinition{
|
"asset2": {
|
||||||
Name: "myVarCapAsset",
|
Name: "myVarCapAsset",
|
||||||
Symbol: "MVCA",
|
Symbol: "MVCA",
|
||||||
InitialState: map[string][]interface{}{
|
InitialState: map[string][]interface{}{
|
||||||
"variableCap": []interface{}{
|
"variableCap": {
|
||||||
Owners{
|
Owners{
|
||||||
Threshold: 1,
|
Threshold: 1,
|
||||||
Minters: []string{
|
Minters: []string{
|
||||||
|
@ -131,10 +131,10 @@ func BuildGenesisTest(t *testing.T) []byte {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"asset3": AssetDefinition{
|
"asset3": {
|
||||||
Name: "myOtherVarCapAsset",
|
Name: "myOtherVarCapAsset",
|
||||||
InitialState: map[string][]interface{}{
|
InitialState: map[string][]interface{}{
|
||||||
"variableCap": []interface{}{
|
"variableCap": {
|
||||||
Owners{
|
Owners{
|
||||||
Threshold: 1,
|
Threshold: 1,
|
||||||
Minters: []string{
|
Minters: []string{
|
||||||
|
@ -168,7 +168,7 @@ func GenesisVM(t *testing.T) ([]byte, chan common.Message, *VM) {
|
||||||
memdb.New(),
|
memdb.New(),
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{&common.Fx{
|
[]*common.Fx{{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
}},
|
}},
|
||||||
|
@ -195,7 +195,7 @@ func NewTx(t *testing.T, genesisBytes []byte, vm *VM) *Tx {
|
||||||
newTx := &Tx{UnsignedTx: &BaseTx{
|
newTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -357,7 +357,7 @@ func TestTxSerialization(t *testing.T) {
|
||||||
Symbol: "symb",
|
Symbol: "symb",
|
||||||
Denomination: 0,
|
Denomination: 0,
|
||||||
States: []*InitialState{
|
States: []*InitialState{
|
||||||
&InitialState{
|
{
|
||||||
FxID: 0,
|
FxID: 0,
|
||||||
Outs: []verify.Verifiable{
|
Outs: []verify.Verifiable{
|
||||||
&secp256k1fx.MintOutput{
|
&secp256k1fx.MintOutput{
|
||||||
|
@ -456,7 +456,7 @@ func TestFxInitializationFailure(t *testing.T) {
|
||||||
/*db=*/ memdb.New(),
|
/*db=*/ memdb.New(),
|
||||||
/*genesisState=*/ genesisBytes,
|
/*genesisState=*/ genesisBytes,
|
||||||
/*engineMessenger=*/ make(chan common.Message, 1),
|
/*engineMessenger=*/ make(chan common.Message, 1),
|
||||||
/*fxs=*/ []*common.Fx{&common.Fx{
|
/*fxs=*/ []*common.Fx{{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &testFx{initialize: errUnknownFx},
|
Fx: &testFx{initialize: errUnknownFx},
|
||||||
}},
|
}},
|
||||||
|
@ -537,7 +537,7 @@ func TestIssueDependentTx(t *testing.T) {
|
||||||
firstTx := &Tx{UnsignedTx: &BaseTx{
|
firstTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: genesisTx.ID(),
|
TxID: genesisTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
|
@ -552,7 +552,7 @@ func TestIssueDependentTx(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
|
Outs: []*ava.TransferableOutput{{
|
||||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||||
Out: &secp256k1fx.TransferOutput{
|
Out: &secp256k1fx.TransferOutput{
|
||||||
Amt: 50000,
|
Amt: 50000,
|
||||||
|
@ -596,7 +596,7 @@ func TestIssueDependentTx(t *testing.T) {
|
||||||
secondTx := &Tx{UnsignedTx: &BaseTx{
|
secondTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
Ins: []*ava.TransferableInput{{
|
||||||
UTXOID: ava.UTXOID{
|
UTXOID: ava.UTXOID{
|
||||||
TxID: firstTx.ID(),
|
TxID: firstTx.ID(),
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
|
@ -671,11 +671,11 @@ func TestIssueNFT(t *testing.T) {
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{
|
[]*common.Fx{
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty.Prefix(0),
|
ID: ids.Empty.Prefix(0),
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
},
|
},
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty.Prefix(1),
|
ID: ids.Empty.Prefix(1),
|
||||||
Fx: &nftfx.Fx{},
|
Fx: &nftfx.Fx{},
|
||||||
},
|
},
|
||||||
|
@ -704,7 +704,7 @@ func TestIssueNFT(t *testing.T) {
|
||||||
Name: "Team Rocket",
|
Name: "Team Rocket",
|
||||||
Symbol: "TR",
|
Symbol: "TR",
|
||||||
Denomination: 0,
|
Denomination: 0,
|
||||||
States: []*InitialState{&InitialState{
|
States: []*InitialState{{
|
||||||
FxID: 1,
|
FxID: 1,
|
||||||
Outs: []verify.Verifiable{
|
Outs: []verify.Verifiable{
|
||||||
&nftfx.MintOutput{
|
&nftfx.MintOutput{
|
||||||
|
@ -740,9 +740,9 @@ func TestIssueNFT(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
},
|
},
|
||||||
Ops: []*Operation{&Operation{
|
Ops: []*Operation{{
|
||||||
Asset: ava.Asset{ID: createAssetTx.ID()},
|
Asset: ava.Asset{ID: createAssetTx.ID()},
|
||||||
UTXOIDs: []*ava.UTXOID{&ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{{
|
||||||
TxID: createAssetTx.ID(),
|
TxID: createAssetTx.ID(),
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
}},
|
}},
|
||||||
|
@ -752,9 +752,7 @@ func TestIssueNFT(t *testing.T) {
|
||||||
},
|
},
|
||||||
GroupID: 1,
|
GroupID: 1,
|
||||||
Payload: []byte{'h', 'e', 'l', 'l', 'o'},
|
Payload: []byte{'h', 'e', 'l', 'l', 'o'},
|
||||||
Outputs: []*secp256k1fx.OutputOwners{
|
Outputs: []*secp256k1fx.OutputOwners{{}},
|
||||||
&secp256k1fx.OutputOwners{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
@ -793,9 +791,9 @@ func TestIssueNFT(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
},
|
},
|
||||||
Ops: []*Operation{&Operation{
|
Ops: []*Operation{{
|
||||||
Asset: ava.Asset{ID: createAssetTx.ID()},
|
Asset: ava.Asset{ID: createAssetTx.ID()},
|
||||||
UTXOIDs: []*ava.UTXOID{&ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{{
|
||||||
TxID: mintNFTTx.ID(),
|
TxID: mintNFTTx.ID(),
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
}},
|
}},
|
||||||
|
@ -840,15 +838,15 @@ func TestIssueProperty(t *testing.T) {
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
issuer,
|
issuer,
|
||||||
[]*common.Fx{
|
[]*common.Fx{
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty.Prefix(0),
|
ID: ids.Empty.Prefix(0),
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
},
|
},
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty.Prefix(1),
|
ID: ids.Empty.Prefix(1),
|
||||||
Fx: &nftfx.Fx{},
|
Fx: &nftfx.Fx{},
|
||||||
},
|
},
|
||||||
&common.Fx{
|
{
|
||||||
ID: ids.Empty.Prefix(2),
|
ID: ids.Empty.Prefix(2),
|
||||||
Fx: &propertyfx.Fx{},
|
Fx: &propertyfx.Fx{},
|
||||||
},
|
},
|
||||||
|
@ -877,7 +875,7 @@ func TestIssueProperty(t *testing.T) {
|
||||||
Name: "Team Rocket",
|
Name: "Team Rocket",
|
||||||
Symbol: "TR",
|
Symbol: "TR",
|
||||||
Denomination: 0,
|
Denomination: 0,
|
||||||
States: []*InitialState{&InitialState{
|
States: []*InitialState{{
|
||||||
FxID: 2,
|
FxID: 2,
|
||||||
Outs: []verify.Verifiable{
|
Outs: []verify.Verifiable{
|
||||||
&propertyfx.MintOutput{
|
&propertyfx.MintOutput{
|
||||||
|
@ -905,9 +903,9 @@ func TestIssueProperty(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
},
|
},
|
||||||
Ops: []*Operation{&Operation{
|
Ops: []*Operation{{
|
||||||
Asset: ava.Asset{ID: createAssetTx.ID()},
|
Asset: ava.Asset{ID: createAssetTx.ID()},
|
||||||
UTXOIDs: []*ava.UTXOID{&ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{{
|
||||||
TxID: createAssetTx.ID(),
|
TxID: createAssetTx.ID(),
|
||||||
OutputIndex: 0,
|
OutputIndex: 0,
|
||||||
}},
|
}},
|
||||||
|
@ -960,9 +958,9 @@ func TestIssueProperty(t *testing.T) {
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
BCID: chainID,
|
BCID: chainID,
|
||||||
},
|
},
|
||||||
Ops: []*Operation{&Operation{
|
Ops: []*Operation{{
|
||||||
Asset: ava.Asset{ID: createAssetTx.ID()},
|
Asset: ava.Asset{ID: createAssetTx.ID()},
|
||||||
UTXOIDs: []*ava.UTXOID{&ava.UTXOID{
|
UTXOIDs: []*ava.UTXOID{{
|
||||||
TxID: mintPropertyTx.ID(),
|
TxID: mintPropertyTx.ID(),
|
||||||
OutputIndex: 1,
|
OutputIndex: 1,
|
||||||
}},
|
}},
|
||||||
|
|
|
@ -128,7 +128,7 @@ func (tx *addDefaultSubnetDelegatorTx) SemanticVerify(db database.Database) (*ve
|
||||||
// The account if this block's proposal is committed and the validator is
|
// The account if this block's proposal is committed and the validator is
|
||||||
// added to the pending validator set. (Increase the account's nonce;
|
// added to the pending validator set. (Increase the account's nonce;
|
||||||
// decrease its balance.)
|
// decrease its balance.)
|
||||||
newAccount, err := account.Remove(tx.Wght, tx.Nonce) // Remove also removes the fee
|
newAccount, err := account.Remove(0, tx.Nonce) // Remove also removes the fee
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, permError{err}
|
return nil, nil, nil, nil, permError{err}
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,51 +387,51 @@ func TestAddDefaultSubnetDelegatorTxSemanticVerify(t *testing.T) {
|
||||||
}
|
}
|
||||||
txFee = txFeeSaved // Reset tx fee
|
txFee = txFeeSaved // Reset tx fee
|
||||||
|
|
||||||
// Case 8: fail verification for spending more funds than it has
|
// // Case 8: fail verification for spending more funds than it has
|
||||||
tx, err = vm.newAddDefaultSubnetDelegatorTx(
|
// tx, err = vm.newAddDefaultSubnetDelegatorTx(
|
||||||
defaultNonce+1,
|
// defaultNonce+1,
|
||||||
defaultBalance*2, // weight
|
// defaultBalance*2, // weight
|
||||||
uint64(defaultValidateStartTime.Unix()), // start time
|
// uint64(defaultValidateStartTime.Unix()), // start time
|
||||||
uint64(defaultValidateEndTime.Unix()), // end time
|
// uint64(defaultValidateEndTime.Unix()), // end time
|
||||||
defaultKey.PublicKey().Address(), // node ID
|
// defaultKey.PublicKey().Address(), // node ID
|
||||||
defaultKey.PublicKey().Address(), // destination
|
// defaultKey.PublicKey().Address(), // destination
|
||||||
testNetworkID, // network ID
|
// testNetworkID, // network ID
|
||||||
defaultKey, // tx fee payer
|
// defaultKey, // tx fee payer
|
||||||
)
|
// )
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
_, _, _, _, err = tx.SemanticVerify(vm.DB)
|
// _, _, _, _, err = tx.SemanticVerify(vm.DB)
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
t.Fatal("should have failed verification because payer account spent twice the account's balance")
|
// t.Fatal("should have failed verification because payer account spent twice the account's balance")
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Case 9: Confirm balance is correct
|
// // Case 9: Confirm balance is correct
|
||||||
tx, err = vm.newAddDefaultSubnetDelegatorTx(
|
// tx, err = vm.newAddDefaultSubnetDelegatorTx(
|
||||||
defaultNonce+1,
|
// defaultNonce+1,
|
||||||
defaultStakeAmount, // weight
|
// defaultStakeAmount, // weight
|
||||||
uint64(defaultValidateStartTime.Unix()), // start time
|
// uint64(defaultValidateStartTime.Unix()), // start time
|
||||||
uint64(defaultValidateEndTime.Unix()), // end time
|
// uint64(defaultValidateEndTime.Unix()), // end time
|
||||||
defaultKey.PublicKey().Address(), // node ID
|
// defaultKey.PublicKey().Address(), // node ID
|
||||||
defaultKey.PublicKey().Address(), // destination
|
// defaultKey.PublicKey().Address(), // destination
|
||||||
testNetworkID, // network ID
|
// testNetworkID, // network ID
|
||||||
defaultKey, // tx fee payer
|
// defaultKey, // tx fee payer
|
||||||
)
|
// )
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
onCommitDB, _, _, _, err := tx.SemanticVerify(vm.DB)
|
// onCommitDB, _, _, _, err := tx.SemanticVerify(vm.DB)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
account, err := tx.vm.getAccount(onCommitDB, defaultKey.PublicKey().Address())
|
// account, err := tx.vm.getAccount(onCommitDB, defaultKey.PublicKey().Address())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
balance := account.Balance
|
// balance := account.Balance
|
||||||
|
|
||||||
if balance != defaultBalance-(defaultStakeAmount+txFee) {
|
// if balance != defaultBalance-(defaultStakeAmount+txFee) {
|
||||||
t.Fatalf("balance was not updated correctly after subnet delegator tx")
|
// t.Fatalf("balance was not updated correctly after subnet delegator tx")
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue