Require for reissued transactions to be orphans

This commit is contained in:
StephenButtolph 2020-05-31 00:03:55 -04:00
parent 218ff1136c
commit 07656c7c23
2 changed files with 112 additions and 2 deletions

View File

@ -369,6 +369,7 @@ func (t *Transitive) batch(txs []snowstorm.Tx, force, empty bool) error {
issuedTxs := ids.Set{}
consumed := ids.Set{}
issued := false
orphans := t.Consensus.Orphans()
for _, tx := range txs {
inputs := tx.InputIDs()
overlaps := consumed.Overlaps(inputs)
@ -380,8 +381,10 @@ func (t *Transitive) batch(txs []snowstorm.Tx, force, empty bool) error {
overlaps = false
}
// Force allows for a conflict to be issued
if txID := tx.ID(); !overlaps && !issuedTxs.Contains(txID) && (force || t.Consensus.IsVirtuous(tx)) && !tx.Status().Decided() {
if txID := tx.ID(); !overlaps && // should never allow conflicting txs in the same vertex
!issuedTxs.Contains(txID) && // shouldn't issue duplicated transactions to the same vertex
(force || t.Consensus.IsVirtuous(tx)) && // force allows for a conflict to be issued
(!t.Consensus.TxIssued(tx) || orphans.Contains(txID)) { // should only reissued orphaned txs
batch = append(batch, tx)
issuedTxs.Add(txID)
consumed.Union(inputs)

View File

@ -2975,3 +2975,110 @@ func TestEngineAggressivePolling(t *testing.T) {
t.Fatalf("should have issued one pull query")
}
}
func TestEngineDuplicatedIssuance(t *testing.T) {
config := DefaultConfig()
config.Params.BatchSize = 1
config.Params.BetaVirtuous = 5
config.Params.BetaRogue = 5
sender := &common.SenderTest{}
sender.T = t
config.Sender = sender
sender.Default(true)
sender.CantGetAcceptedFrontier = false
vdr := validators.GenerateRandomValidator(1)
vals := validators.NewSet()
config.Validators = vals
vals.Add(vdr)
st := &stateTest{t: t}
config.State = st
st.Default(true)
vm := &VMTest{}
vm.T = t
config.VM = vm
vm.Default(true)
gVtx := &Vtx{
id: GenerateID(),
status: choices.Accepted,
}
mVtx := &Vtx{
id: GenerateID(),
status: choices.Accepted,
}
gTx := &TestTx{
TestTx: snowstorm.TestTx{
Identifier: GenerateID(),
Stat: choices.Accepted,
},
}
utxos := []ids.ID{GenerateID(), GenerateID()}
tx := &TestTx{
TestTx: snowstorm.TestTx{
Identifier: GenerateID(),
Deps: []snowstorm.Tx{gTx},
Stat: choices.Processing,
},
}
tx.Ins.Add(utxos[0])
st.edge = func() []ids.ID { return []ids.ID{gVtx.ID(), mVtx.ID()} }
st.getVertex = func(id ids.ID) (avalanche.Vertex, error) {
switch {
case id.Equals(gVtx.ID()):
return gVtx, nil
case id.Equals(mVtx.ID()):
return mVtx, nil
}
t.Fatalf("Unknown vertex")
panic("Should have errored")
}
te := &Transitive{}
te.Initialize(config)
te.finishBootstrapping()
lastVtx := new(Vtx)
st.buildVertex = func(_ ids.Set, txs []snowstorm.Tx) (avalanche.Vertex, error) {
consumers := []snowstorm.Tx{}
for _, tx := range txs {
consumers = append(consumers, tx)
}
lastVtx = &Vtx{
parents: []avalanche.Vertex{gVtx, mVtx},
id: GenerateID(),
txs: consumers,
status: choices.Processing,
bytes: []byte{1},
}
return lastVtx, nil
}
sender.CantPushQuery = false
vm.PendingTxsF = func() []snowstorm.Tx { return []snowstorm.Tx{tx} }
te.Notify(common.PendingTxs)
if len(lastVtx.txs) != 1 || !lastVtx.txs[0].ID().Equals(tx.ID()) {
t.Fatalf("Should have issued txs differently")
}
st.buildVertex = func(ids.Set, []snowstorm.Tx) (avalanche.Vertex, error) {
t.Fatalf("shouldn't have attempted to issue a duplicated tx")
return nil, nil
}
te.Notify(common.PendingTxs)
}