gecko/snow/engine/avalanche/issuer.go

112 lines
3.0 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package avalanche
import (
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/consensus/avalanche"
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
)
type issuer struct {
t *Transitive
vtx avalanche.Vertex
issued, abandoned bool
vtxDeps, txDeps ids.Set
}
func (i *issuer) FulfillVtx(id ids.ID) {
i.vtxDeps.Remove(id)
i.Update()
}
func (i *issuer) FulfillTx(id ids.ID) {
i.txDeps.Remove(id)
i.Update()
}
func (i *issuer) Abandon() {
if !i.abandoned {
vtxID := i.vtx.ID()
i.t.pending.Remove(vtxID)
i.abandoned = true
i.t.vtxBlocked.Abandon(vtxID)
}
}
func (i *issuer) Update() {
if i.abandoned || i.issued || i.vtxDeps.Len() != 0 || i.txDeps.Len() != 0 || i.t.Consensus.VertexIssued(i.vtx) || i.t.errs.Errored() {
return
}
i.issued = true
vtxID := i.vtx.ID()
i.t.pending.Remove(vtxID)
txs := i.vtx.Txs()
validTxs := []snowstorm.Tx{}
for _, tx := range txs {
if err := tx.Verify(); err != nil {
i.t.Config.Context.Log.Debug("Transaction %s failed verification due to %s", tx.ID(), err)
} else {
validTxs = append(validTxs, tx)
}
}
if len(validTxs) != len(txs) {
i.t.Config.Context.Log.Debug("Abandoning %s due to failed transaction verification", vtxID)
i.t.batch(validTxs, false /*=force*/, false /*=empty*/)
i.t.vtxBlocked.Abandon(vtxID)
return
}
i.t.Config.Context.Log.Verbo("Adding vertex to consensus:\n%s", i.vtx)
if err := i.t.Consensus.Add(i.vtx); err != nil {
i.t.errs.Add(err)
return
}
p := i.t.Consensus.Parameters()
vdrs := i.t.Config.Validators.Sample(p.K) // Validators to sample
vdrSet := ids.ShortSet{} // Validators to sample repr. as a set
for _, vdr := range vdrs {
vdrSet.Add(vdr.ID())
}
toSample := ids.ShortSet{} // Copy to a new variable because we may remove an element in sender.Sender
toSample.Union(vdrSet) // and we don't want that to affect the set of validators we wait for [ie vdrSet]
i.t.RequestID++
if numVdrs := len(vdrs); numVdrs == p.K && i.t.polls.Add(i.t.RequestID, vdrSet) {
i.t.Config.Sender.PushQuery(toSample, i.t.RequestID, vtxID, i.vtx.Bytes())
} else if numVdrs < p.K {
i.t.Config.Context.Log.Error("Query for %s was dropped due to an insufficient number of validators", vtxID)
}
i.t.vtxBlocked.Fulfill(vtxID)
for _, tx := range i.vtx.Txs() {
i.t.txBlocked.Fulfill(tx.ID())
}
i.t.errs.Add(i.t.repoll())
}
type vtxIssuer struct{ i *issuer }
func (vi *vtxIssuer) Dependencies() ids.Set { return vi.i.vtxDeps }
func (vi *vtxIssuer) Fulfill(id ids.ID) { vi.i.FulfillVtx(id) }
func (vi *vtxIssuer) Abandon(ids.ID) { vi.i.Abandon() }
func (vi *vtxIssuer) Update() { vi.i.Update() }
type txIssuer struct{ i *issuer }
func (ti *txIssuer) Dependencies() ids.Set { return ti.i.txDeps }
func (ti *txIssuer) Fulfill(id ids.ID) { ti.i.FulfillTx(id) }
func (ti *txIssuer) Abandon(ids.ID) { ti.i.Abandon() }
func (ti *txIssuer) Update() { ti.i.Update() }