services: fix Oracle response transaction creation

Problem: transactions with wrong hashes are accepted to the chain if
consensus nodes are designated as Oracle nodes. The result is wrong
MerkleRoot for the accepted block. Consensus nodes got such blocks
right from the dbft and store them without errors, but if
non-consensus nodes are present in the network, they just can't accept
these "bad" blocks:
```
2021-11-29T12:56:40.533+0300	WARN	blockQueue: failed adding block into the blockchain	{"error": "invalid block: MerkleRoot mismatch (expected a866b57ad637934f7a7700e3635a549387e644970b42681d865a54c3b3a46122, calculated d465aafabaf4539a3f619d373d178eeeeab7acb9847e746e398706c8c1582bf8)", "blockHeight": 17, "nextIndex": 18}
```

This problem happens because of transaction hash caching. We can't set
transaction hash if transaction construction wasn't yet completed.
This commit is contained in:
Anna Shaleva 2021-11-29 15:03:20 +03:00
parent 6101d4b251
commit 1d1538c566
2 changed files with 24 additions and 1 deletions

View file

@ -168,6 +168,25 @@ func TestOracle(t *testing.T) {
checkEmitTx := func(t *testing.T, ch chan *transaction.Transaction) {
require.Len(t, ch, 1)
tx := <-ch
// Response transaction has its hash being precalculated. Check that this hash
// matches the actual one.
cachedHash := tx.Hash()
cp := transaction.Transaction{
Version: tx.Version,
Nonce: tx.Nonce,
SystemFee: tx.SystemFee,
NetworkFee: tx.NetworkFee,
ValidUntilBlock: tx.ValidUntilBlock,
Script: tx.Script,
Attributes: tx.Attributes,
Signers: tx.Signers,
Scripts: tx.Scripts,
Trimmed: tx.Trimmed,
}
actualHash := cp.Hash()
require.Equal(t, actualHash, cachedHash, "transaction hash was changed during ")
require.NoError(t, bc.verifyAndPoolTx(tx, bc.GetMemPool(), bc))
}

View file

@ -134,7 +134,11 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transa
}
func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool) {
v, finalize := o.Chain.GetTestVM(trigger.Verification, tx, nil)
// (*Blockchain).GetTestVM calls Hash() method of provided transaction; once being called, this
// method caches transaction hash, but tx building is not yet completed and hash will be changed.
// So make a copy of tx to avoid wrong hash caching.
cp := *tx
v, finalize := o.Chain.GetTestVM(trigger.Verification, &cp, nil)
v.GasLimit = o.Chain.GetPolicer().GetMaxVerificationGAS()
v.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
v.Jump(v.Context(), o.verifyOffset)